Add reproduction of b/148525512

Bug: 148525512
Change-Id: I30e7ce81c7ca10b16e50fcd330788235f12d48dd
diff --git a/src/test/java/com/android/tools/r8/KotlinTestBase.java b/src/test/java/com/android/tools/r8/KotlinTestBase.java
index c3ce381..1a6a428 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestBase.java
@@ -39,6 +39,12 @@
         .collect(Collectors.toList());
   }
 
+  protected static Path getKotlinFileInTestPackage(Package pkg, String fileName)
+      throws IOException {
+    String folder = DescriptorUtils.getBinaryNameFromJavaType(pkg.getName());
+    return getKotlinFileInTest(folder, fileName);
+  }
+
   protected static Path getKotlinFileInTest(String folder, String fileName) {
     return Paths.get(ToolHelper.TESTS_DIR, "java", folder, fileName + FileUtils.KT_EXTENSION);
   }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 5094ec5..9b2f89b 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1189,8 +1189,11 @@
     consumer.finished(null);
   }
 
-  protected static void writeClassFileDataToJar(Path output, Collection<byte[]> classes)
-      throws IOException {
+  protected static void writeClassesToJar(Path output, Class<?>... classes) throws IOException {
+    writeClassesToJar(output, Arrays.asList(classes));
+  }
+
+  protected static void writeClassFileDataToJar(Path output, Collection<byte[]> classes) {
     ClassFileConsumer consumer = new ArchiveConsumer(output);
     for (byte[] clazz : classes) {
       consumer.accept(ByteDataView.of(clazz), extractClassDescriptor(clazz), null);
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
new file mode 100644
index 0000000..405d65c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
@@ -0,0 +1,162 @@
+// 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.kotlin.lambda.b148525512;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.utils.ArchiveResourceProvider;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class B148525512 extends KotlinTestBase {
+
+  private static final Package pkg = B148525512.class.getPackage();
+  private static final String kotlinTestClassesPackage = pkg.getName();
+  private static final String baseKtClassName = kotlinTestClassesPackage + ".BaseKt";
+  private static final String featureKtClassNamet = kotlinTestClassesPackage + ".FeatureKt";
+  private static final String baseClassName = kotlinTestClassesPackage + ".Base";
+
+  private static final Map<KotlinTargetVersion, Path> kotlinBaseClasses = new HashMap<>();
+  private static final Map<KotlinTargetVersion, Path> kotlinFeatureClasses = new HashMap<>();
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0},{1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        KotlinTargetVersion.values());
+  }
+
+  public B148525512(TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  @ClassRule public static TemporaryFolder classTemp = new TemporaryFolder();
+
+  @BeforeClass
+  public static void compileKotlin() throws Exception {
+    // Compile the base Kotlin with the FeatureAPI Java class on classpath.
+    Path featureApiJar = classTemp.newFile("feature_api.jar").toPath();
+    writeClassesToJar(featureApiJar, FeatureAPI.class);
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path ktBaseClasses =
+          kotlinc(KOTLINC, targetVersion)
+              .addClasspathFiles(featureApiJar)
+              .addSourceFiles(getKotlinFileInTestPackage(pkg, "base"))
+              .compile();
+      kotlinBaseClasses.put(targetVersion, ktBaseClasses);
+      // Compile the feature Kotlin code with the base classes on classpath.
+      Path ktFeatureClasses =
+          kotlinc(KOTLINC, targetVersion)
+              .addClasspathFiles(ktBaseClasses)
+              .addSourceFiles(getKotlinFileInTestPackage(pkg, "feature"))
+              .compile();
+      kotlinFeatureClasses.put(targetVersion, ktFeatureClasses);
+    }
+  }
+
+  private void checkLambdaGroups(CodeInspector inspector) {
+    List<FoundClassSubject> lambdaGroups =
+        inspector.allClasses().stream()
+            .filter(clazz -> clazz.getOriginalName().contains("LambdaGroup"))
+            .collect(Collectors.toList());
+    assertEquals(1, lambdaGroups.size());
+    MethodSubject invokeMethod = lambdaGroups.get(0).uniqueMethodWithName("invoke");
+    assertThat(invokeMethod, isPresent());
+    // The lambda group has 2 captures which capture "Base".
+    assertEquals(
+        2,
+        invokeMethod
+            .streamInstructions()
+            .filter(InstructionSubject::isCheckCast)
+            .filter(
+                instruction ->
+                    instruction.asCheckCast().getType().toSourceString().contains("Base"))
+            .count());
+    // TODO(b/148525512): The lambda group has 2 captures which capture "Feature".
+    assertEquals(
+        2,
+        invokeMethod
+            .streamInstructions()
+            .filter(InstructionSubject::isCheckCast)
+            .filter(
+                instruction ->
+                    instruction.asCheckCast().getType().toSourceString().contains("Feature"))
+            .count());
+  }
+
+  @Test
+  public void test() throws Exception {
+    Path featureCode = temp.newFile("feature.zip").toPath();
+    R8TestCompileResult compileResult =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+            .addProgramFiles(kotlinBaseClasses.get(targetVersion))
+            .addProgramClasses(FeatureAPI.class)
+            .addKeepMainRule(baseKtClassName)
+            .addKeepClassAndMembersRules(baseClassName)
+            .addKeepClassAndMembersRules(featureKtClassNamet)
+            .addKeepClassAndMembersRules(FeatureAPI.class)
+            .setMinApi(parameters.getApiLevel())
+            .noMinification() // The check cast inspection above relies on original names.
+            .addFeatureSplit(
+                builder ->
+                    builder
+                        .addProgramResourceProvider(
+                            ArchiveResourceProvider.fromArchive(
+                                kotlinFeatureClasses.get(targetVersion), true))
+                        .setProgramConsumer(new ArchiveConsumer(featureCode, false))
+                        .build())
+            .allowDiagnosticWarningMessages()
+            .compile()
+            .assertAllWarningMessagesMatch(
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+            .inspect(this::checkLambdaGroups);
+
+    // TODO(b/148525512): Fails on 7.0.0 and 8.1.0.
+    if (parameters.getRuntime().asDex().getVm().getVersion() == Version.V7_0_0
+        || parameters.getRuntime().asDex().getVm().getVersion() == Version.V8_1_0) {
+      return;
+    }
+
+    // Run the code without the feature code present.
+    compileResult
+        .run(parameters.getRuntime(), baseKtClassName)
+        .assertSuccessWithOutputLines("1", "2");
+
+    // Run the code with the feature code present.
+    compileResult
+        .addRunClasspathFiles(featureCode)
+        .run(parameters.getRuntime(), baseKtClassName)
+        .assertSuccessWithOutputLines("1", "2", "3", "4");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/FeatureAPI.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/FeatureAPI.java
new file mode 100644
index 0000000..95bc790
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/FeatureAPI.java
@@ -0,0 +1,27 @@
+// 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.kotlin.lambda.b148525512;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class FeatureAPI {
+  public static boolean hasFeature() {
+    try {
+      Class.forName(FeatureAPI.class.getPackage().getName() + ".FeatureKt");
+    } catch (ClassNotFoundException e) {
+      return false;
+    }
+    return true;
+  }
+
+  public static void feature(int i)
+      throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
+          IllegalAccessException {
+    Class<?> featureKtClass = Class.forName(FeatureAPI.class.getPackage().getName() + ".FeatureKt");
+    Method featureMethod = featureKtClass.getMethod("feature", int.class);
+    featureMethod.invoke(null, i);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/base.kt b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/base.kt
new file mode 100644
index 0000000..aced216
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/base.kt
@@ -0,0 +1,18 @@
+// 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.kotlin.lambda.b148525512
+
+fun printInt(l: () -> Int ) = println(l())
+
+open class Base(@JvmField var x: Int, @JvmField var y: Int)
+
+fun main(args: Array<String>) {
+  val base = Base(args.size + 1, args.size + 2)
+  printInt { base.x }
+  printInt { base.y }
+  if (FeatureAPI.hasFeature()) {
+    FeatureAPI.feature(args.size + 3)
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/feature.kt b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/feature.kt
new file mode 100644
index 0000000..3480225
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/feature.kt
@@ -0,0 +1,13 @@
+// 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.kotlin.lambda.b148525512
+
+class Feature(x: Int, y: Int) : Base(x, y)
+
+fun feature(i: Int) {
+  val f = Feature(i, i + 1)
+  printInt { f.x }
+  printInt { f.y }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CheckCastCfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CheckCastCfInstructionSubject.java
new file mode 100644
index 0000000..0f5c42c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CheckCastCfInstructionSubject.java
@@ -0,0 +1,27 @@
+// 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.utils.codeinspector;
+
+import com.android.tools.r8.cf.code.CfCheckCast;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.graph.DexType;
+
+public class CheckCastCfInstructionSubject extends CfInstructionSubject
+    implements CheckCastInstructionSubject {
+  public CheckCastCfInstructionSubject(CfInstruction instruction, MethodSubject method) {
+    super(instruction, method);
+    assert isCheckCast();
+  }
+
+  @Override
+  public DexType getType() {
+    return ((CfCheckCast) instruction).getType();
+  }
+
+  @Override
+  public CheckCastInstructionSubject asCheckCast() {
+    return this;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CheckCastDexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CheckCastDexInstructionSubject.java
new file mode 100644
index 0000000..ea10ad4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CheckCastDexInstructionSubject.java
@@ -0,0 +1,27 @@
+// 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.utils.codeinspector;
+
+import com.android.tools.r8.code.CheckCast;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.graph.DexType;
+
+public class CheckCastDexInstructionSubject extends DexInstructionSubject
+    implements CheckCastInstructionSubject {
+  public CheckCastDexInstructionSubject(Instruction instruction, MethodSubject method) {
+    super(instruction, method);
+    assert isCheckCast();
+  }
+
+  @Override
+  public DexType getType() {
+    return ((CheckCast) instruction).getType();
+  }
+
+  @Override
+  public CheckCastInstructionSubject asCheckCast() {
+    return this;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CheckCastInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CheckCastInstructionSubject.java
new file mode 100644
index 0000000..e7fa916
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CheckCastInstructionSubject.java
@@ -0,0 +1,11 @@
+// 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.utils.codeinspector;
+
+import com.android.tools.r8.graph.DexType;
+
+public interface CheckCastInstructionSubject extends InstructionSubject {
+  DexType getType();
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 5e1e668..63c85d5 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -354,6 +354,8 @@
       return new NewInstanceDexInstructionSubject(instruction, method);
     } else if (dexInst.isConstString(JumboStringMode.ALLOW)) {
       return new ConstStringDexInstructionSubject(instruction, method);
+    } else if (dexInst.isCheckCast()) {
+      return new CheckCastDexInstructionSubject(instruction, method);
     } else {
       return dexInst;
     }
@@ -369,6 +371,8 @@
       return new NewInstanceCfInstructionSubject(instruction, method);
     } else if (cfInst.isConstString(JumboStringMode.ALLOW)) {
       return new ConstStringCfInstructionSubject(instruction, method);
+    } else if (cfInst.isCheckCast()) {
+      return new CheckCastCfInstructionSubject(instruction, method);
     } else {
       return cfInst;
     }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index 9a48dfa..6eba32b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -84,6 +84,10 @@
 
   boolean isCheckCast(String type);
 
+  default CheckCastInstructionSubject asCheckCast() {
+    return null;
+  }
+
   boolean isInstanceOf();
 
   boolean isInstanceOf(String type);