001package daikon;
002
003import java.io.Serializable;
004import java.util.ArrayList;
005import java.util.List;
006import org.checkerframework.checker.lock.qual.GuardSatisfied;
007import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
008import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
009import org.checkerframework.checker.nullness.qual.Nullable;
010import org.checkerframework.dataflow.qual.Pure;
011import org.checkerframework.dataflow.qual.SideEffectFree;
012
013/**
014 * A VarComparabilityImplicit is an arbitrary integer, and comparisons succeed exactly if the two
015 * integers are equal, except that negative integers compare equal to everything. Alternately, for
016 * an array variable, a VarComparabilityImplicit may separately indicate comparabilities for the
017 * elements and indices.
018 *
019 * <pre>
020 * VarComparabilityImplicit ::= int
021 *                            | VarComparabilityImplicit "[" int "]"
022 * </pre>
023 *
024 * <p>This is called "implicit" because the comparability objects do not refer to one another or
025 * refer directly to variables; whether two variables are comparable depends on their comparability
026 * objects. Implicit comparability has the flavor of types in programming languages.
027 *
028 * <p>Soon, this will probably be modified to permit the group identifiers to be arbitrary strings
029 * (not containing square brackets) instead of arbitrary integers.
030 */
031public final class VarComparabilityImplicit extends VarComparability implements Serializable {
032  static final long serialVersionUID = 20020122L;
033
034  /** The number that indicates which comparable set the VarInfo belongs to. */
035  int base;
036
037  /** indexTypes[0] is comparability of the first index of this array. */
038  // null only for the "unknown" type??
039  VarComparabilityImplicit @Nullable [] indexTypes;
040
041  /** Indicates how many of the indices are in use; there may be more indices than this. */
042  int dimensions;
043
044  private @MonotonicNonNull VarComparabilityImplicit cached_element_type;
045
046  public static final VarComparabilityImplicit unknown = new VarComparabilityImplicit(-3, null, 0);
047
048  private VarComparabilityImplicit(
049      int base, VarComparabilityImplicit @Nullable [] indexTypes, int dimensions) {
050    this.base = base;
051    this.indexTypes = indexTypes;
052    this.dimensions = dimensions;
053  }
054
055  @Pure
056  @Override
057  public int hashCode(@GuardSatisfied VarComparabilityImplicit this) {
058    if (base < 0) {
059      // This is equals() to everything
060      return -1;
061    }
062    if (dimensions > 0) {
063      return (indexType(dimensions - 1).hashCode() << 4) ^ elementType().hashCode();
064    }
065    return base;
066  }
067
068  @EnsuresNonNullIf(result = true, expression = "#1")
069  @Pure
070  @Override
071  public boolean equals(
072      @GuardSatisfied VarComparabilityImplicit this, @GuardSatisfied @Nullable Object o) {
073    if (!(o instanceof VarComparabilityImplicit)) {
074      return false;
075    }
076    return equalsVarComparabilityImplicit((VarComparabilityImplicit) o);
077  }
078
079  @EnsuresNonNullIf(result = true, expression = "#1")
080  @Pure
081  public boolean equalsVarComparabilityImplicit(
082      @GuardSatisfied VarComparabilityImplicit this, @GuardSatisfied VarComparabilityImplicit o) {
083    return equality_set_ok(o);
084  }
085
086  public boolean baseAlwayscomparable() {
087    return (base < 0);
088  }
089
090  @Pure
091  @Override
092  public boolean alwaysComparable(@GuardSatisfied VarComparabilityImplicit this) {
093    return (dimensions == 0) && (base < 0);
094  }
095
096  static VarComparabilityImplicit parse(String rep, @Nullable ProglangType vartype) {
097    // String rep_ = rep;          // for debugging
098
099    List<String> dim_reps = new ArrayList<>();
100    // handle array types
101    while (rep.endsWith("]")) {
102      int openpos = rep.lastIndexOf("[");
103      dim_reps.add(0, rep.substring(openpos + 1, rep.length() - 1));
104      rep = rep.substring(0, openpos);
105    }
106    int dims = dim_reps.size();
107    VarComparabilityImplicit[] index_types = new VarComparabilityImplicit[dims];
108    for (int i = 0; i < dims; i++) {
109      index_types[i] = parse(dim_reps.get(i), null);
110    }
111    try {
112      int base = Integer.parseInt(rep);
113      return new VarComparabilityImplicit(base, index_types, dims);
114    } catch (NumberFormatException e) {
115      throw new IllegalArgumentException(e);
116    }
117  }
118
119  @Override
120  public VarComparability makeAlias() {
121    return this;
122  }
123
124  @Override
125  public VarComparability elementType(@GuardSatisfied VarComparabilityImplicit this) {
126    if (cached_element_type == null) {
127      // When Ajax is modified to output non-atomic info for arrays, this
128      // check will no longer be necessary.
129      if (dimensions > 0) {
130        cached_element_type = new VarComparabilityImplicit(base, indexTypes, dimensions - 1);
131      } else {
132        // COMPARABILITY TEST
133        // System.out.println("Warning: taking element type of non-array comparability.");
134        cached_element_type = unknown;
135      }
136    }
137    return cached_element_type;
138  }
139
140  /**
141   * Determines the comparability of the length of this string. Currently always returns unknown,
142   * but it would be best if string lengths were only comparable with other string lengths (or
143   * perhaps nothing).
144   */
145  @Override
146  public VarComparability string_length_type() {
147    return unknown;
148  }
149
150  @Pure
151  @Override
152  public VarComparability indexType(@GuardSatisfied VarComparabilityImplicit this, int dim) {
153    // When Ajax is modified to output non-atomic info for arrays, this
154    // check will no longer be necessary.
155    if (dim < dimensions) {
156      assert indexTypes != null : "@AssumeAssertion(nullness): dependent: not the unknown type";
157      return indexTypes[dim];
158    } else {
159      return unknown;
160    }
161  }
162
163  @SuppressWarnings("all:purity") // Override the purity checker
164  @Pure
165  static boolean comparable(
166      @GuardSatisfied VarComparabilityImplicit type1,
167      @GuardSatisfied VarComparabilityImplicit type2) {
168    if (type1.alwaysComparable()) {
169      return true;
170    }
171    if (type2.alwaysComparable()) {
172      return true;
173    }
174    if ((type1.dimensions > 0) && (type2.dimensions > 0)) {
175      // Both are arrays
176      return (comparable(
177              type1.indexType(type1.dimensions - 1), type2.indexType(type2.dimensions - 1))
178          && comparable(type1.elementType(), type2.elementType()));
179    } else if ((type1.dimensions == 0) && (type2.dimensions == 0)) {
180      // Neither is an array.
181      return type1.base == type2.base;
182    } else {
183      // One array, one non-array, and the non-array isn't universally comparable.
184      assert type1.dimensions == 0 || type2.dimensions == 0;
185      return false;
186    }
187  }
188
189  /**
190   * Same as comparable, except that variables that are comparable to everything (negative
191   * comparability value) can't be included in the same equality set as those with positive values.
192   */
193  @Override
194  public boolean equality_set_ok(
195      @GuardSatisfied VarComparabilityImplicit this, @GuardSatisfied VarComparability other) {
196
197    VarComparabilityImplicit type1 = this;
198    VarComparabilityImplicit type2 = (VarComparabilityImplicit) other;
199
200    if ((type1.dimensions > 0) && (type2.dimensions > 0)) {
201      return (type1
202              .indexType(type1.dimensions - 1)
203              .equality_set_ok(type2.indexType(type2.dimensions - 1))
204          && type1.elementType().equality_set_ok(type2.elementType()));
205    }
206
207    if ((type1.dimensions == 0) && (type2.dimensions == 0)) {
208      return type1.base == type2.base;
209    }
210
211    // One array, one non-array
212    assert type1.dimensions == 0 || type2.dimensions == 0;
213    return false;
214  }
215
216  // for debugging
217  @SideEffectFree
218  @Override
219  public String toString(@GuardSatisfied VarComparabilityImplicit this) {
220    String result = "" + base;
221    for (int i = 0; i < dimensions; i++) {
222      result += "[" + indexType(i) + "]";
223    }
224    return result;
225  }
226}