Reproduce frame type bug when interface and null flows to a phi
Bug: b/337798768
Change-Id: I0ad0e12f54a2f19eb9c6f452e1f5dab30053d7c6
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierDefaultAnalysisConfig.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierDefaultAnalysisConfig.java
index db75814..f9e7860 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierDefaultAnalysisConfig.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierDefaultAnalysisConfig.java
@@ -18,7 +18,10 @@
private final ProgramMethod method;
CfFrameVerifierDefaultAnalysisConfig(AppView<?> appView, CfCode code, ProgramMethod method) {
- this.assignability = new CfAssignability(appView);
+ this.assignability =
+ appView.testing().enableStrictFrameVerification
+ ? new CfSubtypingAssignability(appView.withClassHierarchy(), false)
+ : new CfAssignability(appView);
this.code = code;
this.method = method;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSubtypingAssignability.java b/src/main/java/com/android/tools/r8/cf/code/CfSubtypingAssignability.java
index 147e879..d2867f5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfSubtypingAssignability.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSubtypingAssignability.java
@@ -11,8 +11,17 @@
public class CfSubtypingAssignability extends CfAssignability {
+ private final boolean allowAnyAssignmentToInterfaceTypes;
+
public CfSubtypingAssignability(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ this(appView, true);
+ }
+
+ public CfSubtypingAssignability(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ boolean allowAnyAssignmentToInterfaceTypes) {
super(appView);
+ this.allowAnyAssignmentToInterfaceTypes = allowAnyAssignmentToInterfaceTypes;
}
@Override
@@ -24,9 +33,11 @@
if (source.toTypeElement(appView).lessThanOrEqual(target.toTypeElement(appView), appView)) {
return true;
}
- DexClass targetClass = appView.definitionFor(target);
- if (targetClass != null && targetClass.isInterface()) {
- return true;
+ if (allowAnyAssignmentToInterfaceTypes) {
+ DexClass targetClass = appView.definitionFor(target);
+ if (targetClass != null && targetClass.isInterface()) {
+ return true;
+ }
}
return false;
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 683f441..23a0e46 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2341,6 +2341,7 @@
public boolean enableDeadSwitchCaseElimination = true;
public boolean enableInvokeSuperToInvokeVirtualRewriting = true;
public boolean enableMultiANewArrayDesugaringForClassFiles = false;
+ public boolean enableStrictFrameVerification = false;
public boolean enableSyntheticSharing = true;
public boolean enableSwitchToIfRewriting = true;
public boolean enableEnumUnboxingDebugLogs =
diff --git a/src/test/java/com/android/tools/r8/regress/Regress337798768.java b/src/test/java/com/android/tools/r8/regress/Regress337798768.java
new file mode 100644
index 0000000..567db5a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/Regress337798768.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2024, 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;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress337798768 extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withCfRuntimes().build();
+ }
+
+ public Regress337798768(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ // TODO(b/337798768): should not fail verification
+ @Test(expected = CompilationFailedException.class)
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(o -> o.testing.enableStrictFrameVerification = true)
+ .addKeepClassAndMembersRules(A.class, B.class, I.class)
+ .compile();
+ }
+
+ interface I {
+ void foobar();
+ }
+
+ public static class A implements I {
+
+ @Override
+ public void foobar() {
+ System.out.println("foobar");
+ }
+ }
+
+ public static class B implements I {
+ @Override
+ public void foobar() {
+ System.out.println("B");
+ }
+ }
+
+ public static class TestClass {
+ public static void main(String[] args) {
+ I i =
+ System.currentTimeMillis() == 0
+ ? null
+ : System.currentTimeMillis() == 0 ? new B() : new A();
+ i.foobar();
+ }
+ }
+}