Support for -whyareyounotinlining in Inliner.canInlineInstanceInitializer()

Bug: 142108662
Change-Id: Ic22afe064cf722b93fe81ed73a3b72d213c35640
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index d23805d..b408240 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.InvokeMethod;
@@ -506,6 +507,8 @@
           if (receiver == thisValue) {
             // The <init>() call of the constructor must be on the same class.
             if (calleeMethodHolder != invokedMethod.holder) {
+              whyAreYouNotInliningReporter
+                  .reportUnsafeConstructorInliningDueToIndirectConstructorCall(initCall);
               return false;
             }
             initCallsOnThis.add(initCall);
@@ -513,9 +516,12 @@
         }
       } else if (instruction.isInstancePut()) {
         // Final fields may not be initialized outside of a constructor in the enclosing class.
-        DexField field = instruction.asInstancePut().getField();
+        InstancePut instancePut = instruction.asInstancePut();
+        DexField field = instancePut.getField();
         DexEncodedField target = appView.appInfo().lookupInstanceTarget(field.holder, field);
         if (target == null || target.accessFlags.isFinal()) {
+          whyAreYouNotInliningReporter.reportUnsafeConstructorInliningDueToFinalFieldAssignment(
+              instancePut);
           return false;
         }
       }
@@ -530,6 +536,8 @@
           Value root = inValue.getAliasedValue();
           if (root == thisValue) {
             inlinee.returnMarkingColor(markingColor);
+            whyAreYouNotInliningReporter.reportUnsafeConstructorInliningDueToUninitializedObjectUse(
+                instruction);
             return false;
           }
         }
@@ -546,6 +554,8 @@
             Value root = inValue.getAliasedValue();
             if (root == thisValue) {
               inlinee.returnMarkingColor(markingColor);
+              whyAreYouNotInliningReporter
+                  .reportUnsafeConstructorInliningDueToUninitializedObjectUse(instruction);
               return false;
             }
           }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index b6ae500..9b8d8a8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -777,8 +777,7 @@
           if (singleTarget.isInstanceInitializer()
               && !strategy.canInlineInstanceInitializer(
                   inlinee.code, whyAreYouNotInliningReporter)) {
-            // TODO(b/142108662): Enable assertion once reporting is complete.
-            // assert whyAreYouNotInliningReporter.verifyReasonHasBeenReported();
+            assert whyAreYouNotInliningReporter.verifyReasonHasBeenReported();
             continue;
           }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotKeepingReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotKeepingReporter.java
index dce73a6..1fac475 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotKeepingReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotKeepingReporter.java
@@ -4,6 +4,10 @@
 
 package com.android.tools.r8.ir.optimize.inliner;
 
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeDirect;
+
 public class NopWhyAreYouNotKeepingReporter extends WhyAreYouNotInliningReporter {
 
   private static final NopWhyAreYouNotKeepingReporter INSTANCE =
@@ -26,6 +30,15 @@
   public void reportUnknownTarget() {}
 
   @Override
+  public void reportUnsafeConstructorInliningDueToFinalFieldAssignment(InstancePut instancePut) {}
+
+  @Override
+  public void reportUnsafeConstructorInliningDueToIndirectConstructorCall(InvokeDirect invoke) {}
+
+  @Override
+  public void reportUnsafeConstructorInliningDueToUninitializedObjectUse(Instruction user) {}
+
+  @Override
   public void reportWillExceedInstructionBudget(int numberOfInstructions, int threshold) {}
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
index 6b5d786..039956c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
@@ -6,6 +6,9 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.Collection;
@@ -48,6 +51,14 @@
 
   abstract void reportUnknownTarget();
 
+  public abstract void reportUnsafeConstructorInliningDueToFinalFieldAssignment(
+      InstancePut instancePut);
+
+  public abstract void reportUnsafeConstructorInliningDueToIndirectConstructorCall(
+      InvokeDirect invoke);
+
+  public abstract void reportUnsafeConstructorInliningDueToUninitializedObjectUse(Instruction user);
+
   public abstract void reportWillExceedInstructionBudget(int numberOfInstructions, int threshold);
 
   public abstract boolean verifyReasonHasBeenReported();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
index c59ddba..6fc1f50 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
@@ -5,6 +5,9 @@
 package com.android.tools.r8.ir.optimize.inliner;
 
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeDirect;
 import java.io.PrintStream;
 
 class WhyAreYouNotInliningReporterImpl extends WhyAreYouNotInliningReporter {
@@ -58,6 +61,29 @@
   }
 
   @Override
+  public void reportUnsafeConstructorInliningDueToFinalFieldAssignment(InstancePut instancePut) {
+    print(
+        "final field `"
+            + instancePut.getField()
+            + "` must be initialized in a constructor of `"
+            + callee.method.holder.toSourceString()
+            + "`.");
+  }
+
+  @Override
+  public void reportUnsafeConstructorInliningDueToIndirectConstructorCall(InvokeDirect invoke) {
+    print(
+        "must invoke a constructor from the class being instantiated (would invoke `"
+            + invoke.getInvokedMethod().toSourceString()
+            + "`).");
+  }
+
+  @Override
+  public void reportUnsafeConstructorInliningDueToUninitializedObjectUse(Instruction user) {
+    print("would lead to use of uninitialized object (user: `" + user.toString() + "`).");
+  }
+
+  @Override
   public void reportWillExceedInstructionBudget(int numberOfInstructions, int threshold) {
     printWithExceededThreshold(
         "would exceed the caller's instruction budget",