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;
         }