Version 1.2.42.

Merge: Mark argument and return types as live for all targeted methods.
CL: https://r8-review.googlesource.com/c/r8/+/25420

Merge: Remove special case for synthetic constructor argument types.
CL: https://r8-review.googlesource.com/c/r8/+/25422

Merge: Add regression test for b/112517039.
CL: https://r8-review.googlesource.com/c/r8/+/25540

R=sgjesse@google.com

Bug: 112517039
Change-Id: I76998f40002cf7c1b8b4c60b2a5de6745a1a6620
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 3ad2ac7..84ab0fe 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.2.41";
+  public static final String LABEL = "1.2.42";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 42f9174..32b03180f 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -638,6 +638,7 @@
 
   private void markMethodAsTargeted(DexEncodedMethod encodedMethod, KeepReason reason) {
     markTypeAsLive(encodedMethod.method.holder);
+    markParameterAndReturnTypesAsLive(encodedMethod);
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Method `%s` is targeted.", encodedMethod.method);
     }
@@ -862,22 +863,6 @@
     if (!liveMethods.contains(encodedMethod)) {
       markTypeAsLive(encodedMethod.method.holder);
       markMethodAsTargeted(encodedMethod, reason);
-      // For granting inner/outer classes access to their private constructors, javac generates
-      // additional synthetic constructors. These constructors take a synthetic class
-      // as argument. As it is not possible to express a keep rule for these synthetic classes
-      // always keep synthetic arguments to synthetic constructors. See b/69825683.
-      if (encodedMethod.isInstanceInitializer() && encodedMethod.isSyntheticMethod()) {
-        for (DexType type : encodedMethod.method.proto.parameters.values) {
-          type = type.isArrayType() ? type.toBaseType(appInfo.dexItemFactory) : type;
-          if (type.isPrimitiveType()) {
-            continue;
-          }
-          DexClass clazz = appInfo.definitionFor(type);
-          if (clazz != null && clazz.accessFlags.isSynthetic()) {
-            markTypeAsLive(type);
-          }
-        }
-      }
       if (Log.ENABLED) {
         Log.verbose(getClass(), "Method `%s` has become live due to direct invoke",
             encodedMethod.method);
@@ -1254,14 +1239,7 @@
           markVirtualMethodAsLive(superCallTarget, KeepReason.invokedViaSuperFrom(method));
         }
       }
-      for (DexType parameterType : method.method.proto.parameters.values) {
-        if (parameterType.isArrayType()) {
-          parameterType = parameterType.toBaseType(appInfo.dexItemFactory);
-        }
-        if (parameterType.isClassType()) {
-          markTypeAsLive(parameterType);
-        }
-      }
+      markParameterAndReturnTypesAsLive(method);
       processAnnotations(method.annotations.annotations);
       method.parameterAnnotationsList.forEachAnnotation(this::processAnnotation);
       if (protoLiteExtension != null && protoLiteExtension.appliesTo(method)) {
@@ -1274,6 +1252,24 @@
     }
   }
 
+  private void markParameterAndReturnTypesAsLive(DexEncodedMethod method) {
+    for (DexType parameterType : method.method.proto.parameters.values) {
+      if (parameterType.isArrayType()) {
+        parameterType = parameterType.toBaseType(appInfo.dexItemFactory);
+      }
+      if (parameterType.isClassType()) {
+        markTypeAsLive(parameterType);
+      }
+    }
+    DexType returnType = method.method.proto.returnType;
+    if (returnType.isArrayType()) {
+      returnType = returnType.toBaseType(appInfo.dexItemFactory);
+    }
+    if (returnType.isClassType()) {
+      markTypeAsLive(returnType);
+    }
+  }
+
   private void collectProguardCompatibilityRule(KeepReason reason) {
     if (reason.isDueToProguardCompatibility() && compatibility != null) {
       compatibility.addRule(reason.getProguardKeepRule());
diff --git a/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java
new file mode 100644
index 0000000..ace3bf5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2018 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.shaking;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isRenamed;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+class B112517039ReturnType extends Exception {
+}
+
+interface B112517039I {
+  B112517039ReturnType m();
+  void flaf(Exception e);
+}
+
+class B112517039Caller {
+  public void call(B112517039I i) {
+    System.out.println("Ewwo!");
+    i.flaf(i.m());
+  }
+}
+
+class B112517039Main {
+  public static void main(String[] args) {
+    try {
+      B112517039Caller caller = new B112517039Caller();
+      caller.call(null);
+    } catch (NullPointerException e) {
+      System.out.println("NullPointerException");
+    }
+  }
+}
+
+@RunWith(VmTestRunner.class)
+public class ReturnTypeTest extends TestBase {
+  @Test
+  public void testFromJavac() throws Exception {
+    String mainName = B112517039Main.class.getCanonicalName();
+    ProcessResult javaResult = ToolHelper.runJava(ToolHelper.getClassPathForTests(), mainName);
+    assertEquals(0, javaResult.exitCode);
+    assertThat(javaResult.stdout, containsString("Ewwo!"));
+    assertThat(javaResult.stdout, containsString("NullPointerException"));
+
+    List<String> config = ImmutableList.of(
+        "-printmapping",
+        "-keep class " + mainName + " {",
+        "  public static void main(...);",
+        "}"
+    );
+    R8Command.Builder builder = R8Command.builder();
+    builder.addProgramFiles(ToolHelper.getClassFilesForTestDirectory(
+        ToolHelper.getPackageDirectoryForTestPackage(B112517039Main.class.getPackage()),
+        path -> path.getFileName().toString().startsWith("B112517039")));
+    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+    builder.addProguardConfiguration(config, Origin.unknown());
+    AndroidApp processedApp = ToolHelper.runR8(builder.build(), options -> {
+      options.enableInlining = false;
+    });
+
+    Path outDex = temp.getRoot().toPath().resolve("dex.zip");
+    processedApp.writeToZip(outDex, OutputMode.DexIndexed);
+    ProcessResult artResult = ToolHelper.runArtNoVerificationErrorsRaw(outDex.toString(), mainName);
+    assertEquals(0, artResult.exitCode);
+    assertThat(javaResult.stdout, containsString("Ewwo!"));
+    assertThat(javaResult.stdout, containsString("NullPointerException"));
+
+    DexInspector inspector = new DexInspector(processedApp);
+    ClassSubject returnType = inspector.clazz(B112517039ReturnType.class);
+    assertThat(returnType, isRenamed());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
index 81fd6b0..3be0feb 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -94,9 +94,10 @@
 
       Result result = runTest(mainClass, proguardConfig);
 
-      // Without includedescriptorclasses return type is removed.
       result.assertKept(ClassWithNativeMethods.class);
-      result.assertRemoved(NativeReturnType.class);
+      // Return types are not removed as they can be needed for verification.
+      // See b/112517039.
+      result.assertRenamed(NativeReturnType.class);
       // Argument type is not removed due to the concern about the broken type hierarchy.
       result.assertRenamed(NativeArgumentType.class);
       // Field type is not removed due to the concern about the broken type hierarchy.