Propagate types after lambda desugaring
Bug: 140284170
Change-Id: I11ea2c664cfdeb85c3dc54f2d3991aebf1ab5b65
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 6b4b02b..7e6fffe 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -199,4 +199,14 @@
return clazz != context;
}
}
+
+ @Override
+ public boolean verifyTypes(AppView<?> appView) {
+ TypeLatticeElement type = outValue().getTypeLattice();
+ assert type.isClassType();
+ assert type.asClassTypeLatticeElement().getClassType() == clazz
+ || appView.options().testing.allowTypeErrors;
+ assert type.isDefinitelyNotNull();
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index fb64572..3dbb5d0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
@@ -33,6 +34,7 @@
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
@@ -113,6 +115,7 @@
* <p>NOTE: this method can be called concurrently for several different methods.
*/
public void desugarLambdas(DexEncodedMethod encodedMethod, IRCode code) {
+ Set<Value> affectedValues = Sets.newIdentityHashSet();
DexType currentType = encodedMethod.method.holder;
ListIterator<BasicBlock> blocks = code.listIterator();
while (blocks.hasNext()) {
@@ -121,8 +124,8 @@
while (instructions.hasNext()) {
Instruction instruction = instructions.next();
if (instruction.isInvokeCustom()) {
- LambdaDescriptor descriptor = inferLambdaDescriptor(
- instruction.asInvokeCustom().getCallSite());
+ InvokeCustom invoke = instruction.asInvokeCustom();
+ LambdaDescriptor descriptor = inferLambdaDescriptor(invoke.getCallSite());
if (descriptor == LambdaDescriptor.MATCH_FAILED) {
continue;
}
@@ -134,10 +137,14 @@
// We rely on patch performing its work in a way which
// keeps both `instructions` and `blocks` iterators in
// valid state so that we can continue iteration.
- patchInstruction(lambdaClass, code, blocks, instructions);
+ patchInstruction(invoke, lambdaClass, code, blocks, instructions, affectedValues);
}
}
}
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
+ assert code.isConsistentSSA();
}
public void desugarLambda(
@@ -320,14 +327,15 @@
// Patches invoke-custom instruction to create or get an instance
// of the generated lambda class.
- private void patchInstruction(LambdaClass lambdaClass, IRCode code,
- ListIterator<BasicBlock> blocks, InstructionListIterator instructions) {
+ private void patchInstruction(
+ InvokeCustom invoke,
+ LambdaClass lambdaClass,
+ IRCode code,
+ ListIterator<BasicBlock> blocks,
+ InstructionListIterator instructions,
+ Set<Value> affectedValues) {
assert lambdaClass != null;
assert instructions != null;
- assert instructions.peekPrevious().isInvokeCustom();
-
- // Move to the previous instruction, must be InvokeCustom
- InvokeCustom invoke = instructions.previous().asInvokeCustom();
// The value representing new lambda instance: we reuse the
// value from the original invoke-custom instruction, and thus
@@ -338,6 +346,8 @@
lambdaInstanceValue =
code.createValue(
TypeLatticeElement.fromDexType(lambdaClass.type, Nullability.maybeNull(), appView));
+ } else {
+ affectedValues.add(lambdaInstanceValue);
}
// For stateless lambdas we replace InvokeCustom instruction with StaticGet
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index b31acbc..0c38093 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.utils.ArtErrorParser;
import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.TestDescriptionWatcher;
@@ -52,6 +53,7 @@
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.BiFunction;
+import java.util.function.Consumer;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.stream.Collectors;
@@ -1178,6 +1180,11 @@
"021-string2", ImmutableList.of("-dontwarn junit.framework.**"),
"082-inline-execute", ImmutableList.of("-dontwarn junit.framework.**"));
+ private static Map<String, Consumer<InternalOptions>> configurations =
+ ImmutableMap.of(
+ // Has a new-instance instruction that attempts to instantiate an interface.
+ "435-new-instance", options -> options.testing.allowTypeErrors = true);
+
private static List<String> failuresToTriage = ImmutableList.of(
// Dex file input into a jar file, not yet supported by the test framework.
"663-odd-dex-size",
@@ -1266,6 +1273,7 @@
private final boolean disableDesugaring;
// Extra keep rules to use when running with R8.
private final List<String> keepRules;
+ private final Consumer<InternalOptions> configuration;
TestSpecification(
String name,
@@ -1285,7 +1293,8 @@
boolean disableUninstantiatedTypeOptimization,
boolean hasMissingClasses,
boolean disableDesugaring,
- List<String> keepRules) {
+ List<String> keepRules,
+ Consumer<InternalOptions> configuration) {
this.name = name;
this.dexTool = dexTool;
this.nativeLibrary = nativeLibrary;
@@ -1304,6 +1313,7 @@
this.hasMissingClasses = hasMissingClasses;
this.disableDesugaring = disableDesugaring;
this.keepRules = keepRules;
+ this.configuration = configuration;
}
TestSpecification(
@@ -1332,7 +1342,8 @@
false,
false,
true, // Disable desugaring for JCTF tests.
- ImmutableList.of());
+ ImmutableList.of(),
+ null);
}
TestSpecification(
@@ -1360,7 +1371,8 @@
false,
false,
true, // Disable desugaring for JCTF tests.
- ImmutableList.of());
+ ImmutableList.of(),
+ null);
}
public File resolveFile(String name) {
@@ -1525,7 +1537,8 @@
requireUninstantiatedTypeOptimizationToBeDisabled.contains(name),
hasMissingClasses.contains(name),
false,
- keepRules.getOrDefault(name, ImmutableList.of())));
+ keepRules.getOrDefault(name, ImmutableList.of()),
+ configurations.get(name)));
}
}
return data;
@@ -1588,13 +1601,15 @@
runArtTest(ToolHelper.getDexVm(), compilerUnderTest);
}
- private static class CompilationOptions {
+ private static class CompilationOptions implements Consumer<InternalOptions> {
+
private final boolean disableInlining;
private final boolean disableClassInlining;
private final boolean disableUninstantiatedTypeOptimization;
private final boolean hasMissingClasses;
private final boolean disableDesugaring;
private final List<String> keepRules;
+ private final Consumer<InternalOptions> configuration;
private CompilationOptions(TestSpecification spec) {
this.disableInlining = spec.disableInlining;
@@ -1603,6 +1618,25 @@
this.hasMissingClasses = spec.hasMissingClasses;
this.disableDesugaring = spec.disableDesugaring;
this.keepRules = spec.keepRules;
+ this.configuration = spec.configuration;
+ }
+
+ @Override
+ public void accept(InternalOptions options) {
+ if (disableInlining) {
+ options.enableInlining = false;
+ }
+ if (disableClassInlining) {
+ options.enableClassInlining = false;
+ }
+ if (disableUninstantiatedTypeOptimization) {
+ options.enableUninstantiatedTypeOptimization = false;
+ }
+ // Some tests actually rely on missing classes for what they test.
+ options.ignoreMissingClasses = hasMissingClasses;
+ if (configuration != null) {
+ configuration.accept(options);
+ }
}
@Override
@@ -1799,20 +1833,10 @@
ToolHelper.runR8(
builder.build(),
options -> {
- if (compilationOptions.disableInlining) {
- options.enableInlining = false;
- }
- if (compilationOptions.disableClassInlining) {
- options.enableClassInlining = false;
- }
- if (compilationOptions.disableUninstantiatedTypeOptimization) {
- options.enableUninstantiatedTypeOptimization = false;
- }
+ compilationOptions.accept(options);
// Make sure we don't depend on this settings.
options.classInliningInstructionLimit = 10000;
options.lineNumberOptimization = LineNumberOptimization.OFF;
- // Some tests actually rely on missing classes for what they test.
- options.ignoreMissingClasses = compilationOptions.hasMissingClasses;
});
break;
}