001package daikon;
002
003import java.util.logging.Logger;
004import org.checkerframework.checker.lock.qual.GuardSatisfied;
005import org.checkerframework.dataflow.qual.Pure;
006
007// Internally, we use the names "array[]", "array[]-element", and
008// "array[]-indexn".  These may be different depending on the programming
009// language; for instance, C uses "*array" in place of "array[]-element".
010
011/**
012 * Represents the comparability of variables, including methods to determine if two
013 * VarComparabilities are comparable. VarComparability types have two formats: implicit and none.
014 *
015 * <p>A VarComparabilityImplicit is an arbitrary string, and comparisons succeed exactly if the two
016 * VarComparabilitys are identical.
017 *
018 * <p>VarComparabilityNone means no comparability information was provided.
019 */
020public abstract class VarComparability {
021
022  /** Debug tracer. */
023  public static final Logger debug = Logger.getLogger("daikon.VarComparability");
024
025  public static final int NONE = 0;
026  public static final int IMPLICIT = 1;
027
028  /**
029   * Create a VarComparability representing the given arguments with respect to a variable.
030   *
031   * @param format the type of comparability, either NONE or IMPLICIT
032   * @param rep a regular expression indicating how to match. The form is "(a)[b][c]..." where each
033   *     variable is string (or number) that is a UID for a basic type. a is the type of the
034   *     element, b is the type of the first index, c the type of the second, etc. Index variables
035   *     only apply if this is an array.
036   * @param vartype the declared type of the variable
037   */
038  public static VarComparability parse(int format, String rep, ProglangType vartype) {
039    if (format == NONE) {
040      return VarComparabilityNone.parse(rep, vartype);
041    } else if (format == IMPLICIT) {
042      return VarComparabilityImplicit.parse(rep, vartype);
043    } else {
044      throw new IllegalArgumentException(
045          "bad format argument " + format + " should have been in {0, 1, 2}");
046    }
047  }
048
049  /**
050   * Create a VarComparability based on comparabilities of indices.
051   *
052   * @return a new comparability that is an array with the same dimensionality and indices as given,
053   *     but with a different element type
054   * @param elemTypeName the new type of the elements of return value
055   * @param old the varcomparability that this is derived from; has the same indices as this
056   */
057  public static VarComparability makeComparabilitySameIndices(
058      String elemTypeName, VarComparability old) {
059    if (old instanceof VarComparabilityNone) {
060      return VarComparabilityNone.it;
061    } else {
062      throw new Error("makeComparabilitySameIndices not implemented for implicit comparables");
063    }
064  }
065
066  public static VarComparability makeAlias(VarInfo vi) {
067    return vi.comparability.makeAlias();
068  }
069
070  public abstract VarComparability makeAlias();
071
072  public abstract VarComparability elementType(@GuardSatisfied VarComparability this);
073
074  public abstract VarComparability indexType(@GuardSatisfied VarComparability this, int dim);
075
076  /** Return the comparability for the length of this string* */
077  public abstract VarComparability string_length_type();
078
079  /** Returns true if this is comparable to everything else. */
080  public abstract boolean alwaysComparable(@GuardSatisfied VarComparability this);
081
082  /** Returns whether two variables are comparable. */
083  @Pure
084  public static boolean comparable(VarInfo v1, VarInfo v2) {
085    return comparable(v1.comparability, v2.comparability);
086  }
087
088  /** Returns whether two comparabilities are comparable. */
089  @SuppressWarnings("all:purity") // Override the purity checker
090  @Pure
091  public static boolean comparable(
092      @GuardSatisfied VarComparability type1, @GuardSatisfied VarComparability type2) {
093
094    if (type1 != null && type2 != null && type1.getClass() != type2.getClass()) {
095      throw new Error(
096          String.format(
097              "Trying to compare VarComparabilities of different types: %s (%s) and %s (%s)",
098              type1.toString(), type1.getClass(), type2.toString(), type2.getClass()));
099    }
100
101    if (type1 instanceof VarComparabilityNone || type1 == null || type2 == null) {
102      return VarComparabilityNone.comparable(
103          (VarComparabilityNone) type1, (VarComparabilityNone) type2);
104    } else if (type1 instanceof VarComparabilityImplicit) {
105      return VarComparabilityImplicit.comparable(
106          (VarComparabilityImplicit) type1, (VarComparabilityImplicit) type2);
107    } else {
108      throw new Error("Unrecognized subtype of VarComparability: " + type1);
109    }
110  }
111
112  /**
113   * In general, if two items are comparable, they can be placed in the same equality set. This is
114   * not always true for some comparabilities (because they are not always transitive). They can
115   * override this method to provide the correct results.
116   */
117  public boolean equality_set_ok(
118      @GuardSatisfied VarComparability this, @GuardSatisfied VarComparability other) {
119    return comparable(this, other);
120  }
121}