Merge "Gmscore: comment out 'checkdiscard' from proguard config files."
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 92dc9cd..1c7e92d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -158,6 +158,14 @@
   }
 
 
+  /**
+   * Returns true if this method is synthetic.
+   */
+  public boolean isSyntheticMethod() {
+    return accessFlags.isSynthetic();
+  }
+
+
   public boolean isInliningCandidate(DexEncodedMethod container, Reason inliningReason,
       AppInfoWithSubtyping appInfo) {
     if (isClassInitializer()) {
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 1ae38e9..b1a2102 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -705,6 +705,22 @@
     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);
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
new file mode 100644
index 0000000..9026740
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
@@ -0,0 +1,84 @@
+// 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.regress.b69825683;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+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.FoundClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+
+public class Regress69825683Test extends TestBase {
+
+  @Test
+  public void outerConstructsInner() throws Exception {
+    Class mainClass = com.android.tools.r8.regress.b69825683.outerconstructsinner.Outer.class;
+    R8Command.Builder builder = R8Command.builder();
+    builder.addProgramFiles(ToolHelper.getClassFilesForTestPackage(mainClass.getPackage()));
+    builder.addProguardConfiguration(ImmutableList.of(
+        "-keep class " + mainClass.getCanonicalName() + " {",
+        "  public static void main(java.lang.String[]);",
+        "}",
+        "-dontobfuscate"),
+        Origin.unknown());
+    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    AndroidApp app = ToolHelper.runR8(builder.build());
+    DexInspector inspector = new DexInspector(app);
+    List<FoundClassSubject> classes = inspector.allClasses();
+
+    // Check that the synthetic class is still present.
+    assertEquals(3, classes.size());
+    assertEquals(1,
+        classes.stream()
+            .map(FoundClassSubject::getOriginalName)
+            .filter(name  -> name.endsWith("$1"))
+            .count());
+
+    // Run code to check that the constructor with synthetic class as argument is present.
+    Class innerClass =
+        com.android.tools.r8.regress.b69825683.outerconstructsinner.Outer.Inner.class;
+    String innerName = innerClass.getCanonicalName();
+    int index = innerName.lastIndexOf('.');
+    innerName = innerName.substring(0, index) + "$" + innerName.substring(index + 1);
+    assertTrue(runOnArt(app, mainClass).startsWith(innerName));
+  }
+
+  @Test
+  public void innerConstructsOuter() throws Exception {
+    Class mainClass = com.android.tools.r8.regress.b69825683.innerconstructsouter.Outer.class;
+    R8Command.Builder builder = R8Command.builder();
+    builder.addProgramFiles(ToolHelper.getClassFilesForTestPackage(mainClass.getPackage()));
+    builder.addProguardConfiguration(ImmutableList.of(
+        "-keep class " + mainClass.getCanonicalName() + " {",
+        "  public static void main(java.lang.String[]);",
+        "}",
+        "-dontobfuscate"),
+        Origin.unknown());
+    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    AndroidApp app = ToolHelper.runR8(builder.build());
+    DexInspector inspector = new DexInspector(app);
+    List<FoundClassSubject> classes = inspector.allClasses();
+
+    // Check that the synthetic class is still present.
+    assertEquals(3, classes.size());
+    assertEquals(1,
+        classes.stream()
+            .map(FoundClassSubject::getOriginalName)
+            .filter(name  -> name.endsWith("$1"))
+            .count());
+
+    // Run code to check that the constructor with synthetic class as argument is present.
+    assertTrue(runOnArt(app, mainClass).startsWith(mainClass.getCanonicalName()));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/innerconstructsouter/Outer.java b/src/test/java/com/android/tools/r8/regress/b69825683/innerconstructsouter/Outer.java
new file mode 100644
index 0000000..8241c18
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/innerconstructsouter/Outer.java
@@ -0,0 +1,25 @@
+// 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.regress.b69825683.innerconstructsouter;
+
+public class Outer {
+
+  private Outer() {
+  }
+
+  public static class Inner {
+    public Outer build() {
+      return new Outer();
+    }
+  }
+
+  public static void main(String args[]) {
+    Inner builder = new Inner();
+    builder.build();
+    for (java.lang.reflect.Constructor m : Outer.class.getDeclaredConstructors()) {
+      System.out.println(m);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/outerconstructsinner/Outer.java b/src/test/java/com/android/tools/r8/regress/b69825683/outerconstructsinner/Outer.java
new file mode 100644
index 0000000..79bccc5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/outerconstructsinner/Outer.java
@@ -0,0 +1,25 @@
+// 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.regress.b69825683.outerconstructsinner;
+
+public class Outer {
+
+  public Outer() {
+    new Inner();
+  }
+
+  public class Inner {
+
+    private Inner() {
+    }
+  }
+
+  public static void main(String args[]) {
+    new Outer();
+    for (java.lang.reflect.Constructor m : Outer.Inner.class.getDeclaredConstructors()) {
+      System.out.println(m);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index bbd6fb5..b8ca32e 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -189,18 +189,19 @@
   }
 
   public void forAllClasses(Consumer<FoundClassSubject> inspection) {
-    forAll(application.classes(), clazz -> {
-      ClassNamingForNameMapper naming = null;
-      if (mapping != null) {
-        String obfuscated = originalToObfuscatedMapping.get(clazz.type.toSourceString());
-        if (obfuscated != null) {
-          naming = mapping.getClassNaming(obfuscated);
-        }
-      }
-      return new FoundClassSubject(clazz, naming);
+    forAll(application.classes(), cls -> {
+      ClassSubject found = clazz(cls.type.toSourceString());
+      assert found.isPresent();
+      return (FoundClassSubject) found;
     }, inspection);
   }
 
+  public List<FoundClassSubject> allClasses() {
+    ImmutableList.Builder<FoundClassSubject> builder = ImmutableList.builder();
+    forAllClasses(builder::add);
+    return builder.build();
+  }
+
   public MethodSubject method(Method method) {
     ClassSubject clazz = clazz(method.getDeclaringClass());
     if (!clazz.isPresent()) {
@@ -310,6 +311,8 @@
 
     public abstract AnnotationSubject annotation(String name);
 
+    public abstract String getOriginalName();
+
     public abstract String getOriginalDescriptor();
 
     public abstract String getFinalDescriptor();
@@ -364,6 +367,11 @@
     }
 
     @Override
+    public String getOriginalName() {
+      return null;
+    }
+
+    @Override
     public String getOriginalDescriptor() {
       return null;
     }
@@ -518,6 +526,15 @@
     }
 
     @Override
+    public String getOriginalName() {
+      if (naming != null) {
+        return naming.originalName;
+      } else {
+        return DescriptorUtils.descriptorToJavaType(getFinalDescriptor());
+      }
+    }
+
+    @Override
     public String getOriginalDescriptor() {
       if (naming != null) {
         return DescriptorUtils.javaTypeToDescriptor(naming.originalName);