ART: Redo verification on class resolution failure

During compile-time verification, when a class needs to be resolved
and access to that class checked, if we can't resolve the class,
do a conservative access check and post an ACCESS_CLASS failure, if
necessary. This will trigger a re-verification at runtime, when the
class should be available.

Fix an invoke-polymorphic test to not trigger dead code. Fix method
expectations in verifier_deps_test.

Bug: 64681719
Test: m test-art-host
Test: cts-tradefed run commandAndExit cts --m vm-tests-tf
Change-Id: I3639639476f6938e10df1b0dac4545fe841a6ad2
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 6538925..5c097da 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -624,7 +624,7 @@
 }
 
 TEST_F(VerifierDepsTest, ConstClass_Unresolved) {
-  ASSERT_TRUE(VerifyMethod("ConstClass_Unresolved"));
+  ASSERT_FALSE(VerifyMethod("ConstClass_Unresolved"));
   ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
 }
 
@@ -634,7 +634,7 @@
 }
 
 TEST_F(VerifierDepsTest, CheckCast_Unresolved) {
-  ASSERT_TRUE(VerifyMethod("CheckCast_Unresolved"));
+  ASSERT_FALSE(VerifyMethod("CheckCast_Unresolved"));
   ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
 }
 
@@ -644,7 +644,7 @@
 }
 
 TEST_F(VerifierDepsTest, InstanceOf_Unresolved) {
-  ASSERT_TRUE(VerifyMethod("InstanceOf_Unresolved"));
+  ASSERT_FALSE(VerifyMethod("InstanceOf_Unresolved"));
   ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
 }
 
@@ -654,12 +654,12 @@
 }
 
 TEST_F(VerifierDepsTest, NewInstance_Unresolved) {
-  ASSERT_TRUE(VerifyMethod("NewInstance_Unresolved"));
+  ASSERT_FALSE(VerifyMethod("NewInstance_Unresolved"));
   ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
 }
 
 TEST_F(VerifierDepsTest, NewArray_Unresolved) {
-  ASSERT_TRUE(VerifyMethod("NewArray_Unresolved"));
+  ASSERT_FALSE(VerifyMethod("NewArray_Unresolved"));
   ASSERT_TRUE(HasClass("[LUnresolvedClass;", false));
 }
 
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index f1b1080..9a87607 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3763,13 +3763,15 @@
   // Record result of class resolution attempt.
   VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass);
 
-  // Check if access is allowed. Unresolved types use xxxWithAccessCheck to
-  // check at runtime if access is allowed and so pass here. If result is
-  // primitive, skip the access check.
-  if (C == CheckAccess::kYes && result->IsNonZeroReferenceTypes() && !result->IsUnresolvedTypes()) {
+  // If requested, check if access is allowed. Unresolved types are included in this check, as the
+  // interpreter only tests whether access is allowed when a class is not pre-verified and runs in
+  // the access-checks interpreter. If result is primitive, skip the access check.
+  //
+  // Note: we do this for unresolved classes to trigger re-verification at runtime.
+  if (C == CheckAccess::kYes && result->IsNonZeroReferenceTypes()) {
     const RegType& referrer = GetDeclaringClass();
-    if (!referrer.IsUnresolvedTypes() && !referrer.CanAccess(*result)) {
-      Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: '"
+    if (!referrer.CanAccess(*result)) {
+      Fail(VERIFY_ERROR_ACCESS_CLASS) << "(possibly) illegal class access: '"
                                       << referrer << "' -> '" << *result << "'";
     }
   }
@@ -4824,6 +4826,9 @@
     return nullptr;
   }
   if (klass_type.IsUnresolvedTypes()) {
+    // Accessibility checks depend on resolved fields.
+    DCHECK(klass_type.Equals(GetDeclaringClass()) || !failures_.empty());
+
     return nullptr;  // Can't resolve Class so no more to do here, will do checking at runtime.
   }
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -4862,6 +4867,9 @@
     return nullptr;
   }
   if (klass_type.IsUnresolvedTypes()) {
+    // Accessibility checks depend on resolved fields.
+    DCHECK(klass_type.Equals(GetDeclaringClass()) || !failures_.empty());
+
     return nullptr;  // Can't resolve Class so no more to do here
   }
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
diff --git a/test/954-invoke-polymorphic-verifier/smali/Unresolved.smali b/test/954-invoke-polymorphic-verifier/smali/Unresolved.smali
index 882f0e9..2604472 100644
--- a/test/954-invoke-polymorphic-verifier/smali/Unresolved.smali
+++ b/test/954-invoke-polymorphic-verifier/smali/Unresolved.smali
@@ -23,7 +23,7 @@
 .line 23
   invoke-direct {p0}, Ljava/lang/Object;-><init>()V
   # Get an unresolvable instance (abstract class)
-  invoke-static {}, LAbstract;->getUnresolvedInstance()Lother/thing/Foo;
+  invoke-static {}, LUnresolved;->getUnresolvedInstance()Lother/thing/Foo;
   move-result-object v0
   const-string v1, "1"
   const-string v2, "2"