Make no assumptions about the type of assumed return field values

Bug: 233828966
Bug: 150836439
Change-Id: I6e580e418dd03cb6682e59e024b1167272cfc7ea
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5d07a1c..d70426f 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -79,6 +79,7 @@
 import com.android.tools.r8.shaking.AbstractMethodRemover;
 import com.android.tools.r8.shaking.AnnotationRemover;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.AssumeInfoCollection;
 import com.android.tools.r8.shaking.ClassInitFieldSynthesizer;
 import com.android.tools.r8.shaking.DefaultTreePrunerConfiguration;
 import com.android.tools.r8.shaking.DiscardedChecker;
@@ -338,6 +339,7 @@
                     options.itemFactory, options.getMinApiLevel()));
           }
         }
+        AssumeInfoCollection.Builder assumeInfoCollectionBuilder = AssumeInfoCollection.builder();
         SubtypingInfo subtypingInfo = SubtypingInfo.create(appView);
         appView.setRootSet(
             RootSet.builder(
@@ -345,7 +347,9 @@
                     subtypingInfo,
                     Iterables.concat(
                         options.getProguardConfiguration().getRules(), synthesizedProguardRules))
+                .setAssumeInfoCollectionBuilder(assumeInfoCollectionBuilder)
                 .build(executorService));
+        appView.setAssumeInfoCollection(assumeInfoCollectionBuilder.build());
 
         // Compute the main dex rootset that will be the base of first and final main dex tracing
         // before building a new appview with only live classes (and invalidating subtypingInfo).
diff --git a/src/main/java/com/android/tools/r8/errors/AssumeValuesMissingStaticFieldDiagnostic.java b/src/main/java/com/android/tools/r8/errors/AssumeValuesMissingStaticFieldDiagnostic.java
new file mode 100644
index 0000000..38f6a46
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/AssumeValuesMissingStaticFieldDiagnostic.java
@@ -0,0 +1,78 @@
+// 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.errors;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+
+@Keep
+public class AssumeValuesMissingStaticFieldDiagnostic implements Diagnostic {
+
+  private final DexType fieldHolder;
+  private final DexString fieldName;
+  private final Origin origin;
+  private final Position position;
+
+  private AssumeValuesMissingStaticFieldDiagnostic(
+      DexType fieldHolder, DexString fieldName, Origin origin, Position position) {
+    this.fieldHolder = fieldHolder;
+    this.fieldName = fieldName;
+    this.origin = origin;
+    this.position = position;
+  }
+
+  @Override
+  public Origin getOrigin() {
+    return origin;
+  }
+
+  @Override
+  public Position getPosition() {
+    return position;
+  }
+
+  @Override
+  public String getDiagnosticMessage() {
+    return "The field "
+        + fieldHolder.getTypeName()
+        + "."
+        + fieldName
+        + " is used as the return value in an -assumenosideeffects or -assumevalues rule"
+        + ", but no such static field exists.";
+  }
+
+  public static class Builder {
+
+    private DexType fieldHolder;
+    private DexString fieldName;
+    private Origin origin;
+    private Position position;
+
+    public Builder() {}
+
+    public Builder setField(DexType fieldHolder, DexString fieldName) {
+      this.fieldHolder = fieldHolder;
+      this.fieldName = fieldName;
+      return this;
+    }
+
+    public Builder setOrigin(Origin origin) {
+      this.origin = origin;
+      return this;
+    }
+
+    public Builder setPosition(Position position) {
+      this.position = position;
+      return this;
+    }
+
+    public AssumeValuesMissingStaticFieldDiagnostic build() {
+      return new AssumeValuesMissingStaticFieldDiagnostic(fieldHolder, fieldName, origin, position);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 6dd2ee7..ec80f8f 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -35,6 +35,7 @@
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
 import com.android.tools.r8.optimize.interfaces.collection.OpenClosedInterfacesCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.AssumeInfoCollection;
 import com.android.tools.r8.shaking.KeepClassInfo;
 import com.android.tools.r8.shaking.KeepFieldInfo;
 import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -74,6 +75,7 @@
   private T appInfo;
   private AppInfoWithClassHierarchy appInfoForDesugaring;
   private AppServices appServices;
+  private AssumeInfoCollection assumeInfoCollection = AssumeInfoCollection.builder().build();
   private final DontWarnConfiguration dontWarnConfiguration;
   private final WholeProgramOptimizations wholeProgramOptimizations;
   private GraphLens codeLens = GraphLens.getIdentityLens();
@@ -311,6 +313,14 @@
     this.appServices = appServices;
   }
 
+  public AssumeInfoCollection getAssumeInfoCollection() {
+    return assumeInfoCollection;
+  }
+
+  public void setAssumeInfoCollection(AssumeInfoCollection assumeInfoCollection) {
+    this.assumeInfoCollection = assumeInfoCollection;
+  }
+
   public DontWarnConfiguration getDontWarnConfiguration() {
     return dontWarnConfiguration;
   }
@@ -760,6 +770,7 @@
     if (appServices() != null) {
       setAppServices(appServices().prunedCopy(prunedItems));
     }
+    setAssumeInfoCollection(getAssumeInfoCollection().withoutPrunedItems(prunedItems));
     if (hasProguardCompatibilityActions()) {
       setProguardCompatibilityActions(
           getProguardCompatibilityActions().withoutPrunedItems(prunedItems));
@@ -857,6 +868,8 @@
                 .setAppInfo(appView.appInfoWithLiveness().rewrittenWithLens(application, lens));
           }
           appView.setAppServices(appView.appServices().rewrittenWithLens(lens));
+          appView.setAssumeInfoCollection(
+              appView.getAssumeInfoCollection().rewrittenWithLens(appView, lens));
           if (appView.hasInitClassLens()) {
             appView.setInitClassLens(appView.initClassLens().rewrittenWithLens(lens));
           }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index a377b46..6f5e604 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -539,17 +539,26 @@
     return lookupTarget(instanceFields, field);
   }
 
-  public DexField lookupUniqueInstanceFieldWithName(DexString name) {
-    DexField field = null;
-    for (DexEncodedField encodedField : instanceFields()) {
-      if (encodedField.getReference().name == name) {
-        if (field != null) {
+  public DexEncodedField lookupUniqueInstanceFieldWithName(DexString name) {
+    return internalLookupUniqueFieldThatMatches(field -> field.getName() == name, instanceFields());
+  }
+
+  public DexEncodedField lookupUniqueStaticFieldWithName(DexString name) {
+    return internalLookupUniqueFieldThatMatches(field -> field.getName() == name, staticFields());
+  }
+
+  private static DexEncodedField internalLookupUniqueFieldThatMatches(
+      Predicate<DexEncodedField> predicate, List<DexEncodedField> fields) {
+    DexEncodedField result = null;
+    for (DexEncodedField field : fields) {
+      if (predicate.test(field)) {
+        if (result != null) {
           return null;
         }
-        field = encodedField.getReference();
+        result = field;
       }
     }
-    return field;
+    return result;
   }
 
   /** Find method in this class matching {@param method}. */
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
index ab305a3..a6e27f5 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
@@ -60,7 +60,7 @@
         AppView<AppInfoWithLiveness> appView, DexEncodedMethod method) {
       return new MethodCharacteristics(
           method,
-          appView.appInfo().isAssumeNoSideEffectsMethod(method.getReference()),
+          appView.getAssumeInfoCollection().isSideEffectFree(method.getReference()),
           appView.appInfo().getMainDexInfo().isTracedMethodRoot(method.getReference()));
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
index eb22b19..fbe3ff5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
@@ -202,10 +202,10 @@
         return new ProtoTypeObject(constClass.getValue());
       } else if (definition.isConstString()) {
         ConstString constString = definition.asConstString();
-        DexField field =
+        DexEncodedField field =
             context.getHolder().lookupUniqueInstanceFieldWithName(constString.getValue());
         if (field != null) {
-          return new LiveProtoFieldObject(field);
+          return new LiveProtoFieldObject(field.getReference());
         }
         // This const-string refers to a field that no longer exists. In this case, we create a
         // special dead-object instead of failing with an InvalidRawMessageInfoException below.
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
index bc1a554..bab5e75 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
@@ -31,6 +31,11 @@
     return knownArrayLengthStates.computeIfAbsent(length, KnownLengthArrayState::new);
   }
 
+  public NumberFromIntervalValue createNumberFromIntervalValue(
+      long minInclusive, long maxInclusive) {
+    return new NumberFromIntervalValue(minInclusive, maxInclusive);
+  }
+
   public SingleFieldValue createSingleFieldValue(DexField field, ObjectState state) {
     return state.isEmpty()
         ? new SingleStatelessFieldValue(field)
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
index 3238680..94d2d59 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
@@ -31,6 +31,14 @@
     return maxInclusive - minInclusive + 1;
   }
 
+  public long getMinInclusive() {
+    return minInclusive;
+  }
+
+  public long getMaxInclusive() {
+    return maxInclusive;
+  }
+
   @Override
   public boolean isNumberFromIntervalValue() {
     return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 552ebf3..3551561 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
@@ -83,9 +84,9 @@
     }
     SingleFieldResolutionResult<?> singleFieldResolutionResult =
         resolutionResult.asSingleFieldResolutionResult();
-    DexEncodedField resolvedField = singleFieldResolutionResult.getResolvedField();
+    DexClassAndField resolvedField = singleFieldResolutionResult.getResolutionPair();
     // Check if the instruction may fail with an IncompatibleClassChangeError.
-    if (resolvedField.isStatic() != isStaticFieldInstruction()) {
+    if (resolvedField.getAccessFlags().isStatic() != isStaticFieldInstruction()) {
       return true;
     }
     // Check if the resolution target is accessible.
@@ -115,11 +116,8 @@
         isStaticFieldInstruction() && !assumption.canAssumeClassIsAlreadyInitialized();
     if (mayTriggerClassInitialization) {
       // Only check for <clinit> side effects if there is no -assumenosideeffects rule.
-      if (appView.appInfo().hasLiveness()) {
-        AppInfoWithLiveness appInfoWithLiveness = appView.appInfo().withLiveness();
-        if (appInfoWithLiveness.noSideEffects.containsKey(resolvedField.getReference())) {
-          return false;
-        }
+      if (appView.getAssumeInfoCollection().isSideEffectFree(resolvedField)) {
+        return false;
       }
       // May trigger <clinit> that may have side effects.
       if (field.holder.classInitializationMayHaveSideEffectsInContext(appView, context)) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index 668d515..f5e7b03 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -216,12 +216,10 @@
     }
 
     DexClassAndMethod resolvedMethod = resolutionResult.getResolutionPair();
-    if (appView.hasLiveness()) {
-      if (appView.appInfoWithLiveness().isAssumeNoSideEffectsMethod(getInvokedMethod())
-          || appView.appInfoWithLiveness().isAssumeNoSideEffectsMethod(resolvedMethod)) {
+    if (appView.getAssumeInfoCollection().isSideEffectFree(getInvokedMethod())
+        || appView.getAssumeInfoCollection().isSideEffectFree(resolvedMethod)) {
         return false;
       }
-    }
 
     // Find the target and check if the invoke may have side effects.
     DexClassAndMethod singleTarget = lookupSingleTarget(appView, context);
@@ -237,10 +235,8 @@
     }
 
     // Verify that the target method does not have side-effects.
-    if (appView.hasLiveness()) {
-      if (appView.appInfoWithLiveness().isAssumeNoSideEffectsMethod(singleTarget)) {
-        return false;
-      }
+    if (appView.getAssumeInfoCollection().isSideEffectFree(singleTarget)) {
+      return false;
     }
 
     DexEncodedMethod singleTargetDefinition = singleTarget.getDefinition();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 8f47b31..9e6a918 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -220,7 +220,7 @@
     }
 
     // Verify that the target method does not have side-effects.
-    if (appViewWithLiveness.appInfo().isAssumeNoSideEffectsMethod(singleTarget)) {
+    if (appView.getAssumeInfoCollection().isSideEffectFree(singleTarget)) {
       return false;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index bb83120..54710df 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.NumberFromIntervalValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
 import com.android.tools.r8.ir.regalloc.LiveIntervals;
@@ -940,8 +941,8 @@
     return isThis;
   }
 
-  public void setValueRange(LongInterval range) {
-    valueRange = range;
+  public void setValueRange(NumberFromIntervalValue range) {
+    valueRange = new LongInterval(range.getMinInclusive(), range.getMaxInclusive());
   }
 
   public boolean hasValueRange() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index df9d508..ed206d4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -240,9 +240,7 @@
     if (invoke.hasUsedOutValue() && invoke.getOutType().isReferenceType()) {
       AssumeInfo assumeInfo =
           AssumeInfoLookup.lookupAssumeInfo(appView, resolutionResult, singleTarget);
-      if (assumeInfo != null
-          && assumeInfo.hasReturnInfo()
-          && assumeInfo.getReturnInfo().isNonNull()) {
+      if (assumeInfo.getAssumeType().getNullability().isDefinitelyNotNull()) {
         assumedValuesBuilder.addNonNullValueKnownToDominateAllUsers(invoke, invoke.outValue());
       }
     }
@@ -294,10 +292,8 @@
     DexClassAndField field = resolutionResult.getResolutionPair();
 
     if (field.getType().isReferenceType()) {
-      AssumeInfo assumeInfo = AssumeInfoLookup.lookupAssumeInfo(appView, field);
-      if (assumeInfo != null
-          && assumeInfo.hasReturnInfo()
-          && assumeInfo.getReturnInfo().isNonNull()) {
+      AssumeInfo assumeInfo = appView.getAssumeInfoCollection().get(field);
+      if (assumeInfo.getAssumeType().getNullability().isDefinitelyNotNull()) {
         assumedValuesBuilder.addNonNullValueKnownToDominateAllUsers(fieldGet, fieldGet.outValue());
       }
     }
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 107bd20..75c275f 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
@@ -43,6 +43,7 @@
 import com.android.tools.r8.ir.optimize.inliner.InliningReasonStrategy;
 import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.AssumeInfoCollection;
 import com.android.tools.r8.shaking.MainDexInfo;
 import com.android.tools.r8.synthesis.SyntheticItems;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -339,16 +340,16 @@
       SingleResolutionResult<?> resolutionResult,
       ProgramMethod singleTarget,
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
-    AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod singleTargetReference = singleTarget.getReference();
     if (!appView.getKeepInfo(singleTarget).isInliningAllowed(appView.options())) {
       whyAreYouNotInliningReporter.reportPinned();
       return true;
     }
 
-    if (appInfo.noSideEffects.containsKey(invoke.getInvokedMethod())
-        || appInfo.noSideEffects.containsKey(resolutionResult.getResolvedMethod().getReference())
-        || appInfo.noSideEffects.containsKey(singleTargetReference)) {
+    AssumeInfoCollection assumeInfoCollection = appView.getAssumeInfoCollection();
+    if (assumeInfoCollection.isSideEffectFree(invoke.getInvokedMethod())
+        || assumeInfoCollection.isSideEffectFree(resolutionResult.getResolutionPair())
+        || assumeInfoCollection.isSideEffectFree(singleTargetReference)) {
       return !singleTarget.getDefinition().getOptimizationInfo().forceInline();
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index d572d2f..d30ee64 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -3,10 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize;
 
-import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
 import static com.google.common.base.Predicates.alwaysTrue;
 
-import com.android.tools.r8.graph.AccessControl;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexClassAndMethod;
@@ -41,10 +39,7 @@
 import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo;
 import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfoLookup;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.ProguardMemberRuleReturnValue;
 import com.android.tools.r8.utils.IteratorUtils;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Sets;
 import java.util.ListIterator;
 import java.util.Set;
@@ -55,19 +50,13 @@
   private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
 
   private final AppView<AppInfoWithLiveness> appView;
-  private final Reporter reporter;
-
-  // Fields for which we have reported warnings to due Proguard configuration rules.
-  private final Set<DexField> warnedFields = Sets.newIdentityHashSet();
 
   public MemberValuePropagation(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
-    this.reporter = appView.options().reporter;
   }
 
   private void rewriteArrayGet(
       IRCode code,
-      ProgramMethod context,
       Set<Value> affectedValues,
       ListIterator<BasicBlock> blocks,
       InstructionListIterator iterator,
@@ -126,93 +115,36 @@
     if (field.isProgramField()) {
       return appView.appInfo().mayPropagateValueFor(appView, field.getReference());
     }
-    return appView.appInfo().assumedValues.containsKey(field.getReference())
-        || appView.appInfo().noSideEffects.containsKey(field.getReference());
+    return appView.getAssumeInfoCollection().contains(field);
   }
 
   private boolean mayPropagateValueFor(DexClassAndMethod method) {
     if (method.isProgramMethod()) {
       return appView.appInfo().mayPropagateValueFor(appView, method.getReference());
     }
-    return appView.appInfo().assumedValues.containsKey(method.getReference())
-        || appView.appInfo().noSideEffects.containsKey(method.getReference());
+    return appView.getAssumeInfoCollection().contains(method);
   }
 
   private Instruction createReplacementFromAssumeInfo(
       AssumeInfo assumeInfo, IRCode code, Instruction instruction) {
-    if (!assumeInfo.hasReturnInfo()) {
+    if (assumeInfo.getAssumeValue().isUnknown()) {
       return null;
     }
 
-    ProguardMemberRuleReturnValue returnValueRule = assumeInfo.getReturnInfo();
-
-    // Check if this value can be assumed constant.
-    if (returnValueRule.isSingleValue()) {
-      if (instruction.getOutType().isReferenceType()) {
-        if (returnValueRule.getSingleValue() == 0) {
-          return appView
-              .abstractValueFactory()
-              .createNullValue()
-              .createMaterializingInstruction(appView, code, instruction);
-        }
-        return null;
+    AbstractValue assumeValue = assumeInfo.getAssumeValue();
+    if (assumeValue.isSingleValue()) {
+      SingleValue singleValue = assumeValue.asSingleValue();
+      if (singleValue.isMaterializableInContext(appView, code.context())) {
+        return singleValue.createMaterializingInstruction(appView, code, instruction);
       }
-      return appView.abstractValueFactory()
-          .createSingleNumberValue(returnValueRule.getSingleValue())
-          .createMaterializingInstruction(appView, code, instruction);
-    }
-
-    if (returnValueRule.isField()) {
-      DexField field = returnValueRule.getField();
-      assert instruction.getOutType() == TypeElement.fromDexType(field.type, maybeNull(), appView);
-
-      DexClassAndField staticField = appView.appInfo().lookupStaticTarget(field);
-      if (staticField == null) {
-        if (warnedFields.add(field)) {
-          reporter.warning(
-              new StringDiagnostic(
-                  "Field `"
-                      + field.toSourceString()
-                      + "` is used in an -assumevalues rule but does not exist.",
-                  code.origin));
-        }
-        return null;
-      }
-
-      if (AccessControl.isMemberAccessible(
-              staticField, staticField.getHolder(), code.context(), appView)
-          .isTrue()) {
-        return StaticGet.builder()
-            .setField(field)
-            .setFreshOutValue(code, field.getTypeElement(appView), instruction.getLocalInfo())
-            .build();
-      }
-
-      Instruction replacement =
-          staticField
-              .getDefinition()
-              .valueAsConstInstruction(code, instruction.getLocalInfo(), appView);
-      if (replacement == null) {
-        reporter.warning(
-            new StringDiagnostic(
-                "Unable to apply the rule `"
-                    + returnValueRule.toString()
-                    + "`: Could not determine the value of field `"
-                    + field.toSourceString()
-                    + "`",
-                code.origin));
-        return null;
-      }
-      return replacement;
     }
 
     return null;
   }
 
   private void setValueRangeFromAssumeInfo(AssumeInfo assumeInfo, Value value) {
-    if (assumeInfo.hasReturnInfo() && assumeInfo.getReturnInfo().isValueRange()) {
-      assert !assumeInfo.getReturnInfo().isSingleValue();
-      value.setValueRange(assumeInfo.getReturnInfo().getValueRange());
+    if (assumeInfo.getAssumeValue().isNumberFromIntervalValue()) {
+      value.setValueRange(assumeInfo.getAssumeValue().asNumberFromIntervalValue());
     }
   }
 
@@ -232,10 +164,9 @@
       return false;
     }
     affectedValues.addAll(current.outValue().affectedValues());
-    if (assumeInfo.isAssumeNoSideEffects()) {
+    if (assumeInfo.isSideEffectFree()) {
       iterator.replaceCurrentInstruction(replacement);
     } else {
-      assert assumeInfo.isAssumeValues();
       BasicBlock block = current.getBlock();
       Position position = current.getPosition();
       if (current.hasOutValue()) {
@@ -397,7 +328,7 @@
     }
 
     // Check if there is a Proguard configuration rule that specifies the value of the field.
-    AssumeInfo lookup = AssumeInfoLookup.lookupAssumeInfo(appView, target);
+    AssumeInfo lookup = appView.getAssumeInfoCollection().get(target);
     if (lookup != null
         && applyAssumeInfoIfPossible(code, affectedValues, blocks, iterator, current, lookup)) {
       return;
@@ -539,8 +470,7 @@
       while (iterator.hasNext()) {
         Instruction current = iterator.next();
         if (current.isArrayGet()) {
-          rewriteArrayGet(
-              code, context, affectedValues, blockIterator, iterator, current.asArrayGet());
+          rewriteArrayGet(code, affectedValues, blockIterator, iterator, current.asArrayGet());
         } else if (current.isInvokeMethod()) {
           rewriteInvokeMethodWithConstantValues(
               code, context, affectedValues, blockIterator, iterator, current.asInvokeMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
index bd7edf7..0c65d04 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
@@ -37,14 +37,14 @@
       return false;
     }
 
+    // Check if there is an -assumenosideeffects rule for the toString() method.
+    if (appView.getAssumeInfoCollection().isSideEffectFree(toStringMethodReference)) {
+      return false;
+    }
+
     if (appView.appInfo().hasLiveness()) {
       AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
 
-      // Check if there is an -assumenosideeffects rule for the toString() method.
-      if (appInfo.isAssumeNoSideEffectsMethod(toStringMethodReference)) {
-        return false;
-      }
-
       // Check if this is a program class with a toString() method that does not have side effects.
       DexClass clazz = appInfo.definitionFor(classType);
       if (clazz != null && clazz.isEffectivelyFinal(appView)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfo.java
index a704abb..21d8d33 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfo.java
@@ -4,64 +4,188 @@
 
 package com.android.tools.r8.ir.optimize.membervaluepropagation.assume;
 
-import com.android.tools.r8.shaking.ProguardMemberRule;
-import com.android.tools.r8.shaking.ProguardMemberRuleReturnValue;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
+import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
+import java.util.Objects;
 
 public class AssumeInfo {
 
-  public enum AssumeType {
-    ASSUME_NO_SIDE_EFFECTS,
-    ASSUME_VALUES;
+  private static final AssumeInfo EMPTY =
+      new AssumeInfo(DynamicType.unknown(), AbstractValue.unknown(), false);
 
-    AssumeType meet(AssumeType type) {
-      return this == ASSUME_NO_SIDE_EFFECTS || type == ASSUME_NO_SIDE_EFFECTS
-          ? ASSUME_NO_SIDE_EFFECTS
-          : ASSUME_VALUES;
+  private final DynamicType assumeType;
+  private final AbstractValue assumeValue;
+  private final boolean isSideEffectFree;
+
+  private AssumeInfo(DynamicType assumeType, AbstractValue assumeValue, boolean isSideEffectFree) {
+    this.assumeType = assumeType;
+    this.assumeValue = assumeValue;
+    this.isSideEffectFree = isSideEffectFree;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static AssumeInfo create(
+      DynamicType assumeType, AbstractValue assumeValue, boolean isSideEffectFree) {
+    return assumeType.isUnknown() && assumeValue.isUnknown() && !isSideEffectFree
+        ? empty()
+        : new AssumeInfo(assumeType, assumeValue, isSideEffectFree);
+  }
+
+  public static AssumeInfo empty() {
+    return EMPTY;
+  }
+
+  public DynamicType getAssumeType() {
+    return assumeType;
+  }
+
+  public AbstractValue getAssumeValue() {
+    return assumeValue;
+  }
+
+  public boolean isEmpty() {
+    if (this == empty()) {
+      return true;
     }
+    assert !assumeType.isUnknown() || !assumeValue.isUnknown() || isSideEffectFree;
+    return false;
   }
 
-  private final AssumeType type;
-  private final ProguardMemberRule rule;
-
-  public AssumeInfo(AssumeType type, ProguardMemberRule rule) {
-    this.type = type;
-    this.rule = rule;
+  public boolean isSideEffectFree() {
+    return isSideEffectFree;
   }
 
-  public boolean hasReturnInfo() {
-    return rule.hasReturnValue();
+  public AssumeInfo meet(AssumeInfo other) {
+    DynamicType meetType = internalMeetType(assumeType, other.assumeType);
+    AbstractValue meetValue = internalMeetValue(assumeValue, other.assumeValue);
+    boolean meetIsSideEffectFree =
+        internalMeetIsSideEffectFree(isSideEffectFree, other.isSideEffectFree);
+    return AssumeInfo.create(meetType, meetValue, meetIsSideEffectFree);
   }
 
-  public ProguardMemberRuleReturnValue getReturnInfo() {
-    return rule.getReturnValue();
+  private static DynamicType internalMeetType(DynamicType type, DynamicType other) {
+    if (type.equals(other)) {
+      return type;
+    }
+    if (type.isUnknown()) {
+      return other;
+    }
+    if (other.isUnknown()) {
+      return type;
+    }
+    return DynamicType.unknown();
   }
 
-  public boolean isAssumeNoSideEffects() {
-    return type == AssumeType.ASSUME_NO_SIDE_EFFECTS;
+  private static AbstractValue internalMeetValue(AbstractValue value, AbstractValue other) {
+    if (value.equals(other)) {
+      return value;
+    }
+    if (value.isUnknown()) {
+      return other;
+    }
+    if (other.isUnknown()) {
+      return value;
+    }
+    return AbstractValue.unknown();
   }
 
-  public boolean isAssumeValues() {
-    return type == AssumeType.ASSUME_VALUES;
+  private static boolean internalMeetIsSideEffectFree(
+      boolean isSideEffectFree, boolean otherIsSideEffectFree) {
+    return isSideEffectFree || otherIsSideEffectFree;
   }
 
-  public AssumeInfo meet(AssumeInfo lookup) {
-    return new AssumeInfo(type.meet(lookup.type), rule.hasReturnValue() ? rule : lookup.rule);
+  public AssumeInfo rewrittenWithLens(AppView<?> appView, GraphLens graphLens) {
+    // Verify that there is no need to rewrite the assumed type.
+    assert assumeType.isNotNullType() || assumeType.isUnknown();
+    // If the assumed value is a static field, then rewrite it.
+    if (assumeValue.isSingleFieldValue()) {
+      DexField field = assumeValue.asSingleFieldValue().getField();
+      DexField rewrittenField = graphLens.getRenamedFieldSignature(field);
+      if (rewrittenField != field) {
+        SingleFieldValue rewrittenAssumeValue =
+            appView
+                .abstractValueFactory()
+                .createSingleFieldValue(rewrittenField, ObjectState.empty());
+        return create(assumeType, rewrittenAssumeValue, isSideEffectFree);
+      }
+    }
+    return this;
+  }
+
+  public AssumeInfo withoutPrunedItems(PrunedItems prunedItems) {
+    // Verify that there is no need to prune the assumed type.
+    assert assumeType.isNotNullType() || assumeType.isUnknown();
+    // If the assumed value is a static field, and the static field is removed, then prune the
+    // assumed value.
+    if (assumeValue.isSingleFieldValue()
+        && prunedItems.isRemoved(assumeValue.asSingleFieldValue().getField())) {
+      return create(assumeType, AbstractValue.unknown(), isSideEffectFree);
+    }
+    return this;
   }
 
   @Override
   public boolean equals(Object other) {
-    if (other == null) {
-      return false;
+    if (this == other) {
+      return true;
     }
-    if (!(other instanceof AssumeInfo)) {
+    if (other == null || getClass() != other.getClass()) {
       return false;
     }
     AssumeInfo assumeInfo = (AssumeInfo) other;
-    return type == assumeInfo.type && rule == assumeInfo.rule;
+    return assumeValue.equals(assumeInfo.assumeValue)
+        && assumeType.equals(assumeInfo.assumeType)
+        && isSideEffectFree == assumeInfo.isSideEffectFree;
   }
 
   @Override
   public int hashCode() {
-    return type.ordinal() * 31 + rule.hashCode();
+    return Objects.hash(assumeValue, assumeType, isSideEffectFree);
+  }
+
+  public static class Builder {
+
+    private DynamicType assumeType = DynamicType.unknown();
+    private AbstractValue assumeValue = AbstractValue.unknown();
+    private boolean isSideEffectFree = false;
+
+    public Builder meet(AssumeInfo assumeInfo) {
+      return meetAssumeType(assumeInfo.assumeType)
+          .meetAssumeValue(assumeInfo.assumeValue)
+          .meetIsSideEffectFree(assumeInfo.isSideEffectFree);
+    }
+
+    public Builder meetAssumeType(DynamicType assumeType) {
+      this.assumeType = internalMeetType(this.assumeType, assumeType);
+      return this;
+    }
+
+    public Builder meetAssumeValue(AbstractValue assumeValue) {
+      this.assumeValue = internalMeetValue(this.assumeValue, assumeValue);
+      return this;
+    }
+
+    public Builder meetIsSideEffectFree(boolean isSideEffectFree) {
+      this.isSideEffectFree = internalMeetIsSideEffectFree(this.isSideEffectFree, isSideEffectFree);
+      return this;
+    }
+
+    public Builder setIsSideEffectFree() {
+      this.isSideEffectFree = true;
+      return this;
+    }
+
+    public AssumeInfo build() {
+      return create(assumeType, assumeValue, isSideEffectFree);
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
index d207ac7..223fa00 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
@@ -4,15 +4,11 @@
 
 package com.android.tools.r8.ir.optimize.membervaluepropagation.assume;
 
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexMember;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
-import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo.AssumeType;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.ProguardMemberRule;
+import com.android.tools.r8.shaking.AssumeInfoCollection;
 
 public class AssumeInfoLookup {
 
@@ -20,33 +16,12 @@
       AppView<AppInfoWithLiveness> appView,
       SingleResolutionResult<?> resolutionResult,
       DexClassAndMethod singleTarget) {
-    AssumeInfo resolutionLookup = lookupAssumeInfo(appView, resolutionResult.getResolutionPair());
-    if (resolutionLookup == null) {
-      return singleTarget != null ? lookupAssumeInfo(appView, singleTarget) : null;
-    }
+    AssumeInfoCollection assumeInfoCollection = appView.getAssumeInfoCollection();
+    AssumeInfo resolutionLookup = assumeInfoCollection.get(resolutionResult.getResolutionPair());
     AssumeInfo singleTargetLookup =
-        singleTarget != null ? lookupAssumeInfo(appView, singleTarget) : null;
+        singleTarget != null ? assumeInfoCollection.get(singleTarget) : null;
     return singleTargetLookup != null
         ? resolutionLookup.meet(singleTargetLookup)
         : resolutionLookup;
   }
-
-  public static AssumeInfo lookupAssumeInfo(
-      AppView<? extends AppInfoWithClassHierarchy> appView, DexClassAndMember<?, ?> member) {
-    DexMember<?, ?> reference = member.getReference();
-    ProguardMemberRule assumeNoSideEffectsRule = appView.rootSet().noSideEffects.get(reference);
-    ProguardMemberRule assumeValuesRule = appView.rootSet().assumedValues.get(reference);
-    if (assumeNoSideEffectsRule == null && assumeValuesRule == null) {
-      return null;
-    }
-    AssumeType type =
-        assumeNoSideEffectsRule != null
-            ? AssumeType.ASSUME_NO_SIDE_EFFECTS
-            : AssumeType.ASSUME_VALUES;
-    if ((assumeNoSideEffectsRule != null && assumeNoSideEffectsRule.hasReturnValue())
-        || assumeValuesRule == null) {
-      return new AssumeInfo(type, assumeNoSideEffectsRule);
-    }
-    return new AssumeInfo(type, assumeValuesRule);
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 0f67814..f3cb024 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -106,7 +106,7 @@
           invokeType)) {
         eligibleLibraryMethod = currentResolvedMethod.asLibraryMethod();
       }
-      if (appView.appInfo().isAssumeMethod(currentResolvedMethod)) {
+      if (appView.getAssumeInfoCollection().contains(currentResolvedMethod)) {
         break;
       }
       DexClass currentResolvedHolder = currentResolvedMethod.getHolder();
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 1d5e67d..677338b 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexClassAndMember;
-import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
@@ -143,10 +142,6 @@
   private final KeepInfoCollection keepInfo;
   /** All items with assumemayhavesideeffects rule. */
   public final Map<DexReference, ProguardMemberRule> mayHaveSideEffects;
-  /** All items with assumenosideeffects rule. */
-  public final Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects;
-  /** All items with assumevalues rule. */
-  public final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues;
   /** All methods that should be inlined if possible due to a configuration directive. */
   private final Set<DexMethod> alwaysInline;
   /**
@@ -222,8 +217,6 @@
       Map<DexCallSite, ProgramMethodSet> callSites,
       KeepInfoCollection keepInfo,
       Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
-      Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects,
-      Map<DexMember<?, ?>, ProguardMemberRule> assumedValues,
       Set<DexMethod> alwaysInline,
       Set<DexMethod> neverInlineDueToSingleCaller,
       Set<DexMethod> whyAreYouNotInlining,
@@ -255,8 +248,6 @@
     this.objectAllocationInfoCollection = objectAllocationInfoCollection;
     this.keepInfo = keepInfo;
     this.mayHaveSideEffects = mayHaveSideEffects;
-    this.noSideEffects = noSideEffects;
-    this.assumedValues = assumedValues;
     this.callSites = callSites;
     this.alwaysInline = alwaysInline;
     this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
@@ -299,8 +290,6 @@
         previous.callSites,
         previous.keepInfo,
         previous.mayHaveSideEffects,
-        previous.noSideEffects,
-        previous.assumedValues,
         previous.alwaysInline,
         previous.neverInlineDueToSingleCaller,
         previous.whyAreYouNotInlining,
@@ -346,8 +335,6 @@
         previous.callSites,
         extendPinnedItems(previous, prunedItems.getAdditionalPinnedItems()),
         previous.mayHaveSideEffects,
-        pruneMapFromMembers(previous.noSideEffects, prunedItems, executorService, futures),
-        pruneMapFromMembers(previous.assumedValues, prunedItems, executorService, futures),
         pruneMethods(previous.alwaysInline, prunedItems, executorService, futures),
         pruneMethods(previous.neverInlineDueToSingleCaller, prunedItems, executorService, futures),
         pruneMethods(previous.whyAreYouNotInlining, prunedItems, executorService, futures),
@@ -551,8 +538,6 @@
         callSites,
         keepInfo,
         mayHaveSideEffects,
-        noSideEffects,
-        assumedValues,
         alwaysInline,
         neverInlineDueToSingleCaller,
         whyAreYouNotInlining,
@@ -630,8 +615,6 @@
     this.objectAllocationInfoCollection = previous.objectAllocationInfoCollection;
     this.keepInfo = previous.keepInfo;
     this.mayHaveSideEffects = previous.mayHaveSideEffects;
-    this.noSideEffects = previous.noSideEffects;
-    this.assumedValues = previous.assumedValues;
     this.callSites = previous.callSites;
     this.alwaysInline = previous.alwaysInline;
     this.neverInlineDueToSingleCaller = previous.neverInlineDueToSingleCaller;
@@ -762,26 +745,6 @@
     return neverInlineDueToSingleCaller.contains(method.getReference());
   }
 
-  public boolean isAssumeMethod(DexClassAndMethod method) {
-    return isAssumeNoSideEffectsMethod(method) || isAssumeValuesMethod(method);
-  }
-
-  public boolean isAssumeNoSideEffectsMethod(DexMethod method) {
-    return noSideEffects.containsKey(method);
-  }
-
-  public boolean isAssumeNoSideEffectsMethod(DexClassAndMethod method) {
-    return isAssumeNoSideEffectsMethod(method.getReference());
-  }
-
-  public boolean isAssumeValuesMethod(DexMethod method) {
-    return assumedValues.containsKey(method);
-  }
-
-  public boolean isAssumeValuesMethod(DexClassAndMethod method) {
-    return isAssumeValuesMethod(method.getReference());
-  }
-
   public boolean isWhyAreYouNotInliningMethod(DexMethod method) {
     return whyAreYouNotInlining.contains(method);
   }
@@ -1239,13 +1202,6 @@
         keepInfo.rewrite(definitionSupplier, lens, application.options),
         // Take any rule in case of collisions.
         lens.rewriteReferenceKeys(mayHaveSideEffects, (reference, rules) -> ListUtils.first(rules)),
-        // Take the assume rule from the representative in case of collisions.
-        lens.rewriteReferenceKeys(
-            noSideEffects,
-            (reference, rules) -> noSideEffects.get(lens.getOriginalMemberSignature(reference))),
-        lens.rewriteReferenceKeys(
-            assumedValues,
-            (reference, rules) -> assumedValues.get(lens.getOriginalMemberSignature(reference))),
         lens.rewriteReferences(alwaysInline),
         lens.rewriteReferences(neverInlineDueToSingleCaller),
         lens.rewriteReferences(whyAreYouNotInlining),
diff --git a/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java
new file mode 100644
index 0000000..5d1686f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java
@@ -0,0 +1,144 @@
+// 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 static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMember;
+import com.android.tools.r8.graph.DexMember;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo;
+import com.android.tools.r8.utils.MapUtils;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+public class AssumeInfoCollection {
+
+  private final Map<DexMember<?, ?>, AssumeInfo> backing;
+
+  AssumeInfoCollection(Map<DexMember<?, ?>, AssumeInfo> backing) {
+    assert backing.values().stream().noneMatch(AssumeInfo::isEmpty);
+    this.backing = backing;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public boolean contains(DexClassAndMember<?, ?> member) {
+    return backing.containsKey(member.getReference());
+  }
+
+  private AssumeInfo get(DexMember<?, ?> member) {
+    return backing.getOrDefault(member, AssumeInfo.empty());
+  }
+
+  public AssumeInfo get(DexClassAndMember<?, ?> member) {
+    return get(member.getReference());
+  }
+
+  public boolean isSideEffectFree(DexMember<?, ?> member) {
+    return get(member).isSideEffectFree();
+  }
+
+  public boolean isSideEffectFree(DexClassAndMember<?, ?> member) {
+    return isSideEffectFree(member.getReference());
+  }
+
+  public AssumeInfoCollection rewrittenWithLens(AppView<?> appView, GraphLens graphLens) {
+    Map<DexMember<?, ?>, AssumeInfo> rewrittenCollection = new IdentityHashMap<>();
+    backing.forEach(
+        (reference, info) -> {
+          DexMember<?, ?> rewrittenReference = graphLens.getRenamedMemberSignature(reference);
+          AssumeInfo rewrittenInfo = info.rewrittenWithLens(appView, graphLens);
+          assert !rewrittenInfo.isEmpty();
+          rewrittenCollection.put(rewrittenReference, rewrittenInfo);
+        });
+    return new AssumeInfoCollection(rewrittenCollection);
+  }
+
+  public AssumeInfoCollection withoutPrunedItems(PrunedItems prunedItems) {
+    Map<DexMember<?, ?>, AssumeInfo> rewrittenCollection = new IdentityHashMap<>();
+    backing.forEach(
+        (reference, info) -> {
+          if (!prunedItems.isRemoved(reference)) {
+            AssumeInfo rewrittenInfo = info.withoutPrunedItems(prunedItems);
+            if (!rewrittenInfo.isEmpty()) {
+              rewrittenCollection.put(reference, rewrittenInfo);
+            }
+          }
+        });
+    return new AssumeInfoCollection(rewrittenCollection);
+  }
+
+  public static class Builder {
+
+    private final Map<DexMember<?, ?>, AssumeInfo.Builder> backing = new ConcurrentHashMap<>();
+
+    public Builder applyIf(boolean condition, Consumer<Builder> consumer) {
+      if (condition) {
+        consumer.accept(this);
+      }
+      return this;
+    }
+
+    public AssumeInfo buildInfo(DexClassAndMember<?, ?> member) {
+      AssumeInfo.Builder builder = backing.get(member.getReference());
+      return builder != null ? builder.build() : AssumeInfo.empty();
+    }
+
+    private AssumeInfo.Builder getOrCreateAssumeInfo(DexMember<?, ?> member) {
+      return backing.computeIfAbsent(member, ignoreKey(AssumeInfo::builder));
+    }
+
+    private AssumeInfo.Builder getOrCreateAssumeInfo(DexClassAndMember<?, ?> member) {
+      return getOrCreateAssumeInfo(member.getReference());
+    }
+
+    public boolean isEmpty() {
+      return backing.isEmpty();
+    }
+
+    public Builder meet(DexMember<?, ?> member, AssumeInfo assumeInfo) {
+      getOrCreateAssumeInfo(member).meet(assumeInfo);
+      return this;
+    }
+
+    public Builder meetAssumeType(DexClassAndMember<?, ?> member, DynamicType assumeType) {
+      getOrCreateAssumeInfo(member).meetAssumeType(assumeType);
+      return this;
+    }
+
+    public Builder meetAssumeValue(DexClassAndMember<?, ?> member, AbstractValue assumeValue) {
+      getOrCreateAssumeInfo(member).meetAssumeValue(assumeValue);
+      return this;
+    }
+
+    public Builder setIsSideEffectFree(DexClassAndMember<?, ?> member) {
+      getOrCreateAssumeInfo(member).setIsSideEffectFree();
+      return this;
+    }
+
+    public AssumeInfoCollection build() {
+      return new AssumeInfoCollection(
+          MapUtils.newIdentityHashMap(
+              builder ->
+                  backing.forEach(
+                      (reference, infoBuilder) -> {
+                        AssumeInfo info = infoBuilder.build();
+                        if (!info.isEmpty()) {
+                          builder.accept(reference, info);
+                        }
+                      }),
+              backing.size()));
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 8dd3707..02f3eb9 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -4143,8 +4143,6 @@
             callSites,
             keepInfo,
             rootSet.mayHaveSideEffects,
-            rootSet.noSideEffects,
-            rootSet.assumedValues,
             amendWithCompanionMethods(rootSet.alwaysInline),
             amendWithCompanionMethods(rootSet.neverInlineDueToSingleCaller),
             amendWithCompanionMethods(rootSet.whyAreYouNotInlining),
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
index 85d14b9..41c48cb 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
@@ -26,7 +26,6 @@
 import com.android.tools.r8.ir.conversion.IRToDexFinalizer;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo;
-import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfoLookup;
 import com.android.tools.r8.shaking.Enqueuer.FieldAccessKind;
 import com.android.tools.r8.shaking.Enqueuer.FieldAccessMetadata;
 import com.android.tools.r8.shaking.Enqueuer.Mode;
@@ -99,8 +98,8 @@
       // If the value of the field is not guaranteed to be the default value, even if it is never
       // assigned, then give up.
       // TODO(b/205810841): Allow this by handling this in the corresponding IR rewriter.
-      AssumeInfo assumeInfo = AssumeInfoLookup.lookupAssumeInfo(appView, field);
-      if (assumeInfo != null && assumeInfo.hasReturnInfo()) {
+      AssumeInfo assumeInfo = appView.getAssumeInfoCollection().get(field);
+      if (!assumeInfo.getAssumeValue().isUnknown()) {
         return enqueueDeferredEnqueuerActions(field);
       }
       if (field.getAccessFlags().isStatic() && field.getDefinition().hasExplicitStaticValue()) {
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 34841ef..1a8c950 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.InputDependencyGraphConsumer;
 import com.android.tools.r8.Version;
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -19,7 +18,6 @@
 import com.android.tools.r8.position.TextRange;
 import com.android.tools.r8.shaking.ProguardConfiguration.Builder;
 import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
-import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
 import com.android.tools.r8.shaking.ProguardWildcard.BackReference;
 import com.android.tools.r8.shaking.ProguardWildcard.Pattern;
 import com.android.tools.r8.utils.IdentifierUtils;
@@ -1475,23 +1473,16 @@
                             ruleBuilder.setReturnValue(
                                 new ProguardMemberRuleReturnValue(new LongInterval(min, max)));
                           } else {
-                            if (ruleBuilder.getTypeMatcher() instanceof MatchSpecificType) {
-                              int lastDotIndex = qualifiedFieldNameOrInteger.lastIndexOf(".");
-                              DexType fieldType =
-                                  ((MatchSpecificType) ruleBuilder.getTypeMatcher()).type;
-                              DexType fieldClass =
-                                  dexItemFactory.createType(
-                                      javaTypeToDescriptor(
-                                          qualifiedFieldNameOrInteger.substring(0, lastDotIndex)));
-                              DexString fieldName =
-                                  dexItemFactory.createString(
-                                      qualifiedFieldNameOrInteger.substring(lastDotIndex + 1));
-                              DexField field =
-                                  dexItemFactory.createField(fieldClass, fieldType, fieldName);
-                              ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(field));
-                            } else {
-                              throw parseError("Expected specific type", fieldOrValueStart);
-                            }
+                            int lastDotIndex = qualifiedFieldNameOrInteger.lastIndexOf(".");
+                            DexType fieldHolder =
+                                dexItemFactory.createType(
+                                    javaTypeToDescriptor(
+                                        qualifiedFieldNameOrInteger.substring(0, lastDotIndex)));
+                            DexString fieldName =
+                                dexItemFactory.createString(
+                                    qualifiedFieldNameOrInteger.substring(lastDotIndex + 1));
+                            ruleBuilder.setReturnValue(
+                                new ProguardMemberRuleReturnValue(fieldHolder, fieldName));
                           }
                         }
                       }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
index 11b8868..8894a53 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
@@ -4,7 +4,17 @@
 
 package com.android.tools.r8.shaking;
 
-import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.LongInterval;
 
 public class ProguardMemberRuleReturnValue {
@@ -18,34 +28,44 @@
   private final Type type;
   private final boolean booleanValue;
   private final LongInterval longInterval;
-  private final DexField field;
+  private final DexType fieldHolder;
+  private final DexString fieldName;
+  private final boolean notNull;
 
   ProguardMemberRuleReturnValue(boolean value) {
     this.type = Type.BOOLEAN;
     this.booleanValue = value;
     this.longInterval = null;
-    this.field = null;
+    this.fieldHolder = null;
+    this.fieldName = null;
+    this.notNull = false;
   }
 
   ProguardMemberRuleReturnValue(LongInterval value) {
     this.type = Type.VALUE_RANGE;
     this.booleanValue = false;
     this.longInterval = value;
-    this.field = null;
+    this.fieldHolder = null;
+    this.fieldName = null;
+    this.notNull = false;
   }
 
-  ProguardMemberRuleReturnValue(DexField field) {
+  ProguardMemberRuleReturnValue(DexType fieldHolder, DexString fieldName) {
     this.type = Type.FIELD;
     this.booleanValue = false;
     this.longInterval = null;
-    this.field = field;
+    this.fieldHolder = fieldHolder;
+    this.fieldName = fieldName;
+    this.notNull = false;
   }
 
   ProguardMemberRuleReturnValue() {
     this.type = Type.NULL;
     this.booleanValue = false;
     this.longInterval = null;
-    this.field = null;
+    this.fieldHolder = null;
+    this.fieldName = null;
+    this.notNull = false;
   }
 
   public boolean isBoolean() {
@@ -105,9 +125,60 @@
     return longInterval;
   }
 
-  public DexField getField() {
+  public DexType getFieldHolder() {
     assert isField();
-    return field;
+    return fieldHolder;
+  }
+
+  public DexString getFieldName() {
+    assert isField();
+    return fieldName;
+  }
+
+  public AbstractValue toAbstractValue(AppView<?> appView, DexType valueType) {
+    AbstractValueFactory abstractValueFactory = appView.abstractValueFactory();
+    switch (type) {
+      case BOOLEAN:
+        return abstractValueFactory.createSingleNumberValue(BooleanUtils.intValue(booleanValue));
+      case FIELD:
+        DexClass holder = appView.definitionFor(fieldHolder);
+        if (holder != null) {
+          DexEncodedField field = holder.lookupUniqueStaticFieldWithName(fieldName);
+          if (field != null) {
+            return abstractValueFactory.createSingleFieldValue(
+                field.getReference(), ObjectState.empty());
+          }
+        }
+        return AbstractValue.unknown();
+      case NULL:
+        return abstractValueFactory.createNullValue();
+      case VALUE_RANGE:
+        if (longInterval.isSingleValue()) {
+          long singleValue = longInterval.getSingleValue();
+          if (valueType.isReferenceType()) {
+            return singleValue == 0
+                ? abstractValueFactory.createNullValue()
+                : AbstractValue.unknown();
+          }
+          return abstractValueFactory.createSingleNumberValue(singleValue);
+        }
+        return abstractValueFactory.createNumberFromIntervalValue(
+            longInterval.getMin(), longInterval.getMax());
+      default:
+        throw new Unreachable("Unexpected type: " + type);
+    }
+  }
+
+  public DynamicType toDynamicType(AppView<?> appView, DexType valueType) {
+    if (valueType.isReferenceType()) {
+      if (type == Type.VALUE_RANGE && !longInterval.containsValue(0)) {
+        return DynamicType.definitelyNotNull();
+      }
+      if (notNull) {
+        return DynamicType.definitelyNotNull();
+      }
+    }
+    return DynamicType.unknown();
   }
 
   @Override
@@ -126,7 +197,7 @@
       }
     } else {
       assert isField();
-      result.append(field.holder.toSourceString() + '.' + field.name);
+      result.append(getFieldHolder().getTypeName()).append('.').append(getFieldName());
     }
     return result.toString();
   }
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 471f865..c8818b1 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -10,6 +10,7 @@
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.AssumeNoSideEffectsRuleForObjectMembersDiagnostic;
+import com.android.tools.r8.errors.AssumeValuesMissingStaticFieldDiagnostic;
 import com.android.tools.r8.errors.InlinableStaticFinalFieldPreconditionDiagnostic;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -45,9 +46,12 @@
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodDesugaringBaseEventConsumer;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
+import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.shaking.AnnotationMatchResult.AnnotationsIgnoredMatchResult;
 import com.android.tools.r8.shaking.AnnotationMatchResult.ConcreteAnnotationMatchResult;
@@ -105,6 +109,7 @@
   public static class RootSetBuilder {
 
     private final AppView<? extends AppInfoWithClassHierarchy> appView;
+    private AssumeInfoCollection.Builder assumeInfoCollectionBuilder;
     private final SubtypingInfo subtypingInfo;
     private final DirectMappedDexApplication application;
     private final Iterable<? extends ProguardConfigurationRule> rules;
@@ -127,8 +132,6 @@
         new IdentityHashMap<>();
     private final Map<DexReference, ProguardMemberRule> mayHaveSideEffects =
         new IdentityHashMap<>();
-    private final Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
-    private final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
     private final Set<DexMember<?, ?>> identifierNameStrings = Sets.newIdentityHashSet();
     private final Map<DexMethod, ProgramMethod> keptMethodBridges = new ConcurrentHashMap<>();
     private final Queue<DelayedRootSetActionItem> delayedRootSetActionItems =
@@ -176,6 +179,12 @@
       // Intentionally empty.
     }
 
+    public RootSetBuilder setAssumeInfoCollectionBuilder(
+        AssumeInfoCollection.Builder assumeInfoCollectionBuilder) {
+      this.assumeInfoCollectionBuilder = assumeInfoCollectionBuilder;
+      return this;
+    }
+
     // Process a class with the keep rule.
     private void process(DexClass clazz, ProguardConfigurationRule rule, ProguardIfRule ifRule) {
       if (!satisfyClassType(rule, clazz)) {
@@ -256,13 +265,17 @@
         markClass(clazz, rule, ifRule);
         markMatchingVisibleMethods(clazz, memberKeepRules, rule, null, true, ifRule);
         markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
-      } else if (rule instanceof ProguardAssumeMayHaveSideEffectsRule
-          || rule instanceof ProguardAssumeNoSideEffectRule
-          || rule instanceof ProguardAssumeValuesRule) {
+      } else if (rule instanceof ProguardAssumeMayHaveSideEffectsRule) {
         markMatchingVisibleMethods(clazz, memberKeepRules, rule, null, true, ifRule);
-        markMatchingOverriddenMethods(
-            appView.appInfo(), clazz, memberKeepRules, rule, null, true, ifRule);
+        markMatchingOverriddenMethods(clazz, memberKeepRules, rule, null, true, ifRule);
         markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
+      } else if (rule instanceof ProguardAssumeNoSideEffectRule
+          || rule instanceof ProguardAssumeValuesRule) {
+        if (assumeInfoCollectionBuilder != null) {
+          markMatchingVisibleMethods(clazz, memberKeepRules, rule, null, true, ifRule);
+          markMatchingOverriddenMethods(clazz, memberKeepRules, rule, null, true, ifRule);
+          markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
+        }
       } else if (rule instanceof NoFieldTypeStrengtheningRule) {
         markMatchingFields(clazz, memberKeepRules, rule, null, ifRule);
       } else if (rule instanceof InlineRule
@@ -349,7 +362,7 @@
       }
       finalizeCheckDiscardedInformation();
       generateAssumeNoSideEffectsWarnings();
-      if (!noSideEffects.isEmpty() || !assumedValues.isEmpty()) {
+      if (assumeInfoCollectionBuilder != null && !assumeInfoCollectionBuilder.isEmpty()) {
         BottomUpClassHierarchyTraversal.forAllClasses(appView, subtypingInfo)
             .visit(appView.appInfo().classes(), this::propagateAssumeRules);
       }
@@ -381,8 +394,6 @@
           noHorizontalClassMerging,
           neverPropagateValue,
           mayHaveSideEffects,
-          noSideEffects,
-          assumedValues,
           dependentKeepClassCompatRule,
           identifierNameStrings,
           ifRules,
@@ -401,17 +412,12 @@
           assert !encodedMethod.shouldNotHaveCode();
           continue;
         }
-        propagateAssumeRules(clazz.type, encodedMethod.getReference(), subTypes, noSideEffects);
-        propagateAssumeRules(clazz.type, encodedMethod.getReference(), subTypes, assumedValues);
+        propagateAssumeRules(clazz, encodedMethod.getReference(), subTypes);
       }
     }
 
-    private void propagateAssumeRules(
-        DexType type,
-        DexMethod reference,
-        Set<DexType> subTypes,
-        Map<DexMember<?, ?>, ProguardMemberRule> assumeRulePool) {
-      ProguardMemberRule ruleToBePropagated = null;
+    private void propagateAssumeRules(DexClass clazz, DexMethod reference, Set<DexType> subTypes) {
+      AssumeInfo infoToBePropagated = null;
       for (DexType subType : subTypes) {
         DexMethod referenceInSubType =
             appView.dexItemFactory().createMethod(subType, reference.proto, reference.name);
@@ -419,34 +425,34 @@
         // override the method, and when the retrieval of bound rule fails, it is unclear whether it
         // is due to the lack of the definition or it indeed means no matching rules. Similar to how
         // we apply those assume rules, here we use a resolved target.
-        DexEncodedMethod target =
+        DexClassAndMethod target =
             appView
                 .appInfo()
                 .unsafeResolveMethodDueToDexFormatLegacy(referenceInSubType)
-                .getSingleTarget();
+                .getResolutionPair();
         // But, the resolution should not be landed on the current type we are visiting.
-        if (target == null || target.getHolderType() == type) {
+        if (target == null || target.getHolder() == clazz) {
           continue;
         }
-        ProguardMemberRule ruleInSubType = assumeRulePool.get(target.getReference());
+        AssumeInfo ruleInSubType = assumeInfoCollectionBuilder.buildInfo(target);
         // We are looking for the greatest lower bound of assume rules from all sub types.
         // If any subtype doesn't have a matching assume rule, the lower bound is literally nothing.
         if (ruleInSubType == null) {
-          ruleToBePropagated = null;
+          infoToBePropagated = null;
           break;
         }
-        if (ruleToBePropagated == null) {
-          ruleToBePropagated = ruleInSubType;
+        if (infoToBePropagated == null) {
+          infoToBePropagated = ruleInSubType;
         } else {
           // TODO(b/133208961): Introduce comparison/meet of assume rules.
-          if (!ruleToBePropagated.equals(ruleInSubType)) {
-            ruleToBePropagated = null;
+          if (!infoToBePropagated.equals(ruleInSubType)) {
+            infoToBePropagated = null;
             break;
           }
         }
       }
-      if (ruleToBePropagated != null) {
-        assumeRulePool.put(reference, ruleToBePropagated);
+      if (infoToBePropagated != null) {
+        assumeInfoCollectionBuilder.meet(reference, infoToBePropagated);
       }
     }
 
@@ -663,7 +669,6 @@
     }
 
     private void markMatchingOverriddenMethods(
-        AppInfoWithClassHierarchy appInfoWithSubtyping,
         DexClass clazz,
         Collection<ProguardMemberRule> memberKeepRules,
         ProguardConfigurationRule rule,
@@ -1156,36 +1161,17 @@
           return;
         }
         evaluateKeepRule(
-            item.asProgramDefinition(), context.asProguardKeepRule(), rule, precondition, ifRule);
+            item.asProgramDefinition(), context.asProguardKeepRule(), precondition, ifRule);
       } else if (context instanceof ProguardAssumeMayHaveSideEffectsRule) {
         mayHaveSideEffects.put(item.getReference(), rule);
         context.markAsUsed();
       } else if (context instanceof ProguardAssumeNoSideEffectRule) {
-        if (item.isMember()) {
-          DexClassAndMember<?, ?> member = item.asMember();
-          if (member.getHolderType() == appView.dexItemFactory().objectType) {
-            assert member.isMethod();
-            reportAssumeNoSideEffectsWarningForJavaLangClassMethod(
-                member.asMethod(), (ProguardAssumeNoSideEffectRule) context);
-          } else {
-            noSideEffects.put(member.getReference(), rule);
-            if (member.isMethod()) {
-              DexClassAndMethod method = member.asMethod();
-              if (method.getDefinition().isClassInitializer()) {
-                feedback.classInitializerMayBePostponed(method.getDefinition());
-              }
-            }
-          }
-          context.markAsUsed();
-        }
+        evaluateAssumeNoSideEffectsRule(item, (ProguardAssumeNoSideEffectRule) context, rule);
+      } else if (context instanceof ProguardAssumeValuesRule) {
+        evaluateAssumeValuesRule(item, (ProguardAssumeValuesRule) context, rule);
       } else if (context instanceof ProguardWhyAreYouKeepingRule) {
         reasonAsked.computeIfAbsent(item.getReference(), i -> i);
         context.markAsUsed();
-      } else if (context instanceof ProguardAssumeValuesRule) {
-        if (item.isMember()) {
-          assumedValues.put(item.asMember().getReference(), rule);
-          context.markAsUsed();
-        }
       } else if (context.isProguardCheckDiscardRule()) {
         assert item.isProgramMember();
         evaluateCheckDiscardMemberRule(
@@ -1427,10 +1413,60 @@
       }
     }
 
+    private void evaluateAssumeNoSideEffectsRule(
+        Definition item, ProguardAssumeNoSideEffectRule context, ProguardMemberRule rule) {
+      assert assumeInfoCollectionBuilder != null;
+      if (!item.isMember()) {
+        return;
+      }
+      DexClassAndMember<?, ?> member = item.asMember();
+      if (member.getHolderType() == appView.dexItemFactory().objectType) {
+        assert member.isMethod();
+        reportAssumeNoSideEffectsWarningForJavaLangClassMethod(member.asMethod(), context);
+      } else {
+        DexType valueType =
+            member.getReference().apply(DexField::getType, DexMethod::getReturnType);
+        assumeInfoCollectionBuilder
+            .applyIf(
+                rule.hasReturnValue(),
+                builder -> {
+                  DynamicType assumeType = rule.getReturnValue().toDynamicType(appView, valueType);
+                  AbstractValue assumeValue =
+                      rule.getReturnValue().toAbstractValue(appView, valueType);
+                  builder.meetAssumeType(member, assumeType).meetAssumeValue(member, assumeValue);
+                  reportAssumeValuesWarningForMissingReturnField(context, rule, assumeValue);
+                })
+            .setIsSideEffectFree(member);
+        if (member.isMethod()) {
+          DexClassAndMethod method = member.asMethod();
+          if (method.getDefinition().isClassInitializer()) {
+            feedback.classInitializerMayBePostponed(method.getDefinition());
+          }
+        }
+      }
+      context.markAsUsed();
+    }
+
+    private void evaluateAssumeValuesRule(
+        Definition item, ProguardAssumeValuesRule context, ProguardMemberRule rule) {
+      assert assumeInfoCollectionBuilder != null;
+      if (!item.isMember() || !rule.hasReturnValue()) {
+        return;
+      }
+      DexClassAndMember<?, ?> member = item.asMember();
+      DexType valueType = member.getReference().apply(DexField::getType, DexMethod::getReturnType);
+      DynamicType assumeType = rule.getReturnValue().toDynamicType(appView, valueType);
+      AbstractValue assumeValue = rule.getReturnValue().toAbstractValue(appView, valueType);
+      assumeInfoCollectionBuilder
+          .meetAssumeType(member, assumeType)
+          .meetAssumeValue(member, assumeValue);
+      reportAssumeValuesWarningForMissingReturnField(context, rule, assumeValue);
+      context.markAsUsed();
+    }
+
     private void evaluateKeepRule(
         ProgramDefinition item,
         ProguardKeepRule context,
-        ProguardMemberRule rule,
         DexProgramClass precondition,
         ProguardIfRule ifRule) {
       if (item.isField()) {
@@ -1663,6 +1699,22 @@
                     .build());
           });
     }
+
+    private void reportAssumeValuesWarningForMissingReturnField(
+        ProguardConfigurationRule context, ProguardMemberRule rule, AbstractValue assumeValue) {
+      if (rule.hasReturnValue() && rule.getReturnValue().isField()) {
+        assert assumeValue.isSingleFieldValue() || assumeValue.isUnknown();
+        if (assumeValue.isUnknown()) {
+          ProguardMemberRuleReturnValue returnValue = rule.getReturnValue();
+          options.reporter.warning(
+              new AssumeValuesMissingStaticFieldDiagnostic.Builder()
+                  .setField(returnValue.getFieldHolder(), returnValue.getFieldName())
+                  .setOrigin(context.getOrigin())
+                  .setPosition(context.getPosition())
+                  .build());
+        }
+      }
+    }
   }
 
   abstract static class RootSetBase {
@@ -1712,8 +1764,6 @@
     public final Set<DexType> noHorizontalClassMerging;
     public final Set<DexMember<?, ?>> neverPropagateValue;
     public final Map<DexReference, ProguardMemberRule> mayHaveSideEffects;
-    public final Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects;
-    public final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues;
     public final Set<DexMember<?, ?>> identifierNameStrings;
     public final Set<ProguardIfRule> ifRules;
 
@@ -1733,8 +1783,6 @@
         Set<DexType> noHorizontalClassMerging,
         Set<DexMember<?, ?>> neverPropagateValue,
         Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
-        Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects,
-        Map<DexMember<?, ?>, ProguardMemberRule> assumedValues,
         Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
         Set<DexMember<?, ?>> identifierNameStrings,
         Set<ProguardIfRule> ifRules,
@@ -1759,8 +1807,6 @@
       this.noHorizontalClassMerging = noHorizontalClassMerging;
       this.neverPropagateValue = neverPropagateValue;
       this.mayHaveSideEffects = mayHaveSideEffects;
-      this.noSideEffects = noSideEffects;
-      this.assumedValues = assumedValues;
       this.identifierNameStrings = Collections.unmodifiableSet(identifierNameStrings);
       this.ifRules = Collections.unmodifiableSet(ifRules);
     }
@@ -1819,7 +1865,6 @@
       pruneDeadReferences(noVerticalClassMerging, definitions, enqueuer);
       pruneDeadReferences(noHorizontalClassMerging, definitions, enqueuer);
       pruneDeadReferences(alwaysInline, definitions, enqueuer);
-      pruneDeadReferences(noSideEffects.keySet(), definitions, enqueuer);
     }
 
     private static void pruneDeadReferences(
@@ -1871,8 +1916,6 @@
           noHorizontalClassMerging,
           neverPropagateValue,
           mayHaveSideEffects,
-          noSideEffects,
-          assumedValues,
           dependentKeepClassCompatRule,
           identifierNameStrings,
           ifRules,
@@ -2044,8 +2087,6 @@
       StringBuilder builder = new StringBuilder();
       builder.append("RootSet");
       builder.append("\nreasonAsked: " + reasonAsked.size());
-      builder.append("\nnoSideEffects: " + noSideEffects.size());
-      builder.append("\nassumedValues: " + assumedValues.size());
       builder.append("\nidentifierNameStrings: " + identifierNameStrings.size());
       builder.append("\nifRules: " + ifRules.size());
       return builder.toString();
@@ -2164,8 +2205,6 @@
           Collections.emptySet(),
           emptyMap(),
           emptyMap(),
-          emptyMap(),
-          emptyMap(),
           Collections.emptySet(),
           ifRules,
           delayedRootSetActionItems,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LibraryFieldPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LibraryFieldPropagationTest.java
index 2a420cf..99de3f5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LibraryFieldPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LibraryFieldPropagationTest.java
@@ -35,7 +35,8 @@
 
   @Parameterized.Parameters(name = "{0}, with assume values rule: {1}")
   public static List<Object[]> data() {
-    return buildParameters(getTestParameters().withAllRuntimes().build(), BooleanUtils.values());
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
   }
 
   public LibraryFieldPropagationTest(TestParameters parameters, boolean withAssumeValuesRule) {
@@ -71,7 +72,7 @@
             withAssumeValuesRule
                 ? "-assumevalues class java.lang.Thread { public int MIN_PRIORITY return 1; }"
                 : "")
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::verifyFieldValueNotPropagated)
         .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 44c8c6e..927fd18 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -494,9 +494,8 @@
         assertFalse(rule.getReturnValue().isValueRange());
         assertTrue(rule.getReturnValue().isField());
         assertFalse(rule.getReturnValue().isNull());
-        assertEquals("com.google.C", rule.getReturnValue().getField().holder.toString());
-        assertEquals("int", rule.getReturnValue().getField().type.toString());
-        assertEquals("X", rule.getReturnValue().getField().name.toString());
+        assertEquals("com.google.C", rule.getReturnValue().getFieldHolder().getTypeName());
+        assertEquals("X", rule.getReturnValue().getFieldName().toString());
         matches |= 1 << 4;
       } else if (rule.getName().matches("returnsNull")) {
         assertTrue(rule.hasReturnValue());
@@ -515,9 +514,8 @@
         assertFalse(rule.getReturnValue().isValueRange());
         assertTrue(rule.getReturnValue().isField());
         assertFalse(rule.getReturnValue().isNull());
-        assertEquals("com.google.C", rule.getReturnValue().getField().holder.toString());
-        assertEquals("Object", rule.getReturnValue().getField().type.toString());
-        assertEquals("X", rule.getReturnValue().getField().name.toString());
+        assertEquals("com.google.C", rule.getReturnValue().getFieldHolder().getTypeName());
+        assertEquals("X", rule.getReturnValue().getFieldName().toString());
         matches |= 1 << 7;
       } else {
         fail("Unexpected");
@@ -581,9 +579,8 @@
         assertFalse(rule.getReturnValue().isValueRange());
         assertTrue(rule.getReturnValue().isField());
         assertFalse(rule.getReturnValue().isNull());
-        assertEquals("com.google.C", rule.getReturnValue().getField().holder.toString());
-        assertEquals("int", rule.getReturnValue().getField().type.toString());
-        assertEquals("X", rule.getReturnValue().getField().name.toString());
+        assertEquals("com.google.C", rule.getReturnValue().getFieldHolder().getTypeName());
+        assertEquals("X", rule.getReturnValue().getFieldName().toString());
         matches |= 1 << 4;
       } else if (rule.getName().matches("isNull")) {
         assertTrue(rule.hasReturnValue());
@@ -602,9 +599,8 @@
         assertFalse(rule.getReturnValue().isValueRange());
         assertTrue(rule.getReturnValue().isField());
         assertFalse(rule.getReturnValue().isNull());
-        assertEquals("com.google.C", rule.getReturnValue().getField().holder.toString());
-        assertEquals("Object", rule.getReturnValue().getField().type.toString());
-        assertEquals("X", rule.getReturnValue().getField().name.toString());
+        assertEquals("com.google.C", rule.getReturnValue().getFieldHolder().getTypeName());
+        assertEquals("X", rule.getReturnValue().getFieldName().toString());
         matches |= 1 << 7;
       } else {
         fail("Unexpected");
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
index 8130a6f..b73fc3e 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
@@ -88,15 +88,8 @@
               ? StringUtils.lines(
                   "[AnotherSub2, debug]: message08", "[AnotherSub2, debug]: message5", "The end")
               : StringUtils.lines(
-                  // TODO(b/133208961): Introduce comparison/meet of assume rules.
-                  // Itf has side effects for all methods, since we don't compute the meet yet.
-                  "[Sub1, info]: message00",
                   "[Base1, debug]: message00",
-                  "[Sub1, verbose]: message00",
-                  "[Base2, info]: message08",
                   "[AnotherSub2, debug]: message08",
-                  "[AnotherSub2, verbose]: message08",
-                  // Base2#debug also has side effects.
                   "[AnotherSub2, debug]: message5",
                   "The end");
         case NON_SPECIFIC_RULES_ALL:
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeReturnsFieldWithSubtypingTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeReturnsFieldWithSubtypingTest.java
new file mode 100644
index 0000000..63ea0c7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeReturnsFieldWithSubtypingTest.java
@@ -0,0 +1,58 @@
+// 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.assumevalues;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AssumeReturnsFieldWithSubtypingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addKeepRules(
+            "-assumevalues class " + Main.class.getTypeName() + " {",
+            "  java.lang.Object getGreeting() return " + Main.class.getTypeName() + ".greeting;",
+            "}",
+            // TODO(b/233828966): Maybe disallow shrinking of this in the first round of shaking.
+            "-keepclassmembers,allowobfuscation class " + Main.class.getTypeName() + "{",
+            "  java.lang.String greeting;",
+            "}")
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  static class Main {
+
+    static String greeting = System.currentTimeMillis() > 0 ? "Hello world!" : null;
+
+    public static void main(String[] args) {
+      System.out.println(getGreeting());
+    }
+
+    static Object getGreeting() {
+      return "Unexpected";
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesMissingStaticFieldDiagnosticTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesMissingStaticFieldDiagnosticTest.java
new file mode 100644
index 0000000..cc00fac
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesMissingStaticFieldDiagnosticTest.java
@@ -0,0 +1,63 @@
+// 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.assumevalues;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.AssumeValuesMissingStaticFieldDiagnostic;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AssumeValuesMissingStaticFieldDiagnosticTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(Backend.DEX)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addKeepRules(
+            "-assumevalues class " + Main.class.getTypeName() + " {",
+            "  static java.lang.Object get() return Missing.field;",
+            "}")
+        .allowDiagnosticWarningMessages()
+        .setMinApi(AndroidApiLevel.LATEST)
+        .compileWithExpectedDiagnostics(
+            diagnostics ->
+                diagnostics.assertWarningsMatch(
+                    allOf(
+                        diagnosticType(AssumeValuesMissingStaticFieldDiagnostic.class),
+                        diagnosticMessage(
+                            equalTo(
+                                "The field Missing.field is used as the return value in an "
+                                    + "-assumenosideeffects or -assumevalues rule, but no such "
+                                    + "static field exists.")))));
+  }
+
+  static class Main {
+
+    static Object get() {
+      return null;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
index b2f0964..9b5cc93 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
@@ -303,13 +303,17 @@
           "-assumevalues class android.os.Build$VERSION { *; }"
         };
 
-    for (String rule : rules) {
+    for (int ruleIndex = 0; ruleIndex < rules.length; ruleIndex++) {
+      final int finalRuleIndex = ruleIndex;
+      String rule = rules[ruleIndex];
       runTest(
           AndroidApiLevel.O_MR1,
           AndroidApiLevel.O_MR1,
           AndroidApiLevel.O_MR1,
           expectedResultForNative(AndroidApiLevel.O_MR1),
-          builder -> builder.allowUnusedProguardConfigurationRules(backend == Backend.CF),
+          builder ->
+              builder.allowUnusedProguardConfigurationRules(
+                  backend == Backend.CF || finalRuleIndex >= 4),
           this::compatCodePresent,
           ImmutableList.of(rule),
           SynthesizedRule.NOT_PRESENT);