Use interface bit in lambda target.

Bug: 166485528
Bug: 166732606
Bug: 166726895
Change-Id: Ibe8249ede4203ecbe9ae8f49fbd7ab46b4058edb
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index d203a12..9ba4398 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
@@ -33,7 +32,6 @@
 import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OptionalBool;
 import com.google.common.base.Suppliers;
 import com.google.common.primitives.Longs;
@@ -538,18 +536,8 @@
       return accessibilityBridge;
     }
 
-    boolean holderIsInterface() {
-      InternalOptions options = appView.options();
-      DexMethod implMethod = descriptor.implHandle.asMethod();
-      DexClass implMethodHolder = appView.definitionFor(implMethod.holder);
-      if (implMethodHolder == null) {
-        assert options
-            .desugaredLibraryConfiguration
-            .getBackportCoreLibraryMember()
-            .containsKey(implMethod.holder);
-        return false;
-      }
-      return implMethodHolder.isInterface();
+    boolean isInterface() {
+      return descriptor.implHandle.isInterface;
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index a5cf917..0c64b12 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -232,7 +232,7 @@
                 methodToCall.proto,
                 argValueTypes,
                 argRegisters,
-                target.holderIsInterface()));
+                target.isInterface()));
 
     // Does the method have return value?
     if (enforcedReturnType.isVoidType()) {
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 6f18ef1..05f7848 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1482,7 +1482,7 @@
     }
     Path path = temp.newFolder().toPath().resolve("classes.jar");
     ArchiveConsumer consumer = new ArchiveConsumer(path);
-    for (Class clazz : classes) {
+    for (Class<?> clazz : classes) {
       consumer.accept(
           ByteDataView.of(ToolHelper.getClassAsBytes(clazz)),
           DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()),
@@ -1492,6 +1492,24 @@
     return path;
   }
 
+  public Path buildOnDexRuntime(TestParameters parameters, byte[]... classes)
+      throws IOException, CompilationFailedException {
+    if (parameters.isDexRuntime()) {
+      return testForD8()
+          .addProgramClassFileData(classes)
+          .setMinApi(parameters.getApiLevel())
+          .compile()
+          .writeToZip();
+    }
+    Path path = temp.newFolder().toPath().resolve("classes.jar");
+    ArchiveConsumer consumer = new ArchiveConsumer(path);
+    for (byte[] clazz : classes) {
+      consumer.accept(ByteDataView.of(clazz), extractClassDescriptor(clazz), null);
+    }
+    consumer.finished(null);
+    return path;
+  }
+
   public static String binaryName(Class<?> clazz) {
     return DescriptorUtils.getBinaryNameFromJavaType(typeName(clazz));
   }
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/StaticInterfaceMethodReferenceTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/StaticInterfaceMethodReferenceTest.java
new file mode 100644
index 0000000..627e0ae
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/StaticInterfaceMethodReferenceTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2020, 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.desugaring.interfacemethods;
+
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StaticInterfaceMethodReferenceTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final boolean isInterface;
+
+  @Parameterized.Parameters(name = "{0}, itf:{1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
+        BooleanUtils.values());
+  }
+
+  public StaticInterfaceMethodReferenceTest(TestParameters parameters, boolean isInterface) {
+    this.parameters = parameters;
+    this.isInterface = isInterface;
+  }
+
+  @Test
+  public void test() throws Exception {
+    TestRunResult<?> result;
+    if (parameters.isCfRuntime()) {
+      result =
+          testForJvm()
+              .addProgramClasses(TestClass.class)
+              .addProgramClassFileData(getTarget(isInterface))
+              .run(parameters.getRuntime(), TestClass.class);
+    } else {
+      result =
+          testForD8()
+              .addOptionsModification(o -> o.testing.allowInvokeErrors = true)
+              .addProgramClasses(TestClass.class)
+              .addProgramClassFileData(getTarget(isInterface))
+              .run(parameters.getRuntime(), TestClass.class);
+    }
+    checkResult(result);
+  }
+
+  private void checkResult(TestRunResult<?> result) {
+    // If the reference is of correct type, or running on JDK8 which allows mismatch the
+    // output is the expected print.
+    if (isInterface
+        || (parameters.isCfRuntime() && parameters.getRuntime().asCf().getVm() == CfVm.JDK8)) {
+      result.assertSuccessWithOutputLines("Target::foo");
+      return;
+    }
+
+    // DEX runtimes do not check method reference types so the code will run.
+    // TODO(b/166732606): Compile to an ICCE? What about the "missing" target case?
+    assertFalse(isInterface);
+    if (parameters.isDexRuntime()) {
+      result.assertSuccessWithOutputLines("Target::foo");
+      return;
+    }
+
+    // Otherwise the run should result in an ICCE.
+    result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+  }
+
+  private static byte[] getTarget(boolean isInterface) throws Exception {
+    return transformer(Target.class)
+        .setAccessFlags(
+            classAccessFlags -> {
+              if (isInterface) {
+                assert !classAccessFlags.isSuper();
+                assert classAccessFlags.isAbstract();
+                assert classAccessFlags.isInterface();
+              } else {
+                classAccessFlags.unsetAbstract();
+                classAccessFlags.unsetInterface();
+              }
+            })
+        .transform();
+  }
+
+  interface Target {
+
+    static void foo() {
+      System.out.println("Target::foo");
+    }
+  }
+
+  static class TestClass {
+
+    static void call(Runnable fn) {
+      fn.run();
+    }
+
+    public static void main(String[] args) {
+      call(Target::foo);
+    }
+  }
+}