@Override
  public boolean processChangedField(DifferentiateContext context, Difference.Change<JvmClass, JvmClass.Diff> clsChange, Difference.Change<JvmField, JvmField.Diff> fieldChange, Utils future, Utils present) {
    JvmClass changedClass = clsChange.getPast();
    JvmField changedField = fieldChange.getPast();
    JvmField.Diff diff = fieldChange.getDiff();

    debug("Field: ", changedField.getName());

    Iterable<JvmNodeReferenceID> propagated = lazy(() -> {
      return future.collectSubclassesWithoutField(changedClass.getReferenceID(), changedField);
    });
    JVMFlags addedFlags = diff.getAddedFlags();
    JVMFlags removedFlags = diff.getRemovedFlags();

    if (!changedField.isPrivate() && changedField.isInlinable() && changedField.getValue() != null) { // if the field was a compile-time constant
      boolean harmful = find(List.of(addedFlags, removedFlags), f -> f.isStatic() || f.isFinal()) != null;
      if (harmful || diff.valueChanged() || diff.accessRestricted()) {
        if (context.getParams().isProcessConstantsIncrementally()) {
          debug("Potentially inlined field changed its access or value => affecting field usages and static member import usages");
          affectMemberUsages(context, changedClass.getReferenceID(), changedField, propagated);
          affectStaticMemberImportUsages(context, changedClass.getReferenceID(), changedField.getName(), propagated);
        }
        else {
          debug("Potentially inlined field changed its access or value => a switch to non-incremental mode requested");
          if (!affectOnNonIncrementalChange(context, changedClass.getReferenceID(), changedField, present)) {
            debug("End of Differentiate, returning false");
            return false;
          }
        }
      }
    }

    if (diff.typeChanged() || diff.signatureChanged()) {
      debug("Type or signature changed --- affecting field usages");
      affectMemberUsages(context, changedClass.getReferenceID(), changedField, propagated);
    }
    else if (diff.flagsChanged()) {
      if (addedFlags.isStatic() || removedFlags.isStatic() || addedFlags.isPrivate() || addedFlags.isVolatile()) {
        debug("Added/removed static modifier or added private/volatile modifier --- affecting field usages");
        affectMemberUsages(context, changedClass.getReferenceID(), changedField, propagated);
        if (!changedField.isPrivate()) {
          if (addedFlags.isStatic()) {
            debug("Added static modifier --- affecting static member on-demand import usages");
            affectStaticMemberOnDemandUsages(context, changedClass.getReferenceID(), propagated);
          }
          else if (removedFlags.isStatic()) {
            debug("Removed static modifier --- affecting static field import usages");
            affectStaticMemberImportUsages(context, changedClass.getReferenceID(), changedField.getName(), propagated);
          }
        }
      }
      else {
        Predicate<Node<?, ?>> constraint = null;

        if (removedFlags.isPublic()) {
          debug("Removed public modifier, affecting field usages with appropriate constraint");
          if (addedFlags.isProtected()) {
            constraint = new InheritanceConstraint(future, changedClass);
          }
          else {
            constraint = new PackageConstraint(changedClass.getPackageName());
          }
          affectMemberUsages(context, changedClass.getReferenceID(), changedField, propagated, constraint);
        }
        else if (removedFlags.isProtected() && diff.accessRestricted()){
          debug("Removed protected modifier and the field became less accessible, affecting field usages with package constraint");
          constraint = new PackageConstraint(changedClass.getPackageName());
          affectMemberUsages(context, changedClass.getReferenceID(), changedField, propagated, constraint);
        }

        if (addedFlags.isFinal()) {
          debug("Added final modifier --- affecting field assign usages");
          affectUsages(context, "field assign", flat(asIterable(changedClass.getReferenceID()), propagated), id -> changedField.createAssignUsage(id.getNodeName()), constraint);
        }

      }
    }

    Difference.Specifier<ElementAnnotation, ElementAnnotation.Diff> annotationsDiff = diff.annotations();
    if (!annotationsDiff.unchanged()) {
      EnumSet<AnnotationChangesTracker.Recompile> toRecompile = EnumSet.noneOf(AnnotationChangesTracker.Recompile.class);
      for (AnnotationChangesTracker tracker : ourAnnotationChangeTrackers) {
        if (toRecompile.containsAll(AnnotationChangesTracker.RECOMPILE_ALL)) {
          break;
        }
        Set<AnnotationChangesTracker.Recompile> result = tracker.fieldAnnotationsChanged(changedField, annotationsDiff);
        if (result.contains(AnnotationChangesTracker.Recompile.USAGES)) {
          debug("Extension ", tracker.getClass().getName(), " requested recompilation because of changes in annotations list --- affecting field usages");
        }
        if (result.contains(AnnotationChangesTracker.Recompile.SUBCLASSES)) {
          debug("Extension ", tracker.getClass().getName(), " requested recompilation because of changes in field annotations list --- affecting subclasses");
        }
        toRecompile.addAll(result);
      }
      if (toRecompile.contains(AnnotationChangesTracker.Recompile.USAGES)) {
        affectMemberUsages(context, changedClass.getReferenceID(), changedField, propagated);
      }
      if (toRecompile.contains(AnnotationChangesTracker.Recompile.SUBCLASSES)) {
        affectSubclasses(context, future, changedClass.getReferenceID(), false);
      }
    }

    return true;
  }
@Override
  public boolean processChangedField(DifferentiateContext context, Difference.Change<JvmClass, JvmClass.Diff> clsChange, Difference.Change<JvmField, JvmField.Diff> fieldChange, Utils future, Utils present)