ART: Allow to set and copy conflicts in the verifier

As long as conflicts are not actively being used, it is OK to set
them in a register line or copy them around.

Bug: 22331663
Change-Id: I61999e2d9c92f9bdedcb0a5dea54df3c5bb130ca
diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h
index 244deed..9cd2bdf 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -38,10 +38,9 @@
     verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Expected category1 register type not '"
         << new_type << "'";
     return false;
-  } else if (new_type.IsConflict()) {  // should only be set during a merge
-    verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Set register to unknown type " << new_type;
-    return false;
   } else {
+    // Note: previously we failed when asked to set a conflict. However, conflicts are OK as long
+    //       as they are not accessed, and our backends can handle this nowadays.
     line_[vdst] = new_type.GetId();
   }
   // Clear the monitor entry bits for this register.
@@ -93,8 +92,9 @@
   if (!SetRegisterType(verifier, vdst, type)) {
     return;
   }
-  if ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
-      (cat == kTypeCategoryRef && !type.IsReferenceTypes())) {
+  if (!type.IsConflict() &&                                  // Allow conflicts to be copied around.
+      ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
+       (cat == kTypeCategoryRef && !type.IsReferenceTypes()))) {
     verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy1 v" << vdst << "<-v" << vsrc << " type=" << type
                                                  << " cat=" << static_cast<int>(cat);
   } else if (cat == kTypeCategoryRef) {
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index fe68c5b..6ee7a5b 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -28,4 +28,5 @@
 b/22080519
 b/21645819
 b/22244733
+b/22331663
 Done!
diff --git a/test/800-smali/smali/b_22331663.smali b/test/800-smali/smali/b_22331663.smali
new file mode 100644
index 0000000..af99152
--- /dev/null
+++ b/test/800-smali/smali/b_22331663.smali
@@ -0,0 +1,35 @@
+.class public LB22331663;
+.super Ljava/lang/Object;
+
+
+.method public static run(Z)V
+.registers 6
+       if-eqz v5, :Label2
+
+:Label1
+       # Construct a java.lang.Object completely, and throw a new exception.
+       new-instance v4, Ljava/lang/Object;
+       invoke-direct {v4}, Ljava/lang/Object;-><init>()V
+
+       new-instance v3, Ljava/lang/RuntimeException;
+       invoke-direct {v3}, Ljava/lang/RuntimeException;-><init>()V
+       throw v3
+
+:Label2
+       # Allocate a java.lang.Object (do not initialize), and throw a new exception.
+       new-instance v4, Ljava/lang/Object;
+
+       new-instance v3, Ljava/lang/RuntimeException;
+       invoke-direct {v3}, Ljava/lang/RuntimeException;-><init>()V
+       throw v3
+
+:Label3
+       # Catch handler. Here we had to merge the uninitialized with the initialized reference,
+       # which creates a conflict. Copy the conflict, and then return. This should not make the
+       # verifier fail the method.
+       move-object v0, v4
+
+       return-void
+
+.catchall {:Label1 .. :Label3} :Label3
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 61f0d7b..3dbba8d 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -103,6 +103,8 @@
                 null, null));
         testCases.add(new TestCase("b/22244733", "B22244733", "run", new Object[] { "abc" },
                 null, "abc"));
+        testCases.add(new TestCase("b/22331663", "B22331663", "run", new Object[] { false },
+                null, null));
     }
 
     public void runTests() {