Reproduce IllegalAccessError due to a chain of inlinings (b/111080693).

class p1.A {
  protected ... field
}

class p2.Outer {
  private class B {
    ... bar() { ... }
  }
  static class C extends p1.A {
    ... foo() {
      access field
      invoke B#bar()
    }
  }
  static class D {
    ... baz() { ... invoke C#foo() ... }
  }
}

Field access in C#foo restricts itself from being inlined to subtypes.
However, if B#bar is inlined, its inlining constraint, PACKAGE,
overrides the inlining constraint for C#foo, and made it eligible for
being inlined to D#baz, where that field access is no longer allowed.

Bug: 111080693
Change-Id: I55e0a8ca9cdf78f3bdfc50471c43ebe91dfdaa21
diff --git a/src/test/java/com/android/tools/r8/regress/b111080693/B111080693.java b/src/test/java/com/android/tools/r8/regress/b111080693/B111080693.java
new file mode 100644
index 0000000..d424c39
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b111080693/B111080693.java
@@ -0,0 +1,51 @@
+// 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.b111080693;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+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.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.regress.b111080693.a.Observable;
+import com.android.tools.r8.regress.b111080693.b.RecyclerView;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(VmTestRunner.class)
+public class B111080693 extends TestBase {
+  @Ignore("b/111080693")
+  @Test
+  public void test() throws Exception {
+    R8Command.Builder builder = R8Command.builder();
+    builder.addProgramFiles(
+        ToolHelper.getClassFilesForTestPackage(Observable.class.getPackage()));
+    builder.addProgramFiles(
+        ToolHelper.getClassFilesForTestPackage(RecyclerView.class.getPackage()));
+    builder.addProgramFiles(ToolHelper.getClassFileForTestClass(TestMain.class));
+    builder.addProgramFiles(ToolHelper.getClassFileForTestClass(TestMain.TestAdapter.class));
+    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+    String config = keepMainProguardConfiguration(TestMain.class);
+    builder.addProguardConfiguration(
+        ImmutableList.of(config,
+            "-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*"),
+        Origin.unknown());
+    AndroidApp app = ToolHelper.runR8(builder.build());
+    ProcessResult result = runOnArtRaw(app, TestMain.class);
+    assertEquals(0, result.exitCode);
+    assertThat(result.stderr, not(containsString("IllegalAccessError")));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b111080693/TestMain.java b/src/test/java/com/android/tools/r8/regress/b111080693/TestMain.java
new file mode 100644
index 0000000..8979250
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b111080693/TestMain.java
@@ -0,0 +1,22 @@
+// 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.b111080693;
+
+import com.android.tools.r8.regress.b111080693.b.RecyclerView;
+import com.android.tools.r8.regress.b111080693.b.RecyclerView.Adapter;
+
+public class TestMain {
+  static final class TestAdapter extends Adapter {
+    TestAdapter() {
+    }
+  }
+
+  public static void main(String[] args) {
+    TestAdapter adapter = new TestAdapter();
+    RecyclerView view = new RecyclerView();
+    view.setAdapter(adapter);
+    adapter.notifyDataSetChanged();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b111080693/a/Observable.java b/src/test/java/com/android/tools/r8/regress/b111080693/a/Observable.java
new file mode 100644
index 0000000..c7a4858
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b111080693/a/Observable.java
@@ -0,0 +1,15 @@
+// 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.b111080693.a;
+
+import java.util.ArrayList;
+
+public abstract class Observable<T> {
+  /**
+   * The list of observers.  An observer can be in the list at most
+   * once and will never be null.
+   */
+  protected final ArrayList<T> mObservers = new ArrayList<T>();
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b111080693/b/RecyclerView.java b/src/test/java/com/android/tools/r8/regress/b111080693/b/RecyclerView.java
new file mode 100644
index 0000000..06347fe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b111080693/b/RecyclerView.java
@@ -0,0 +1,78 @@
+// 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.b111080693.b;
+
+import com.android.tools.r8.regress.b111080693.a.Observable;
+
+public class RecyclerView {
+
+  final State mState = new State();
+
+  public static class State {
+    boolean mStructureChanged = false;
+  }
+
+  public abstract static class AdapterDataObserver {
+    public void onChanged() {
+      // Do nothing
+    }
+  }
+
+  private class RecyclerViewDataObserver extends AdapterDataObserver {
+    RecyclerViewDataObserver() {
+    }
+
+    @Override
+    public void onChanged() {
+      // This is the single target of AdapterDataObserver#onChanged(), and could be inlined to
+      // AdapterDataObservable#notifyChanged() as long as this preserves null check of the receiver.
+      // To do so, access the enclosing class' member to use the receiver.
+      mState.mStructureChanged = true;
+    }
+  }
+
+  static class AdapterDataObservable extends Observable<AdapterDataObserver> {
+    public void registerObserver(AdapterDataObserver observer) {
+      mObservers.add(observer);
+    }
+    public void notifyChanged() {
+      for (int i = mObservers.size() - 1; i >= 0; i--) {
+        // The single target, RecyclerViewDataObserver#onChange is inlined, along with check-cast:
+        //    AdapterDataObserver observer_i = mObservers.get(i);
+        //    RecyclerViewDataObserver casted_obs = (RecyclerViewDataObserver) observer_i;
+        //    // inlining RecyclerViewDataObserver#onChanged():
+        mObservers.get(i).onChanged();
+      }
+    }
+  }
+
+  public abstract static class Adapter {
+    private final AdapterDataObservable mObservable = new AdapterDataObservable();
+
+    public void registerAdapterDataObserver(AdapterDataObserver observer) {
+      mObservable.registerObserver(observer);
+    }
+
+    public final void notifyDataSetChanged() {
+      // Single callee, AdapterDataObservable#notifyChanged(), could be inlined, but should not.
+      // Accessing AdapterDataObservable.mObservers, which is a protected field in Observable,
+      // results in an illegal access error.
+      //
+      // Without the above inlining, the inlining constraint for the target here is SUBCLASS due to
+      // that protected field, and thus decline to inline because the holder, Adapter, is not a
+      // subtype of the target holder, AdapterDataObservable.
+      // However, after the above inlining, check-cast to RecyclerViewDataObserver overrides that
+      // condition to PACKAGE, which accidentally allows the target to be inlined here.
+      mObservable.notifyChanged();
+    }
+  }
+
+  private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
+
+  public void setAdapter(Adapter adapter) {
+    adapter.registerAdapterDataObserver(mObserver);
+  }
+
+}