ART: Change merges with Undefined to Undefined

The result of a merge with an Undefined type should be Undefined.
Conflicts are allowed to be copied around, but Undefined registers
should not be touched at all, except to be written into.

Add a success test case (the register isn't used) and a fail test
case (the register is tried to be copied).

Bug: 22331663
Bug: 22371999

(cherry picked from commit 97a1ff353f254b6e46c7501fe3f0e3254c2517b4)

Change-Id: I9697ce31c1d2ab5aee0433dcf1253bcca79c2983
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index c8aa4fd..1435607 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -585,7 +585,15 @@
   DCHECK(!Equals(incoming_type));  // Trivial equality handled by caller
   // Perform pointer equality tests for conflict to avoid virtual method dispatch.
   const ConflictType& conflict = reg_types->Conflict();
-  if (this == &conflict) {
+  if (IsUndefined() || incoming_type.IsUndefined()) {
+    // There is a difference between undefined and conflict. Conflicts may be copied around, but
+    // not used. Undefined registers must not be copied. So any merge with undefined should return
+    // undefined.
+    if (IsUndefined()) {
+      return *this;
+    }
+    return incoming_type;
+  } else if (this == &conflict) {
     DCHECK(IsConflict());
     return *this;  // Conflict MERGE * => Conflict
   } else if (&incoming_type == &conflict) {
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index aa997a6..659f104 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -25,4 +25,6 @@
 b/21645819
 b/22244733
 b/22331663
+b/22331663 (pass)
+b/22331663 (fail)
 Done!
diff --git a/test/800-smali/smali/b_22331663_fail.smali b/test/800-smali/smali/b_22331663_fail.smali
new file mode 100644
index 0000000..0c25e30
--- /dev/null
+++ b/test/800-smali/smali/b_22331663_fail.smali
@@ -0,0 +1,20 @@
+.class public LB22331663Fail;
+.super Ljava/lang/Object;
+
+
+.method public static run(Z)V
+.registers 6
+       if-eqz v5, :Label1
+
+       # Construct a java.lang.Object completely. This makes v4 of reference type.
+       new-instance v4, Ljava/lang/Object;
+       invoke-direct {v4}, Ljava/lang/Object;-><init>()V
+
+:Label1
+       # At this point, v4 is the merge of Undefined and ReferenceType. The verifier should
+       # reject any use of this, even a copy. Previously this was a conflict. Conflicts must
+       # be movable now, so ensure that we do not get a conflict (and then allow the move).
+       move-object v0, v4
+
+       return-void
+.end method
diff --git a/test/800-smali/smali/b_22331663_pass.smali b/test/800-smali/smali/b_22331663_pass.smali
new file mode 100644
index 0000000..1b54180
--- /dev/null
+++ b/test/800-smali/smali/b_22331663_pass.smali
@@ -0,0 +1,22 @@
+.class public LB22331663Pass;
+.super Ljava/lang/Object;
+
+
+.method public static run(Z)V
+.registers 6
+       if-eqz v5, :Label1
+
+       # Construct a java.lang.Object completely. This makes v4 of reference type.
+       new-instance v4, Ljava/lang/Object;
+       invoke-direct {v4}, Ljava/lang/Object;-><init>()V
+
+:Label1
+       # At this point, v4 is the merge of Undefined and ReferenceType. The verifier should not
+       # reject this if it is unused.
+
+       # Do an allocation here. This will force heap checking in gcstress mode.
+       new-instance v0, Ljava/lang/Object;
+       invoke-direct {v0}, Ljava/lang/Object;-><init>()V
+
+       return-void
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index e0872c3..709c7f6 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -97,6 +97,10 @@
                 null, "abc"));
         testCases.add(new TestCase("b/22331663", "B22331663", "run", new Object[] { false },
                 null, null));
+        testCases.add(new TestCase("b/22331663 (pass)", "B22331663Pass", "run",
+                new Object[] { false }, null, null));
+        testCases.add(new TestCase("b/22331663 (fail)", "B22331663Fail", "run",
+                new Object[] { false }, new VerifyError(), null));
     }
 
     public void runTests() {