Don't allow inlining of code that will error because of features
Bug: 181571571
Change-Id: I65edb6093405dd605230bf56e61dc60300944a8b
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index d96643d..c8c462b 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -159,6 +159,11 @@
public boolean isInBaseOrSameFeatureAs(
DexProgramClass clazz, ProgramDefinition context, SyntheticItems syntheticItems) {
+ return isInBaseOrSameFeatureAs(clazz.getContextType(), context, syntheticItems);
+ }
+
+ public boolean isInBaseOrSameFeatureAs(
+ DexType clazz, ProgramDefinition context, SyntheticItems syntheticItems) {
FeatureSplit split = getFeatureSplit(clazz, syntheticItems);
return split.isBase() || split == getFeatureSplit(context, syntheticItems);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index 976e869..e00c267 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -370,6 +370,17 @@
// This will fail at runtime.
return ConstraintWithTarget.NEVER;
}
+ if (!appView
+ .appInfo()
+ .getClassToFeatureSplitMap()
+ .isInBaseOrSameFeatureAs(
+ resolvedMember.getHolderType(),
+ context.asProgramMethod(),
+ appView.getSyntheticItems())) {
+ // We never inline into the base from a feature (calls should never happen) and we
+ // never inline between features, so this check should be sufficient.
+ return ConstraintWithTarget.NEVER;
+ }
DexType resolvedHolder = graphLens.lookupType(resolvedMember.getHolderType());
assert initialResolutionHolder != null;
ConstraintWithTarget memberConstraintWithTarget =
diff --git a/src/test/java/com/android/tools/r8/regress/Regress181837660.java b/src/test/java/com/android/tools/r8/regress/Regress181837660.java
index 7fd9e77..b5e0a7c 100644
--- a/src/test/java/com/android/tools/r8/regress/Regress181837660.java
+++ b/src/test/java/com/android/tools/r8/regress/Regress181837660.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.regress;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -14,7 +16,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.dexsplitter.SplitterTestBase;
-import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableSet;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -28,8 +29,6 @@
@RunWith(Parameterized.class)
public class Regress181837660 extends SplitterTestBase {
- public static final String EXPECTED = StringUtils.lines("42");
-
@Parameters(name = "{0}")
public static TestParametersCollection params() {
return getTestParameters().withDexRuntimes().withAllApiLevels().build();
@@ -67,10 +66,26 @@
FeatureClass.class,
b -> {},
this::configureNoInlineAnnotations);
- // TODO(b/181571571): This should not succeed as illustrated by non inlining case
- assertEquals(0, processResult.exitCode);
+ // This should not succeed as illustrated by non inlining case
+ assertEquals(1, processResult.exitCode);
// We can't actually read the field since it is in the feature.
- assertFalse(processResult.stderr.contains("NoClassDefFoundError"));
+ assertThat(processResult.stderr, containsString("NoClassDefFoundError"));
+ }
+
+ @Test
+ public void testRegress181571571StillInlineValid() throws Exception {
+ ProcessResult processResult =
+ testR8Splitter(
+ parameters,
+ ImmutableSet.of(Base2Class.class),
+ ImmutableSet.of(Feature2Class.class),
+ Feature2Class.class,
+ r8TestCompileResult ->
+ r8TestCompileResult.inspect(
+ base -> assertFalse(base.clazz(Base2Class.class).isPresent())),
+ this::configureNoInlineAnnotations);
+ assertEquals(0, processResult.exitCode);
+ assertEquals(processResult.stdout, "42\n");
}
private void configure(R8FullTestBuilder testBuilder) throws NoSuchMethodException {
@@ -83,20 +98,52 @@
}
public static class BaseClass {
+
@NeverInline
public static String getFromFeature() {
return FeatureClass.featureString;
}
+
+ @NeverInline
+ public static String getSecondFromFeature() {
+ return FeatureClass.getFeatureString();
+ }
}
public static class FeatureClass implements RunInterface {
public static String featureString = "22";
+ public static String getFeatureString() {
+ return "42";
+ }
+
public static String getAString() {
return BaseClass.getFromFeature();
}
+ public static String getSecondString() {
+ return BaseClass.getSecondFromFeature();
+ }
+
+ @Override
+ public void run() {
+ System.out.println(getAString());
+ System.out.println(getSecondString());
+ }
+ }
+
+ public static class Base2Class {
+ public static String getFromFeature() {
+ return System.currentTimeMillis() > 2 ? "42" : "-19";
+ }
+ }
+
+ public static class Feature2Class implements RunInterface {
+ public static String getAString() {
+ return Base2Class.getFromFeature();
+ }
+
@Override
public void run() {
System.out.println(getAString());