Introduce a boolean lattice element

This introduces a class BooleanLatticeElement, but keeps OptionalBool around, such that we can ensure that BOTTOM is not allowed at all the places that used to be typed as OptionalBool.

Change-Id: Id57071bb4bdbc263d4b117e8823155fc4e0abedb
diff --git a/src/main/java/com/android/tools/r8/OptionalBool.java b/src/main/java/com/android/tools/r8/OptionalBool.java
deleted file mode 100644
index 92f5d07..0000000
--- a/src/main/java/com/android/tools/r8/OptionalBool.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8;
-
-/** Three point boolean lattice. */
-public abstract class OptionalBool {
-
-  private static final OptionalBool TRUE =
-      new OptionalBool() {
-        @Override
-        public boolean isTrue() {
-          return true;
-        }
-
-        @Override
-        public String toString() {
-          return "true";
-        }
-      };
-
-  private static final OptionalBool FALSE =
-      new OptionalBool() {
-        @Override
-        public boolean isFalse() {
-          return true;
-        }
-
-        @Override
-        public String toString() {
-          return "false";
-        }
-      };
-
-  private static final OptionalBool UNKNOWN =
-      new OptionalBool() {
-        @Override
-        public boolean isUnknown() {
-          return true;
-        }
-
-        @Override
-        public String toString() {
-          return "unknown";
-        }
-      };
-
-  public static OptionalBool of(boolean bool) {
-    return bool ? TRUE : FALSE;
-  }
-
-  public static OptionalBool unknown() {
-    return UNKNOWN;
-  }
-
-  private OptionalBool() {}
-
-  public boolean isTrue() {
-    return false;
-  }
-
-  public boolean isFalse() {
-    return false;
-  }
-
-  public boolean isUnknown() {
-    return false;
-  }
-
-  public boolean isPossiblyTrue() {
-    return !isFalse();
-  }
-
-  public boolean isPossiblyFalse() {
-    return !isTrue();
-  }
-
-  public boolean getBooleanValue() {
-    if (isUnknown()) {
-      throw new IllegalStateException("Attempt to convert unknown value to a boolean");
-    }
-    return isTrue();
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 83e30d2..a2d6967 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.OptionalBool;
 import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis.InitializedClassesInInstanceMethods;
 import com.android.tools.r8.ir.analysis.proto.GeneratedExtensionRegistryShrinker;
 import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
@@ -17,6 +16,7 @@
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.shaking.VerticalClassMerger.VerticallyMergedClasses;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OptionalBool;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableSet;
 import java.util.Set;
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 ccc2eed..72e372a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -3,13 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.OptionalBool;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.kotlin.KotlinInfo;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.PredicateUtils;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Predicates;
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 415e65b..47930c2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -10,7 +10,6 @@
 import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SUBCLASS;
 import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
 
-import com.android.tools.r8.OptionalBool;
 import com.android.tools.r8.cf.code.CfConstNull;
 import com.android.tools.r8.cf.code.CfConstString;
 import com.android.tools.r8.cf.code.CfInstruction;
@@ -62,6 +61,7 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.Pair;
 import com.google.common.collect.ImmutableList;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
index 827529a..e5ace97 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.analysis;
 
-import com.android.tools.r8.OptionalBool;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinition;
@@ -35,6 +34,7 @@
 import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.OptionalBool;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Streams;
 import java.util.ArrayDeque;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index 27b3ede..fce7b09 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.analysis.proto.schema;
 
-import com.android.tools.r8.OptionalBool;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -25,6 +24,7 @@
 import com.android.tools.r8.shaking.EnqueuerWorklist;
 import com.android.tools.r8.shaking.KeepReason;
 import com.android.tools.r8.utils.BitUtils;
+import com.android.tools.r8.utils.OptionalBool;
 import com.google.common.collect.Sets;
 import java.util.IdentityHashMap;
 import java.util.List;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
index 35c483b..3449c30 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.analysis.sideeffect;
 
-import com.android.tools.r8.OptionalBool;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexType;
@@ -16,6 +15,7 @@
 import com.android.tools.r8.ir.code.NewArrayFilledData;
 import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.OptionalBool;
 
 public class ClassInitializerSideEffectAnalysis {
 
diff --git a/src/main/java/com/android/tools/r8/utils/BooleanLatticeElement.java b/src/main/java/com/android/tools/r8/utils/BooleanLatticeElement.java
new file mode 100644
index 0000000..9e955c6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/BooleanLatticeElement.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+public abstract class BooleanLatticeElement {
+
+  public static final BooleanLatticeElement BOTTOM =
+      new BooleanLatticeElement() {
+
+        @Override
+        public OptionalBool asOptionalBool() {
+          throw new IllegalStateException("BooleanLatticeElement.BOTTOM is not an OptionalBool");
+        }
+
+        @Override
+        public boolean isBottom() {
+          return true;
+        }
+
+        @Override
+        public String toString() {
+          return "bottom";
+        }
+      };
+
+  BooleanLatticeElement() {}
+
+  public abstract OptionalBool asOptionalBool();
+
+  public boolean isBottom() {
+    return false;
+  }
+
+  public boolean isTrue() {
+    return false;
+  }
+
+  public boolean isFalse() {
+    return false;
+  }
+
+  public boolean isUnknown() {
+    return false;
+  }
+
+  public boolean isPossiblyTrue() {
+    return isTrue() || isUnknown();
+  }
+
+  public boolean isPossiblyFalse() {
+    return isFalse() || isUnknown();
+  }
+
+  public BooleanLatticeElement join(BooleanLatticeElement other) {
+    if (this == other || other.isBottom() || isUnknown()) {
+      return this;
+    }
+    if (isBottom() || other.isUnknown()) {
+      return other;
+    }
+    assert isTrue() || isFalse();
+    assert other.isTrue() || other.isFalse();
+    return OptionalBool.UNKNOWN;
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    return this == other;
+  }
+
+  @Override
+  public int hashCode() {
+    return System.identityHashCode(this);
+  }
+
+  // Force all subtypes to implement toString().
+  @Override
+  public abstract String toString();
+}
diff --git a/src/main/java/com/android/tools/r8/utils/OptionalBool.java b/src/main/java/com/android/tools/r8/utils/OptionalBool.java
new file mode 100644
index 0000000..54029d5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/OptionalBool.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+public abstract class OptionalBool extends BooleanLatticeElement {
+
+  public static final OptionalBool TRUE =
+      new OptionalBool() {
+
+        @Override
+        public boolean isTrue() {
+          return true;
+        }
+
+        @Override
+        public String toString() {
+          return "true";
+        }
+      };
+
+  public static final OptionalBool FALSE =
+      new OptionalBool() {
+
+        @Override
+        public boolean isFalse() {
+          return true;
+        }
+
+        @Override
+        public String toString() {
+          return "false";
+        }
+      };
+
+  public static final OptionalBool UNKNOWN =
+      new OptionalBool() {
+
+        @Override
+        public boolean isUnknown() {
+          return true;
+        }
+
+        @Override
+        public String toString() {
+          return "unknown";
+        }
+      };
+
+  OptionalBool() {}
+
+  public static OptionalBool of(boolean bool) {
+    return bool ? TRUE : FALSE;
+  }
+
+  public static OptionalBool unknown() {
+    return UNKNOWN;
+  }
+
+  @Override
+  public OptionalBool asOptionalBool() {
+    return this;
+  }
+}