Add @NoReturnTypeStrengthening annotation for testing

Change-Id: I18a9d4d79a8d9f73e514875f3af47c1dad87739d
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index 9994ab1..fc17f07 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -25,10 +25,12 @@
   }
 
   private final boolean allowParameterTypeStrengthening;
+  private final boolean allowReturnTypeStrengthening;
 
   private KeepMethodInfo(Builder builder) {
     super(builder);
     this.allowParameterTypeStrengthening = builder.isParameterTypeStrengtheningAllowed();
+    this.allowReturnTypeStrengthening = builder.isReturnTypeStrengtheningAllowed();
   }
 
   // This builder is not private as there are known instances where it is safe to modify keep info
@@ -52,6 +54,16 @@
     return allowParameterTypeStrengthening;
   }
 
+  public boolean isReturnTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && internalIsReturnTypeStrengtheningAllowed();
+  }
+
+  boolean internalIsReturnTypeStrengtheningAllowed() {
+    return allowReturnTypeStrengthening;
+  }
+
   public Joiner joiner() {
     assert !isTop();
     return new Joiner(this);
@@ -74,6 +86,7 @@
   public static class Builder extends KeepInfo.Builder<Builder, KeepMethodInfo> {
 
     private boolean allowParameterTypeStrengthening;
+    private boolean allowReturnTypeStrengthening;
 
     private Builder() {
       super();
@@ -82,6 +95,7 @@
     private Builder(KeepMethodInfo original) {
       super(original);
       allowParameterTypeStrengthening = original.internalIsParameterTypeStrengtheningAllowed();
+      allowReturnTypeStrengthening = original.internalIsReturnTypeStrengtheningAllowed();
     }
 
     public boolean isParameterTypeStrengtheningAllowed() {
@@ -101,6 +115,23 @@
       return setAllowParameterTypeStrengthening(false);
     }
 
+    public boolean isReturnTypeStrengtheningAllowed() {
+      return allowReturnTypeStrengthening;
+    }
+
+    public Builder setAllowReturnTypeStrengthening(boolean allowReturnTypeStrengthening) {
+      this.allowReturnTypeStrengthening = allowReturnTypeStrengthening;
+      return self();
+    }
+
+    public Builder allowReturnTypeStrengthening() {
+      return setAllowReturnTypeStrengthening(true);
+    }
+
+    public Builder disallowReturnTypeStrengthening() {
+      return setAllowReturnTypeStrengthening(false);
+    }
+
     @Override
     public Builder self() {
       return this;
@@ -125,7 +156,8 @@
     boolean internalIsEqualTo(KeepMethodInfo other) {
       return super.internalIsEqualTo(other)
           && isParameterTypeStrengtheningAllowed()
-              == other.internalIsParameterTypeStrengtheningAllowed();
+              == other.internalIsParameterTypeStrengtheningAllowed()
+          && isReturnTypeStrengtheningAllowed() == other.internalIsReturnTypeStrengtheningAllowed();
     }
 
     @Override
@@ -135,12 +167,12 @@
 
     @Override
     public Builder makeTop() {
-      return super.makeTop().disallowParameterTypeStrengthening();
+      return super.makeTop().disallowParameterTypeStrengthening().disallowReturnTypeStrengthening();
     }
 
     @Override
     public Builder makeBottom() {
-      return super.makeBottom().allowParameterTypeStrengthening();
+      return super.makeBottom().allowParameterTypeStrengthening().allowReturnTypeStrengthening();
     }
   }
 
@@ -150,11 +182,20 @@
       super(info.builder());
     }
 
+    public Joiner disallowInlining() {
+      return self();
+    }
+
     public Joiner disallowParameterTypeStrengthening() {
       builder.disallowParameterTypeStrengthening();
       return self();
     }
 
+    public Joiner disallowReturnTypeStrengthening() {
+      builder.disallowReturnTypeStrengthening();
+      return self();
+    }
+
     @Override
     public Joiner asMethodJoiner() {
       return this;
@@ -166,7 +207,10 @@
       return super.merge(joiner)
           .applyIf(
               !joiner.builder.isParameterTypeStrengtheningAllowed(),
-              Joiner::disallowParameterTypeStrengthening);
+              Joiner::disallowParameterTypeStrengthening)
+          .applyIf(
+              !joiner.builder.isReturnTypeStrengtheningAllowed(),
+              Joiner::disallowReturnTypeStrengthening);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/NoOptimizationBaseRule.java b/src/main/java/com/android/tools/r8/shaking/NoOptimizationBaseRule.java
new file mode 100644
index 0000000..a397a7e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoOptimizationBaseRule.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public abstract class NoOptimizationBaseRule<R extends NoOptimizationBaseRule<R>>
+    extends ProguardConfigurationRule {
+
+  public abstract static class Builder<R extends NoOptimizationBaseRule<R>, B extends Builder<R, B>>
+      extends ProguardConfigurationRule.Builder<R, B> {
+
+    Builder() {
+      super();
+    }
+  }
+
+  NoOptimizationBaseRule(
+      Origin origin,
+      Position position,
+      String source,
+      List<ProguardTypeMatcher> classAnnotations,
+      ProguardAccessFlags classAccessFlags,
+      ProguardAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      ProguardClassNameList classNames,
+      List<ProguardTypeMatcher> inheritanceAnnotations,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      List<ProguardMemberRule> memberRules) {
+    super(
+        origin,
+        position,
+        source,
+        classAnnotations,
+        classAccessFlags,
+        negatedClassAccessFlags,
+        classTypeNegated,
+        classType,
+        classNames,
+        inheritanceAnnotations,
+        inheritanceClassName,
+        inheritanceIsExtends,
+        memberRules);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/NoParameterTypeStrengtheningRule.java b/src/main/java/com/android/tools/r8/shaking/NoParameterTypeStrengtheningRule.java
index 94cec37..e0b6594 100644
--- a/src/main/java/com/android/tools/r8/shaking/NoParameterTypeStrengtheningRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/NoParameterTypeStrengtheningRule.java
@@ -8,14 +8,15 @@
 import com.android.tools.r8.position.Position;
 import java.util.List;
 
-public class NoParameterTypeStrengtheningRule extends ProguardConfigurationRule {
+public class NoParameterTypeStrengtheningRule
+    extends NoOptimizationBaseRule<NoParameterTypeStrengtheningRule> {
 
   public static final String RULE_NAME = "noparametertypestrengthening";
 
   public static class Builder
-      extends ProguardConfigurationRule.Builder<NoParameterTypeStrengtheningRule, Builder> {
+      extends NoOptimizationBaseRule.Builder<NoParameterTypeStrengtheningRule, Builder> {
 
-    private Builder() {
+    Builder() {
       super();
     }
 
@@ -43,7 +44,7 @@
     }
   }
 
-  private NoParameterTypeStrengtheningRule(
+  NoParameterTypeStrengtheningRule(
       Origin origin,
       Position position,
       String source,
diff --git a/src/main/java/com/android/tools/r8/shaking/NoReturnTypeStrengtheningRule.java b/src/main/java/com/android/tools/r8/shaking/NoReturnTypeStrengtheningRule.java
new file mode 100644
index 0000000..21c6371
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoReturnTypeStrengtheningRule.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoReturnTypeStrengtheningRule
+    extends NoOptimizationBaseRule<NoReturnTypeStrengtheningRule> {
+
+  public static final String RULE_NAME = "noreturntypestrengthening";
+
+  public static class Builder
+      extends NoOptimizationBaseRule.Builder<NoReturnTypeStrengtheningRule, Builder> {
+
+    Builder() {
+      super();
+    }
+
+    @Override
+    public NoReturnTypeStrengtheningRule.Builder self() {
+      return this;
+    }
+
+    @Override
+    public NoReturnTypeStrengtheningRule build() {
+      return new NoReturnTypeStrengtheningRule(
+          origin,
+          getPosition(),
+          source,
+          buildClassAnnotations(),
+          classAccessFlags,
+          negatedClassAccessFlags,
+          classTypeNegated,
+          classType,
+          classNames,
+          buildInheritanceAnnotations(),
+          inheritanceClassName,
+          inheritanceIsExtends,
+          memberRules);
+    }
+  }
+
+  NoReturnTypeStrengtheningRule(
+      Origin origin,
+      Position position,
+      String source,
+      List<ProguardTypeMatcher> classAnnotations,
+      ProguardAccessFlags classAccessFlags,
+      ProguardAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      ProguardClassNameList classNames,
+      List<ProguardTypeMatcher> inheritanceAnnotations,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      List<ProguardMemberRule> memberRules) {
+    super(
+        origin,
+        position,
+        source,
+        classAnnotations,
+        classAccessFlags,
+        negatedClassAccessFlags,
+        classTypeNegated,
+        classType,
+        classNames,
+        inheritanceAnnotations,
+        inheritanceClassName,
+        inheritanceIsExtends,
+        memberRules);
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  String typeString() {
+    return RULE_NAME;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 13cabf2..e4774ce 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -501,7 +501,14 @@
           return true;
         }
         if (acceptString(NoParameterTypeStrengtheningRule.RULE_NAME)) {
-          ProguardConfigurationRule rule = parseNoParameterTypeStrengtheningRule(optionStart);
+          ProguardConfigurationRule rule =
+              parseNoOptimizationRule(optionStart, NoParameterTypeStrengtheningRule.builder());
+          configurationBuilder.addRule(rule);
+          return true;
+        }
+        if (acceptString(NoReturnTypeStrengtheningRule.RULE_NAME)) {
+          ProguardConfigurationRule rule =
+              parseNoOptimizationRule(optionStart, NoReturnTypeStrengtheningRule.builder());
           configurationBuilder.addRule(rule);
           return true;
         }
@@ -833,15 +840,14 @@
       return keepRuleBuilder.build();
     }
 
-    private NoParameterTypeStrengtheningRule parseNoParameterTypeStrengtheningRule(Position start)
-        throws ProguardRuleParserException {
-      NoParameterTypeStrengtheningRule.Builder keepRuleBuilder =
-          NoParameterTypeStrengtheningRule.builder().setOrigin(origin).setStart(start);
-      parseClassSpec(keepRuleBuilder, false);
+    private <R extends NoOptimizationBaseRule<R>, B extends NoOptimizationBaseRule.Builder<R, B>>
+        R parseNoOptimizationRule(Position start, B builder) throws ProguardRuleParserException {
+      builder.setOrigin(origin).setStart(start);
+      parseClassSpec(builder, false);
       Position end = getPosition();
-      keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
-      keepRuleBuilder.setEnd(end);
-      return keepRuleBuilder.build();
+      builder.setSource(getSourceSnippet(contents, start, end));
+      builder.setEnd(end);
+      return builder.build();
     }
 
     private MemberValuePropagationRule parseMemberValuePropagationRule(
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 80756a1..2368ce8 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -268,6 +268,7 @@
       } else if (rule instanceof InlineRule
           || rule instanceof ConstantArgumentRule
           || rule instanceof NoParameterTypeStrengtheningRule
+          || rule instanceof NoReturnTypeStrengtheningRule
           || rule instanceof UnusedArgumentRule
           || rule instanceof ReprocessMethodRule
           || rule instanceof WhyAreYouNotInliningRule) {
@@ -1251,6 +1252,13 @@
             .asMethodJoiner()
             .disallowParameterTypeStrengthening();
         context.markAsUsed();
+      } else if (context instanceof NoReturnTypeStrengtheningRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowReturnTypeStrengthening();
+        context.markAsUsed();
       } else if (context instanceof MemberValuePropagationRule) {
         switch (((MemberValuePropagationRule) context).getType()) {
           case NEVER:
diff --git a/src/test/java/com/android/tools/r8/NoReturnTypeStrengthening.java b/src/test/java/com/android/tools/r8/NoReturnTypeStrengthening.java
new file mode 100644
index 0000000..faf89d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoReturnTypeStrengthening.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface NoReturnTypeStrengthening {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 1a4706e..6b68ca2 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.shaking.NoFieldTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoHorizontalClassMergingRule;
 import com.android.tools.r8.shaking.NoParameterTypeStrengtheningRule;
+import com.android.tools.r8.shaking.NoReturnTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoUnusedInterfaceRemovalRule;
 import com.android.tools.r8.shaking.NoVerticalClassMergingRule;
 import com.android.tools.r8.shaking.ProguardConfiguration;
@@ -514,6 +515,12 @@
             NoParameterTypeStrengtheningRule.RULE_NAME, NoParameterTypeStrengthening.class);
   }
 
+  public T enableNoReturnTypeStrengtheningAnnotations() {
+    return addNoReturnTypeStrengtheningAnnotation()
+        .addInternalMatchAnnotationOnMethodRule(
+            NoReturnTypeStrengtheningRule.RULE_NAME, NoReturnTypeStrengthening.class);
+  }
+
   public T enableNoUnusedInterfaceRemovalAnnotations() {
     return addNoUnusedInterfaceRemovalAnnotations()
         .addInternalMatchInterfaceRule(
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 9ed8247..d4b3ecf 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -470,6 +470,10 @@
     return addTestingAnnotation(NoParameterTypeStrengthening.class);
   }
 
+  public final T addNoReturnTypeStrengtheningAnnotation() {
+    return addTestingAnnotation(NoReturnTypeStrengthening.class);
+  }
+
   public final T addNoUnusedInterfaceRemovalAnnotations() {
     return addTestingAnnotation(NoUnusedInterfaceRemoval.class);
   }