001package daikon;
002
003import static daikon.FileIO.VarDefinition;
004
005import daikon.Quantify.QuantFlags;
006import daikon.Quantify.QuantifyReturn;
007import daikon.VarInfoName.Add;
008import daikon.VarInfoName.Elements;
009import daikon.VarInfoName.ElementsFinder;
010import daikon.VarInfoName.Field;
011import daikon.VarInfoName.FunctionOf;
012import daikon.VarInfoName.FunctionOfN;
013import daikon.VarInfoName.Poststate;
014import daikon.VarInfoName.Prestate;
015import daikon.VarInfoName.Simple;
016import daikon.VarInfoName.SizeOf;
017import daikon.VarInfoName.Slice;
018import daikon.VarInfoName.Subscript;
019import daikon.VarInfoName.TypeOf;
020import daikon.VarInfoName.Visitor;
021import daikon.chicory.DaikonVariableInfo;
022import daikon.derive.Derivation;
023import daikon.derive.binary.SequenceScalarSubscript;
024import daikon.derive.binary.SequenceScalarSubsequence;
025import daikon.derive.binary.SequenceSubsequence;
026import daikon.derive.ternary.SequenceScalarArbitrarySubsequence;
027import daikon.derive.unary.SequenceInitial;
028import daikon.derive.unary.SequenceLength;
029import daikon.derive.unary.SequenceMax;
030import daikon.derive.unary.SequenceMin;
031import daikon.derive.unary.SequenceSum;
032import daikon.inv.Equality;
033import daikon.inv.Invariant;
034import daikon.inv.OutputFormat;
035import daikon.inv.ValueSet;
036import daikon.inv.binary.twoScalar.IntGreaterEqual;
037import daikon.inv.binary.twoScalar.IntGreaterThan;
038import daikon.inv.binary.twoScalar.IntLessEqual;
039import daikon.inv.binary.twoScalar.IntLessThan;
040import daikon.inv.binary.twoScalar.LinearBinary;
041import daikon.inv.unary.scalar.NonZero;
042import daikon.inv.unary.scalar.SingleScalar;
043import daikon.inv.unary.sequence.EltNonZero;
044import daikon.inv.unary.sequence.SingleScalarSequence;
045import java.io.IOException;
046import java.io.ObjectInputStream;
047import java.io.Serializable;
048import java.util.ArrayList;
049import java.util.Arrays;
050import java.util.Comparator;
051import java.util.EnumSet;
052import java.util.HashMap;
053import java.util.HashSet;
054import java.util.LinkedHashSet;
055import java.util.List;
056import java.util.Map;
057import java.util.Set;
058import java.util.StringJoiner;
059import java.util.logging.Level;
060import java.util.logging.Logger;
061import org.checkerframework.checker.formatter.qual.FormatMethod;
062import org.checkerframework.checker.interning.qual.Interned;
063import org.checkerframework.checker.lock.qual.GuardSatisfied;
064import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
065import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
066import org.checkerframework.checker.nullness.qual.NonNull;
067import org.checkerframework.checker.nullness.qual.Nullable;
068import org.checkerframework.checker.signedness.qual.PolySigned;
069import org.checkerframework.dataflow.qual.Pure;
070import org.checkerframework.dataflow.qual.SideEffectFree;
071import org.plumelib.util.ArraysPlume;
072
073/**
074 * Represents information about a particular variable for a program point. This object doesn't hold
075 * the value of the variable at a particular step of the program point, but can get the value it
076 * holds when given a ValueTuple using the getValue() method. VarInfo also includes info about the
077 * variable's name, its declared type, its file representation type, its internal type, and its
078 * comparability.
079 */
080@SuppressWarnings({
081  "nullness", // nullness properties in this file are hairy; save for later
082  "interning"
083})
084public final @Interned class VarInfo implements Cloneable, Serializable {
085  static final long serialVersionUID = 20060815L;
086
087  /**
088   * If true, then variables are only considered comparable if they are declared with the same type.
089   * For example, java.util.List is not comparable to java.util.ArrayList and float is not
090   * comparable to double. This may miss valid invariants, but significant time can be saved and
091   * many variables with different declared types are not comparable (e.g., java.util.Date and
092   * java.util.ArrayList).
093   */
094  public static boolean dkconfig_declared_type_comparability = true;
095
096  /**
097   * If true, the treat static constants (such as MapQuick.GeoPoint.FACTOR) as fields within an
098   * object rather than as a single name. Not correct, but used to obtain compatibility with
099   * VarInfoName.
100   */
101  public static boolean dkconfig_constant_fields_simplify = true;
102
103  /** Debug missing vals. */
104  public static final Logger debugMissing = Logger.getLogger("daikon.VarInfo.missing");
105
106  // Ppts read from version 1 files never have their ppt set.  We should stop supporting version 1
107  // files so that the invariants of this class are better maintained.
108  /**
109   * The program point this variable is in. Is null until set by {@link PptTopLevel#init_vars} and
110   * {@link PptTopLevel#addVarInfos}.
111   */
112  public PptTopLevel ppt;
113
114  /**
115   * Name. Do not compare names of invariants from different program points, because two different
116   * program points could contain unrelated variables named "x".
117   */
118  private VarInfoName var_info_name; // interned
119
120  /**
121   * Name as specified in the program point declaration. VarInfoName sometimes changes this name as
122   * part of parsing so that VarInfoName.name() doesn't return the original name.
123   */
124  private @Interned String str_name; // interned
125
126  /** returns the interned name of the variable. */
127  @Pure
128  public @Interned String name(@GuardSatisfied VarInfo this) {
129    if (FileIO.new_decl_format) {
130      return str_name;
131    } else {
132      return var_info_name.name().intern(); // vin ok
133    }
134  }
135
136  /** Returns the original name of the variable from the program point declaration. */
137  public @Interned String str_name() {
138    return str_name;
139  }
140
141  /**
142   * Type as declared in the target program. This is seldom used within Daikon as these types vary
143   * with program language and the like. It's here more for information than anything else.
144   */
145  public ProglangType type; // interned (as are all ProglangType objects)
146
147  /**
148   * Type as written in the data trace file -- i.e., it is the source variable type mapped into the
149   * set of basic types recognized by Daikon. In particular, it includes boolean and hashcode
150   * (pointer). This is the type that is normally used when determining if an invariant is
151   * applicable to a variable. For example, the less-than invariant is not applicable to booleans or
152   * hashcodes, but is applicable to integers (of various sizes) and floats. (In the variable name,
153   * "rep" stands for "representation".)
154   */
155  public ProglangType file_rep_type; // interned (as are all ProglangType objects)
156
157  /**
158   * Type as internally stored by Daikon. It contains less information than file_rep_type (for
159   * example, boolean and hashcode are both stored as integers). (In the variable name, "rep" stands
160   * for "representation".)
161   *
162   * @see ProglangType#fileTypeToRepType()
163   */
164  public ProglangType rep_type; // interned (as are all ProglangType objects)
165
166  /** Comparability info. */
167  @SuppressWarnings("serial")
168  public VarComparability comparability;
169
170  /** Auxiliary info. */
171  public VarInfoAux aux;
172
173  /** The index in lists of VarInfo objects. */
174  public int varinfo_index;
175
176  /**
177   * The index in a ValueTuple (more generally, in a list of values). It can differ from
178   * varinfo_index due to constants (and possibly other factors). It is -1 iff is_static_constant or
179   * not yet set.
180   */
181  public int value_index;
182
183  /**
184   * Invariants: <br>
185   * is_static_constant == (value_index == -1);<br>
186   * is_static_constant == (static_constant_value != null).
187   */
188  // queried via isStaticConstant() method, so consider making this field private
189  public boolean is_static_constant;
190
191  /** Null if not statically constant. */
192  @SuppressWarnings("serial")
193  @Nullable @Interned Object static_constant_value;
194
195  /** Whether and how derived. Null if this is not derived. */
196  public @MonotonicNonNull Derivation derived;
197
198  // Various enums used for information about variables
199  public enum RefType {
200    POINTER,
201    OFFSET
202  };
203
204  public enum LangFlags {
205    PUBLIC,
206    PRIVATE,
207    PROTECTED,
208    STATIC,
209    FINAL,
210    SYNCHRONIZED,
211    VOLATILE,
212    TRANSIENT,
213    ANNOTATION,
214    ENUM
215  };
216
217  // These enums are intentionally duplicated in Chicory and other
218  // front-ends. These values are written into decl files, and as
219  // such, should stay constant between front-ends. They should not be
220  // changed without good reason; if you do change them, make sure to
221  // also change the corresponding constants in Daikon front ends!
222  // Adding a new enum is fine, but should also be done in all locations.
223  public enum VarKind {
224    FIELD,
225    FUNCTION,
226    ARRAY,
227    VARIABLE,
228    RETURN
229  };
230
231  public enum VarFlags {
232    IS_PARAM,
233    NO_DUPS,
234    NOT_ORDERED,
235    NO_SIZE,
236    NOMOD,
237    SYNTHETIC,
238    CLASSNAME,
239    TO_STRING,
240    NON_NULL,
241    IS_PROPERTY,
242    IS_ENUM,
243    IS_READONLY
244  };
245
246  public @Nullable RefType ref_type;
247  public VarKind var_kind;
248  public EnumSet<VarFlags> var_flags = EnumSet.noneOf(VarFlags.class);
249  public EnumSet<LangFlags> lang_flags = EnumSet.noneOf(LangFlags.class);
250
251  public VarDefinition vardef;
252
253  /**
254   * For documentation, see {@link #get_enclosing_var()}. Null if no variable encloses this one --
255   * that is, this is not a field of another variable, nor a "method call" like tostring or class.
256   */
257  public @Nullable VarInfo enclosing_var;
258
259  /** Number of array dimensions (0 or 1). */
260  public int arr_dims;
261
262  /**
263   * The arguments that were used to create this function application. Null if this variable is not
264   * a function application.
265   */
266  @SuppressWarnings("serial")
267  public @MonotonicNonNull List<VarInfo> function_args = null;
268
269  /** Parent program points in ppt hierarchy (optional) */
270  @SuppressWarnings("serial")
271  public List<VarParent> parents;
272
273  /**
274   * The relative name of this variable with respect to its enclosing variable. Field name for
275   * fields, method name for instance methods.
276   */
277  public @Nullable String relative_name = null;
278
279  /**
280   * Returns whether or not we have encountered to date any missing values due to array indices
281   * being out of bounds. This can happen with both subscripts and subsequences. Note that this
282   * becomes true as we are running, it cannot be set in advance without a first pass.
283   *
284   * <p>This is used as we are processing data to destroy any invariants that use this variable.
285   *
286   * @see Derivation#missingOutOfBounds()
287   */
288  public boolean missingOutOfBounds() {
289    if ((derived != null) && derived.missingOutOfBounds()) {
290      return true;
291    }
292    return false;
293  }
294
295  /**
296   * True if a missing/nonsensical value was ever observed for this variable. This starts out false
297   * and is set dynamically, while reading the trace file.
298   */
299  public boolean canBeMissing = false;
300
301  /**
302   * Which equality group this belongs to. Replaces equal_to. Never null after this is put inside
303   * equalitySet.
304   */
305  public Equality equalitySet;
306
307  /** Cached value for sequenceSize() */
308  private VarInfo sequenceSize;
309
310  /**
311   * non-null if this is an orig() variable.
312   *
313   * <p><b>Do not test equality! Only use its .name slot.</b>
314   */
315  public @Nullable VarInfo postState;
316
317  /**
318   * Throws an exception if this object is malformed. Requires that the VarInfo has been installed
319   * into a program point (the {@code ppt} field is set).
320   *
321   * @exception RuntimeException if representation invariant on this is broken
322   */
323  public void checkRep() {
324    checkRepNoPpt();
325    assert ppt != null;
326    assert 0 <= varinfo_index && varinfo_index < ppt.var_infos.length;
327    assert -1 <= value_index && value_index <= varinfo_index
328        : "" + this + " value_index=" + value_index + ", varinfo_index=" + varinfo_index;
329    assert is_static_constant == (value_index == -1);
330    assert is_static_constant || (static_constant_value == null);
331  }
332
333  /**
334   * Throws an exception if this object is malformed.
335   *
336   * <p>Does not require the {@code ppt} field to be set; can be called on VarInfos that have not
337   * been installed into a program point.
338   *
339   * @exception RuntimeException if representation invariant on this is broken
340   */
341  public void checkRepNoPpt() {
342    try {
343      assert var_info_name != null; // vin ok
344      assert var_info_name == var_info_name.intern(); // vin ok
345      assert type != null;
346      assert file_rep_type != null;
347      assert rep_type != null;
348      assert var_kind != null;
349      assert comparability != null; // anything else ??
350      assert (comparability.alwaysComparable()
351              || (((VarComparabilityImplicit) comparability).dimensions
352                  == file_rep_type.dimensions()))
353          : "Dimensions mismatch for "
354              + this
355              + ": "
356              + ((VarComparabilityImplicit) comparability).dimensions
357              + " "
358              + file_rep_type.dimensions();
359      if ((var_kind == VarKind.FIELD || var_kind == VarKind.ARRAY) && enclosing_var == null) {
360        throw new AssertionError(
361            "enclosing-var not specified for variable " + var_info_name + " of kind " + var_kind);
362      }
363    } catch (Throwable e) {
364      throw new AssertionError(
365          "checkRepNoPpt failed for variable "
366              + var_info_name
367              + (ppt == null ? "" : (" in " + ppt))
368              + ": "
369              + repr(),
370          e);
371    }
372  }
373
374  /** Returns whether or not rep_type is a legal type. */
375  static boolean legalRepType(ProglangType rep_type) {
376    return ((rep_type == ProglangType.INT)
377        || (rep_type == ProglangType.DOUBLE)
378        || (rep_type == ProglangType.STRING)
379        || (rep_type == ProglangType.INT_ARRAY)
380        || (rep_type == ProglangType.DOUBLE_ARRAY)
381        || (rep_type == ProglangType.STRING_ARRAY));
382  }
383
384  /** Returns whether or not constant_value is a legal constant. */
385  @EnsuresNonNullIf(result = false, expression = "#1")
386  static boolean legalConstant(@Nullable Object constant_value) {
387    return ((constant_value == null)
388        || (constant_value instanceof Long)
389        || (constant_value instanceof Double));
390  }
391
392  /**
393   * Returns whether or not file_rep_type is a legal file_rep_type. The file_rep_type matches
394   * rep_type except that it also allows the more detailed scalar types (HASHCODE, BOOLEAN, etc).
395   */
396  static boolean legalFileRepType(ProglangType file_rep_type) {
397    return (legalRepType(file_rep_type)
398        // The below types are converted into one of the rep types
399        // by ProglangType.fileTypeToRepType().
400        || (file_rep_type == ProglangType.HASHCODE)
401        || (file_rep_type == ProglangType.HASHCODE_ARRAY)
402        || ((file_rep_type.dimensions() <= 1) && file_rep_type.baseIsPrimitive()));
403  }
404
405  /**
406   * Create VarInfo from VarDefinition.
407   *
408   * <p>This does not create a fully initialized VarInfo. For example, its ppt and enclosing_var
409   * fields are not yet set. Callers need to do some work to complete the construction of the
410   * VarInfo.
411   */
412  public VarInfo(VarDefinition vardef) {
413    vardef.checkRep();
414
415    this.vardef = vardef;
416
417    // Create a VarInfoName from the external name.  This probably gets
418    // removed in the long run.
419    try {
420      var_info_name = VarInfoName.parse(vardef.name); // vin ok
421    } catch (Exception e) {
422      @SuppressWarnings("nullness") // error case, likely to crash later anyway
423      @NonNull VarInfoName vin = null;
424      var_info_name = vin;
425      System.out.printf("Warning: Can't parse %s as a VarInfoName", vardef.name);
426    }
427    str_name = vardef.name.intern();
428
429    // Copy info from vardef
430    var_kind = vardef.kind;
431    relative_name = vardef.relative_name;
432    ref_type = vardef.ref_type;
433    arr_dims = vardef.arr_dims;
434    comparability = vardef.comparability;
435    file_rep_type = vardef.rep_type;
436    type = vardef.declared_type;
437    var_flags = vardef.flags;
438    lang_flags = vardef.lang_flags;
439    parents = new ArrayList<VarParent>(vardef.parents);
440
441    // If a static constant value was specified, set it
442    if (vardef.static_constant_value != null) {
443      is_static_constant = true;
444      static_constant_value = vardef.static_constant_value;
445    } else {
446      is_static_constant = false;
447    }
448
449    // Create the rep_type from the file rep type
450    rep_type = file_rep_type.fileTypeToRepType();
451
452    // Create the VarInfoAux information
453    final List<String> auxstrs = new ArrayList<>();
454    if (var_flags.contains(VarFlags.IS_PARAM)) {
455      auxstrs.add(VarInfoAux.IS_PARAM + "=true");
456    }
457    if (var_flags.contains(VarFlags.NON_NULL)) {
458      auxstrs.add(VarInfoAux.IS_NON_NULL + "=true");
459    }
460    if (vardef.min_value != null) {
461      auxstrs.add(VarInfoAux.MINIMUM_VALUE + "=" + vardef.min_value);
462    }
463    if (vardef.max_value != null) {
464      auxstrs.add(VarInfoAux.MAXIMUM_VALUE + "=" + vardef.max_value);
465    }
466    if (vardef.min_length != null) {
467      auxstrs.add(VarInfoAux.MINIMUM_LENGTH + "=" + vardef.min_length);
468    }
469    if (vardef.max_length != null) {
470      auxstrs.add(VarInfoAux.MAXIMUM_LENGTH + "=" + vardef.max_length);
471    }
472    if (vardef.valid_values != null) {
473      auxstrs.add(VarInfoAux.VALID_VALUES + "=" + vardef.valid_values);
474    }
475    // Sadly, String.join is only available from Java 8:
476    // https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#join-java.lang.CharSequence-java.lang.Iterable-
477    final String auxstr = String.join(", ", auxstrs);
478
479    try {
480      aux = VarInfoAux.parse(auxstr);
481    } catch (Exception e) {
482      throw new RuntimeException("unexpected aux error", e);
483    }
484  }
485
486  /**
487   * Finishes defining the variable by relating it to other variables. This cannot be done when
488   * creating the variable because the other variables it is related to, may not yet exist.
489   * Variables are related to their enclosing variables (for fields, arrays, and functions) and to
490   * their parent variables in the PptHierarchy. RuntimeExceptions are thrown if any related
491   * variables do not exist.
492   */
493  public void relate_var() {
494
495    if (vardef == null) {
496      return;
497    }
498
499    // System.out.printf("enclosing var for %s is %s%n", str_name,
500    //                   vardef.enclosing_var);
501
502    // Find and set the enclosing variable (if any)
503    if (vardef.enclosing_var_name != null) {
504      enclosing_var = ppt.find_var_by_name(vardef.enclosing_var_name);
505      if (enclosing_var == null) {
506        for (int i = 0; i < ppt.var_infos.length; i++) {
507          System.out.printf("var = '%s'%n", ppt.var_infos[i]);
508        }
509        throw new RuntimeException(
510            String.format(
511                "enclosing variable '%s' for variable '%s' in ppt '%s' cannot be found",
512                vardef.enclosing_var_name, vardef.name, ppt.name));
513      }
514    }
515
516    // Convert vardef.function_args, which is a list of Strings,
517    // into this.function_args, which is a list of VarInfos.
518    if (vardef.function_args != null) {
519      function_args = new ArrayList<VarInfo>(vardef.function_args.size());
520      for (String varname : vardef.function_args) {
521        VarInfo vi = ppt.find_var_by_name(varname);
522        if (vi == null) {
523          throw new RuntimeException(
524              String.format(
525                  "function argument '%s' for variable '%s'  in ppt '%s' cannot be found",
526                  varname, vardef.name, ppt.name));
527        }
528        function_args.add(vi);
529      }
530    }
531
532    // do something appropriate with the ppt/var hierarchy.  It may be
533    // that  this is better done within PptRelation
534  }
535
536  /**
537   * Setup information normally specified in the declaration record for derived variables where the
538   * new variable is the result of applying a function to the other variables. Much of the
539   * information is inferred from (arbitrarily) the first argument to the function.
540   *
541   * <p>The parent_ppt field is set if each VarInfo in the derivation has the same parent. The
542   * parent_variable field is set if there is a parent_ppt and one or more of the bases has a
543   * non-default parent variable. The parent variable name is formed as function_name(arg1,arg2,...)
544   * where arg1, arg2, etc are the parent variable names of each of the arguments.
545   */
546  public void setup_derived_function(String name, VarInfo... bases) {
547
548    // Copy variable info from the base.
549    // Might some of these need to be overridden later, because they are just guesses?
550    VarInfo base = bases[0];
551    ref_type = null;
552    var_flags = base.var_flags.clone();
553    lang_flags = base.lang_flags.clone();
554    for (int ii = 1; ii < bases.length; ii++) {
555      var_flags.retainAll(bases[ii].var_flags);
556      lang_flags.retainAll(bases[ii].lang_flags);
557    }
558    enclosing_var = null;
559    arr_dims = base.arr_dims;
560    var_kind = VarKind.FUNCTION;
561    function_args = Arrays.asList(bases);
562
563    // Build the string name
564    List<String> arg_names = new ArrayList<>();
565    for (VarInfo vi : bases) {
566      arg_names.add(vi.name());
567    }
568    str_name = String.format("%s(%s)", name, String.join(",", arg_names)).intern();
569
570    // The parent ppt is the same as the base if each varinfo in the
571    // derivation has the same parent
572    for (VarParent bp : base.parents) {
573      int parent_relation_id = bp.parent_relation_id;
574      String parent_ppt = bp.parent_ppt;
575
576      if (allHaveRelation(parent_relation_id)) {
577        parents.add(new VarParent(parent_ppt, parent_relation_id, null));
578      }
579    }
580
581    // If there is a parent_ppt, determine the parent_variable name.
582    // If all of the argument names are the default, then the parent_variable
583    // is the default as well.  Otherwise, build up the name from the
584    // function name and the name of each arguments parent variable name.
585    // TWS: the code doesn't appear to handle the case where some are default and some aren't.
586    for (VarParent p : parents) {
587      boolean parent_vars_specified = false;
588      for (VarInfo vi : bases) {
589        for (VarParent vp : vi.parents) {
590          if (vp.parent_variable != null && vp.parent_relation_id == p.parent_relation_id) {
591            parent_vars_specified = true;
592          }
593        }
594      }
595      if (!parent_vars_specified) {
596        p.parent_variable = null;
597      } else {
598        StringJoiner args = new StringJoiner(",");
599        for (VarInfo vi : bases) {
600          boolean found = false;
601          for (VarParent vp : vi.parents) {
602            if (vp.parent_relation_id == p.parent_relation_id) {
603              args.add(vp.parent_variable);
604              found = true;
605            }
606          }
607          if (!found) {
608            args.add(vi.name());
609          }
610        }
611        p.parent_variable = String.format("%s(%s)", name, args.toString());
612      }
613    }
614  }
615
616  /**
617   * Setup information normally specified in the declaration record for derived variables where one
618   * of the variables is the base of the derivation. In general this information is inferred from
619   * the base variable of the derived variables. Note that parent_ppt is set if each VarInfo in the
620   * derivation has the same parent, but parent_variable is not set. This has to be set based on the
621   * particular derivation.
622   */
623  public void setup_derived_base(VarInfo base, @Nullable VarInfo... others) {
624
625    // Copy variable info from the base
626    ref_type = base.ref_type;
627    var_kind = base.var_kind;
628    var_flags = base.var_flags.clone();
629    lang_flags = base.lang_flags.clone();
630    enclosing_var = base.enclosing_var;
631    arr_dims = base.arr_dims;
632    function_args = base.function_args;
633
634    // The parent ppt is the same as the base if each varinfo in the
635    // derivation has the same parent
636    for (VarParent bp : base.parents) {
637      int parent_relation_id = bp.parent_relation_id;
638      String parent_ppt = bp.parent_ppt;
639
640      if (allHaveRelation(parent_relation_id, others)) {
641        parents.add(new VarParent(parent_ppt, parent_relation_id, null));
642      }
643    }
644  }
645
646  /** Returns true if all variables have a parent relation with the specified id. */
647  private static boolean allHaveRelation(int parent_relation_id, VarInfo... vs) {
648    for (VarInfo vi : vs) {
649      if (vi == null) {
650        continue;
651      }
652      boolean hasRelId = false;
653      for (VarParent vp : vi.parents) {
654        if (parent_relation_id == vp.parent_relation_id) {
655          hasRelId = true;
656          break;
657        }
658      }
659      if (!hasRelId) {
660        return false;
661      }
662    }
663    return true;
664  }
665
666  /**
667   * Create the specified VarInfo. The resulting VarInfo does not have its ppt field set.
668   *
669   * @param name the variable name
670   * @param type type as declared in the program point
671   * @param file_rep_type type as written in the data trace file
672   * @param comparability comparability info
673   * @param is_static_constant true if the variable always has the same, known value
674   * @param static_constant_value the static constant value, or null if not statically constant
675   * @param aux auxiliary info
676   */
677  private VarInfo(
678      VarInfoName name,
679      ProglangType type,
680      ProglangType file_rep_type,
681      VarComparability comparability,
682      boolean is_static_constant,
683      @Nullable @Interned Object static_constant_value,
684      VarInfoAux aux) {
685
686    assert name != null;
687    assert file_rep_type != null;
688    assert legalFileRepType(file_rep_type)
689        : "Unsupported representation type "
690            + file_rep_type.format()
691            + "/"
692            + file_rep_type.getClass()
693            + " "
694            + ProglangType.HASHCODE.getClass()
695            + " for variable "
696            + name;
697    assert type != null;
698    assert comparability != null;
699    // COMPARABILITY TEST
700    // assert
701    //   comparability.alwaysComparable() || ((VarComparabilityImplicit)comparability).dimensions ==
702    // file_rep_type.dimensions()
703    //   : "Types dimensions incompatibility: "
704    //     + type
705    //     + " vs. "
706    //     + file_rep_type;
707    assert aux != null;
708    assert legalConstant(static_constant_value)
709        : "unexpected constant class " + static_constant_value.getClass();
710
711    // Possibly the call to intern() isn't necessary; but it's safest to
712    // make the call to intern() rather than running the risk that a caller
713    // didn't.
714    this.var_info_name = name.intern(); // vin ok
715    this.type = type;
716    this.file_rep_type = file_rep_type;
717    this.rep_type = file_rep_type.fileTypeToRepType();
718    arr_dims = rep_type.isArray() ? 1 : 0;
719    this.comparability = comparability;
720    this.is_static_constant = is_static_constant;
721    this.static_constant_value = static_constant_value;
722    this.aux = aux;
723    this.parents = new ArrayList<VarParent>();
724
725    if (debug.isLoggable(Level.FINE)) {
726      debug.fine("Var " + name + " aux: " + aux);
727    }
728
729    // Indicates that these haven't yet been set to reasonable values.
730    value_index = -1;
731    varinfo_index = -1;
732
733    canBeMissing = false;
734  }
735
736  /** Create the specified VarInfo. */
737  public VarInfo(
738      String name,
739      ProglangType type,
740      ProglangType file_rep_type,
741      VarComparability comparability,
742      boolean is_static_constant,
743      @Nullable @Interned Object static_constant_value,
744      VarInfoAux aux) {
745    this(
746        VarInfoName.parse(name),
747        type,
748        file_rep_type,
749        comparability,
750        is_static_constant,
751        static_constant_value,
752        aux);
753    assert name != null;
754    this.str_name = name.intern();
755  }
756
757  /** Create the specified non-static VarInfo. */
758  private VarInfo(
759      VarInfoName name,
760      ProglangType type,
761      ProglangType file_rep_type,
762      VarComparability comparability,
763      VarInfoAux aux) {
764    this(name, type, file_rep_type, comparability, false, null, aux);
765  }
766
767  /** Create the specified non-static VarInfo. */
768  public VarInfo(
769      String name,
770      ProglangType type,
771      ProglangType file_rep_type,
772      VarComparability comparability,
773      VarInfoAux aux) {
774    this(name, type, file_rep_type, comparability, false, null, aux);
775    assert name != null;
776    this.str_name = name.intern();
777  }
778
779  /** Create a VarInfo with the same values as vi. */
780  public VarInfo(VarInfo vi) {
781    this(
782        vi.name(),
783        vi.type,
784        vi.file_rep_type,
785        vi.comparability,
786        vi.is_static_constant,
787        vi.static_constant_value,
788        vi.aux);
789    str_name = vi.str_name;
790    canBeMissing = vi.canBeMissing;
791    postState = vi.postState;
792    equalitySet = vi.equalitySet;
793    ref_type = vi.ref_type;
794    var_kind = vi.var_kind;
795    var_flags = vi.var_flags.clone();
796    lang_flags = vi.lang_flags.clone();
797    vardef = vi.vardef;
798    enclosing_var = vi.enclosing_var;
799    arr_dims = vi.arr_dims;
800    function_args = vi.function_args;
801    parents = new ArrayList<VarParent>();
802    for (VarParent parent : vi.parents) {
803      parents.add(
804          new VarParent(parent.parent_ppt, parent.parent_relation_id, parent.parent_variable));
805    }
806    relative_name = vi.relative_name;
807  }
808
809  // /** Creates and returns a copy of this. */
810  // // Default implementation to quiet Findbugs.
811  // @SuppressWarnings("interning")  // temporary?
812  // public VarInfo clone() throws CloneNotSupportedException {
813  //   return (VarInfo) super.clone();
814  // }
815
816  /**
817   * Create the prestate, or "orig()", version of the variable. Note that the returned value is not
818   * completely initialized. The caller is still responsible for setting some fields of it, such as
819   * enclosing_var.
820   */
821  public static VarInfo origVarInfo(VarInfo vi) {
822    // At an exit point, parameters are uninteresting, but orig(param) is not.
823    // So don't call orig(param) a parameter.
824    // VIN (below should be removed)
825    // VarInfoAux aux_nonparam =
826    //   vi.aux.setValue(VarInfoAux.IS_PARAM, VarInfoAux.FALSE);
827
828    VarInfo result;
829    if (FileIO.new_decl_format) {
830
831      // Build a Variable Definition from the poststate vardef
832      VarDefinition result_vardef = vi.vardef.copy();
833      result_vardef.name = vi.prestate_name();
834
835      // The only hierarchy relation for orig variables is to the enter
836      // ppt.  Remove any specified relations.
837      result_vardef.clear_parent_relation();
838
839      // Fix the VarDefinition enclosing variable, if any, to point to the
840      // prestate version.  This code does not affect the VarInfo yet, but
841      // the side-effected VarDefinition will be passed to "new VarInfo".
842      if (result_vardef.enclosing_var_name != null) {
843        assert vi.enclosing_var != null
844            : "@AssumeAssertion(nullness): dependent: result_vardef was copied from vi and their"
845                + " enclosing_var fields are the same";
846        result_vardef.enclosing_var_name = vi.enclosing_var.prestate_name();
847        assert result_vardef.enclosing_var_name != null : "" + result_vardef;
848      }
849
850      // Build the prestate VarInfo from the VarDefinition.
851      result = new VarInfo(result_vardef);
852
853      // Copy the missing flag from the original variable.  This is necessary
854      // for combined exit points which are built after processing is
855      // complete.  In most cases the missing flag will be set correctly
856      // by merging the missing flag from the numbered exit points.  But
857      // this will fail if the method terminates early each time the variable
858      // is missing.  A better fix would be to instrument early exits and
859      // merge them in as well, but this matches what we did previously.
860      result.canBeMissing = vi.canBeMissing;
861
862    } else {
863      VarInfoName newname = vi.var_info_name.applyPrestate(); // vin ok
864      result =
865          new VarInfo(newname, vi.type, vi.file_rep_type, vi.comparability.makeAlias(), vi.aux);
866      result.canBeMissing = vi.canBeMissing;
867      result.postState = vi;
868      result.equalitySet = vi.equalitySet;
869      result.arr_dims = vi.arr_dims;
870      result.str_name = vi.prestate_name();
871    }
872
873    // At an exit point, parameters are uninteresting, but orig(param) is not.
874    // So don't call orig(param) a parameter.
875    result.set_is_param(false);
876    return result;
877  }
878
879  /**
880   * Given an array of VarInfo objects, return an array of clones, where references to the originals
881   * have been modified into references to the new ones (so that the new set is self-consistent).
882   * The originals should not be modified by this operation.
883   */
884  public static VarInfo[] arrayclone_simple(VarInfo[] a_old) {
885    int len = a_old.length;
886    VarInfo[] a_new = new VarInfo[len];
887    for (int i = 0; i < len; i++) {
888      a_new[i] = new VarInfo(a_old[i]);
889      if (a_old[i].derived != null) {
890        assert a_new[i].derived != null;
891      }
892      a_new[i].varinfo_index = a_old[i].varinfo_index;
893      a_new[i].value_index = a_old[i].value_index;
894    }
895    return a_new;
896  }
897
898  /** Trims the collections used by this VarInfo. */
899  public void trimToSize() {
900    // if (derivees != null) { derivees.trimToSize(); }
901    // Derivation derived; probably can't be trimmed
902  }
903
904  /**
905   * Returns the name of the variable.
906   *
907   * @see #name
908   */
909  @SideEffectFree
910  @Override
911  public String toString(@GuardSatisfied VarInfo this) {
912    return name();
913  }
914
915  /**
916   * Returns the argument or, if it is null, {@code "null"}.
917   *
918   * @param o a reference
919   * @return the argument or, if it is null, {@code "null"}
920   */
921  private @PolySigned Object checkNull(
922      @GuardSatisfied VarInfo this, @Nullable @PolySigned Object o) {
923    return (o == null) ? (@PolySigned Object) "null" : o;
924  }
925
926  /**
927   * Returns a complete string description of the variable.
928   *
929   * @return a complete string description of the variable
930   */
931  public String repr() {
932    return "<VarInfo "
933        + var_info_name // vin ok
934        + ": "
935        + "type="
936        + type
937        + ",file_rep_type="
938        + file_rep_type
939        + ",rep_type="
940        + rep_type
941        + ",comparability="
942        + comparability
943        + ",value_index="
944        + value_index
945        + ",varinfo_index="
946        + varinfo_index
947        + ",is_static_constant="
948        + is_static_constant
949        + ",static_constant_value="
950        + static_constant_value
951        + ",derived="
952        + checkNull(derived)
953        + ",derivees="
954        + derivees()
955        + ",ppt="
956        // This method is only called for debugging, so let's
957        // protect ourselves from a mistake somewhere else, such as ppt being null.
958        // + ppt.name()
959        + ppt
960        + ",canBeMissing="
961        + canBeMissing
962        + (",equal_to=" + (equalitySet == null ? "null" : equalitySet.toString()))
963        + ",PostState="
964        + postState
965        + ",isCanonical()="
966        + isCanonical()
967        + ">";
968  }
969
970  /** Returns whether or not this variable is a static constant. */
971  @EnsuresNonNullIf(
972      result = true,
973      expression = {"constantValue()", "static_constant_value"})
974  @Pure
975  public boolean isStaticConstant() {
976    return is_static_constant;
977  }
978
979  /**
980   * Returns the static constant value of this variable. The variable must be a static constant. The
981   * result is non-null.
982   */
983  public Object constantValue() {
984    if (isStaticConstant()) {
985      assert static_constant_value != null;
986      return static_constant_value;
987    } else {
988      throw new Error("Variable " + name() + " is not constant");
989    }
990  }
991
992  /** Returns true if this is an "orig()" variable. */
993  @EnsuresNonNullIf(result = true, expression = "postState")
994  @Pure
995  public boolean isPrestate() {
996    return postState != null;
997  }
998
999  /** Returns true if this variable is derived from prestate variables. */
1000  @SuppressWarnings("all:not.deterministic") // nondeterminism does not affect result
1001  @Pure
1002  public boolean isPrestateDerived() {
1003    if (postState != null) {
1004      return true;
1005    }
1006    if (isDerived()) {
1007      for (VarInfo vi : derived.getBases()) {
1008        if (!vi.isPrestate()) {
1009          return false;
1010        }
1011      }
1012      return true;
1013    } else {
1014      return isPrestate();
1015    }
1016
1017    // return name.isAllPrestate();
1018  }
1019
1020  /** Returns true if this variable is a derived variable. */
1021  @EnsuresNonNullIf(result = true, expression = "this.derived")
1022  @Pure
1023  public boolean isDerived() {
1024    return (derived != null);
1025  }
1026
1027  /**
1028   * Returns the depth of derivation.
1029   *
1030   * @return the depth of derivation
1031   */
1032  public int derivedDepth() {
1033    if (derived == null) {
1034      return 0;
1035    } else {
1036      return derived.derivedDepth();
1037    }
1038  }
1039
1040  /**
1041   * Return all derived variables that build off this one.
1042   *
1043   * @return all derived variables that build off this one
1044   */
1045  public List<Derivation> derivees() {
1046    ArrayList<Derivation> result = new ArrayList<>();
1047    // This method is only called from the debugging routine 'repr()'.
1048    // So let's protect ourselves from a mistake somewhere else.
1049    if (ppt == null) {
1050      return result;
1051    }
1052    VarInfo[] vis = ppt.var_infos;
1053    for (int i = 0; i < vis.length; i++) {
1054      VarInfo vi = vis[i];
1055      Derivation der = vi.derived;
1056      if (der == null) {
1057        continue;
1058      }
1059      if (ArraysPlume.indexOf(der.getBases(), this) >= 0) {
1060        result.add(der);
1061      }
1062    }
1063    return result;
1064  }
1065
1066  /**
1067   * Returns a list of all of the basic (non-derived) variables that are used to make up this
1068   * variable. If this variable is not derived, it is just this variable. Otherwise it is all of the
1069   * the bases of this derivation.
1070   */
1071  public List<VarInfo> get_all_constituent_vars() {
1072    List<VarInfo> vars = new ArrayList<>();
1073    if (isDerived()) {
1074      for (VarInfo vi : derived.getBases()) {
1075        vars.addAll(vi.get_all_constituent_vars());
1076      }
1077    } else {
1078      vars.add(this);
1079    }
1080    return vars;
1081  }
1082
1083  /**
1084   * Returns a list of all of the simple names that make up this variable. this includes each field
1085   * and function name in the variable. If this variable is derived it includes the simple names
1086   * from each of its bases. For example, 'this.item.a' would return a list with 'this', 'item', and
1087   * 'a' and 'this.theArray[i]' would return 'this', 'theArray' and 'i'.
1088   */
1089  public List<String> get_all_simple_names() {
1090    assert FileIO.new_decl_format;
1091    List<String> names = new ArrayList<>();
1092    if (isDerived()) {
1093      for (VarInfo vi : derived.getBases()) {
1094        names.addAll(vi.get_all_simple_names());
1095      }
1096    } else {
1097      VarInfo start = (isPrestate() ? postState : this);
1098      for (VarInfo vi = start; vi != null; vi = vi.enclosing_var) {
1099        if (relative_name == null) {
1100          names.add(vi.name());
1101        } else {
1102          names.add(vi.relative_name);
1103        }
1104      }
1105    }
1106    return names;
1107  }
1108
1109  @Pure
1110  public boolean isClosure() {
1111    // This should eventually turn into
1112    //   return name.indexOf("closure(") != -1;
1113    // when I rename those variables to "closure(...)".
1114    return name().indexOf("~") != -1; // XXX
1115  }
1116
1117  /** Cached value for getDerivedParam(). */
1118  public @Nullable VarInfo derivedParamCached = null;
1119
1120  /** Cached value for isDerivedParam(). */
1121  // Boolean rather than boolean so we can use "null" to indicate "not yet set".
1122  public @MonotonicNonNull Boolean isDerivedParamCached = null;
1123
1124  /**
1125   * Returns true if this is a param according to aux info, or this is a front end derivation such
1126   * that one of its bases is a param. To figure this out, what we do is get all the param variables
1127   * at this's program point. Then we search in this's name to see if the name contains any of the
1128   * variables. We have to do this because we only have name info, and we assume that x and x.a are
1129   * related from the names alone.
1130   *
1131   * <p>Effects: Sets isDerivedParamCached and derivedParamCached to values the first time this
1132   * method is called. Subsequent calls use these cached values.
1133   */
1134  @EnsuresNonNullIf(result = true, expression = "getDerivedParam()")
1135  @SuppressWarnings("all:purity") // created object is not returned
1136  @Pure
1137  public boolean isDerivedParam() {
1138    if (isDerivedParamCached != null) {
1139      // System.out.printf("var %s is-derived-param = %b%n", name(),
1140      //                   isDerivedParamCached);
1141      return isDerivedParamCached.booleanValue();
1142    }
1143
1144    boolean result = false;
1145    if (isParam() && !isPrestate()) {
1146      result = true;
1147    }
1148
1149    if (!FileIO.new_decl_format) {
1150      // Determine the result from VarInfoName
1151      Set<VarInfo> paramVars = ppt.getParamVars();
1152      Set<VarInfoName> param_names = new LinkedHashSet<>();
1153      for (VarInfo vi : paramVars) {
1154        param_names.add(vi.var_info_name); // vin ok
1155      }
1156
1157      VarInfoName.Finder finder = new VarInfoName.Finder(param_names);
1158      Object baseMaybe = finder.getPart(var_info_name); // vin ok
1159      if (baseMaybe != null) {
1160        VarInfoName base = (VarInfoName) baseMaybe;
1161        derivedParamCached = this.ppt.find_var_by_name(base.name());
1162        if (Global.debugSuppressParam.isLoggable(Level.FINE)) {
1163          Global.debugSuppressParam.fine(name() + " is a derived param");
1164          Global.debugSuppressParam.fine("derived from " + base.name());
1165          Global.debugSuppressParam.fine(paramVars.toString());
1166        }
1167        result = true;
1168      }
1169    } else { // new format
1170      derivedParamCached = enclosing_param();
1171      if (derivedParamCached != null) {
1172        result = true;
1173      } else if (derived != null) {
1174        for (VarInfo vi : derived.getBases()) {
1175          derivedParamCached = vi.enclosing_param();
1176          if (derivedParamCached != null) {
1177            result = true;
1178            break;
1179          }
1180        }
1181      }
1182    }
1183
1184    // System.out.printf("var %s is-derived-param = %b%n", name(), result);
1185    isDerivedParamCached = result ? Boolean.TRUE : Boolean.FALSE;
1186    return result;
1187  }
1188
1189  /**
1190   * Returns the param variable that encloses this variable (if any). Returns null otherwise. Only
1191   * valid in the new decl format.
1192   */
1193  private @Nullable VarInfo enclosing_param() {
1194    // System.out.printf("Considering %s%n", this);
1195    assert FileIO.new_decl_format;
1196    if (isPrestate()) {
1197      return postState.enclosing_param();
1198    }
1199    for (VarInfo evi = this; evi != null; evi = evi.enclosing_var) {
1200      // System.out.printf("%s isParam=%b%n", evi, evi.isParam());
1201      if (evi.isParam()) {
1202        return evi;
1203      }
1204    }
1205    return null;
1206  }
1207
1208  /**
1209   * Return a VarInfo that has two properties: this is a derivation of it, and it is a parameter
1210   * variable. If this is a parameter, then this is returned. For example, "this" is always a
1211   * parameter. The return value of getDerivedParam for "this.a" (which is not a parameter) is
1212   * "this".
1213   *
1214   * <p>Effects: Sets isDerivedParamCached and derivedParamCached to values the first time this
1215   * method is called. Subsequent calls use these cached values.
1216   *
1217   * @return null if the above condition doesn't hold
1218   */
1219  @Pure
1220  public @Nullable VarInfo getDerivedParam() {
1221    if (isDerivedParamCached == null) {
1222      // fill in the cache
1223      isDerivedParam();
1224    }
1225    return derivedParamCached;
1226  }
1227
1228  private @MonotonicNonNull Boolean isDerivedParamAndUninterestingCached = null;
1229
1230  /**
1231   * Returns true if a given VarInfo is a parameter or derived from one in such a way that changes
1232   * to it wouldn't be visible to the method's caller. There are 3 such cases:
1233   *
1234   * <ul>
1235   *   <li>The variable is a pass-by-value parameter "p".
1236   *   <li>The variable is of the form "p.prop" where "prop" is an immutable property of an object,
1237   *       like its type, or (for a Java array) its size.
1238   *   <li>The variable is of the form "p.prop", and "p" has been modified to point to a different
1239   *       object. We assume "p" has been modified if we don't have an invariant "orig(p) == p".
1240   * </ul>
1241   *
1242   * In any case, the variable must have a postState VarInfoName, and equality invariants need to
1243   * have already been computed.
1244   */
1245  @SuppressWarnings("all:purity") // set cache field
1246  @Pure
1247  public boolean isDerivedParamAndUninteresting() {
1248    if (isDerivedParamAndUninterestingCached != null) {
1249      return isDerivedParamAndUninterestingCached.booleanValue();
1250    } else {
1251      isDerivedParamAndUninterestingCached =
1252          _isDerivedParamAndUninteresting() ? Boolean.TRUE : Boolean.FALSE;
1253      return isDerivedParamAndUninterestingCached.booleanValue();
1254    }
1255  }
1256
1257  /** Implementation of {@link #isDerivedParamAndUninteresting()}. */
1258  @Pure
1259  private boolean _isDerivedParamAndUninteresting() {
1260    if (PrintInvariants.debugFiltering.isLoggable(Level.FINE)) {
1261      PrintInvariants.debugFiltering.fine("isDPAU: name is " + name());
1262      PrintInvariants.debugFiltering.fine("  isPrestate is " + String.valueOf(isPrestate()));
1263    }
1264
1265    // Orig variables are not considered parameters.  We only check the
1266    // first variable in a derivation because that is the sequence variable in
1267    // sequence-subscript or sequence-subsequence derivations and we don't
1268    // care if the index into the sequence is prestate or not.
1269    if (isPrestate() || (isDerived() && derived.getBase(0).isPrestate())) {
1270      return false;
1271    }
1272
1273    if (isParam()) {
1274      PrintInvariants.debugFiltering.fine("  not interesting, IS_PARAM == true for " + name());
1275      return true;
1276    }
1277    if (Global.debugSuppressParam.isLoggable(Level.FINE)) {
1278      Global.debugSuppressParam.fine("Testing isDerivedParamAndUninteresting for: " + name());
1279      Global.debugSuppressParam.fine(aux.toString());
1280      Global.debugSuppressParam.fine("At ppt " + ppt.name());
1281    }
1282    if (isDerivedParam()) {
1283      // I am uninteresting if I'm a derived param from X and X's
1284      // type or X's size, because these things are boring if X
1285      // changes (the default for the rest of the code here), and
1286      // boring if X stays the same (because it's obviously true).
1287      if (!FileIO.new_decl_format) {
1288        if (var_info_name instanceof VarInfoName.TypeOf) { // vin ok
1289          VarInfoName base = ((VarInfoName.TypeOf) var_info_name).term; // vin ok
1290          VarInfo baseVar = ppt.find_var_by_name(base.name());
1291          if ((baseVar != null) && baseVar.isParam()) {
1292            Global.debugSuppressParam.fine("TypeOf returning true");
1293            PrintInvariants.debugFiltering.fine("  not interesting, first dpf case");
1294            return true;
1295          }
1296        }
1297        if (var_info_name instanceof VarInfoName.SizeOf) { // vin ok
1298          VarInfoName base = ((VarInfoName.SizeOf) var_info_name).get_term(); // vin ok
1299          VarInfo baseVar = ppt.find_var_by_name(base.name());
1300          if (baseVar != null && baseVar.isParam()) {
1301            Global.debugSuppressParam.fine("SizeOf returning true");
1302            PrintInvariants.debugFiltering.fine("  not interesting, second dpf case");
1303            return true;
1304          }
1305        }
1306      } else { // new decl format
1307        assert enclosing_var != null : this;
1308        assert enclosing_var != null : "@AssumeAssertion(nullness)";
1309
1310        // The class of a parameter can't change in the caller
1311        if (var_flags.contains(VarFlags.CLASSNAME) && enclosing_var.isParam()) {
1312          return true;
1313        }
1314
1315        // The size of a parameter can't change in the caller.  We shouldn't
1316        // have the shift==0 test, but need it to match the old code
1317        if (is_size() && enclosing_var.get_base_array_hashcode().isParam()) {
1318          if (((SequenceLength) derived).shift == 0) {
1319            return true;
1320          }
1321        }
1322      }
1323
1324      VarInfo base = getDerivedParam();
1325      assert base != null : "can't find base for " + name();
1326      // Actually we should be getting all the derivations that could
1327      // be params, and if any of them are uninteresting, this is
1328      // uninteresting.
1329
1330      // Remember that if this is derived from a true param, then this
1331      // is a param too, so we don't need to worry.  However, if this
1332      // is derived from a derivedParam, then we need to find all
1333      // derivation parents that could possibly fail under these
1334      // rules.  Right now, we just get the first one.
1335
1336      // So if x = Foo(this.y, p.y) and this hasn't changed then we
1337      // will be ignoring the fact that y has changed.
1338
1339      // Henceforth only interesting if it's true that base = orig(base)
1340      if (base.name().equals("this")) {
1341        return false;
1342      }
1343      Global.debugSuppressParam.fine("Base is " + base.name());
1344      VarInfo origBase = ppt.find_var_by_name(base.prestate_name());
1345      if (origBase == null) {
1346        Global.debugSuppressParam.fine("No orig variable for base, returning true ");
1347        PrintInvariants.debugFiltering.fine("  not interesting, no orig variable for base");
1348        return true; // There can't be an equal invariant without orig
1349      }
1350      if (base.isEqualTo(origBase)) {
1351        Global.debugSuppressParam.fine("Saw equality.  Derived worth printing.");
1352        return false;
1353      } else {
1354        Global.debugSuppressParam.fine("Didn't see equality in base, so uninteresting");
1355        PrintInvariants.debugFiltering.fine("  didn't see equality in base");
1356        return true;
1357      }
1358
1359    } else {
1360      Global.debugSuppressParam.fine("  Not a derived param.");
1361    }
1362    return false;
1363  }
1364
1365  /** Convenience methods that return information from the ValueTuple. */
1366  @Pure
1367  public int getModified(ValueTuple vt) {
1368    if (is_static_constant) {
1369      // return ValueTuple.STATIC_CONSTANT;
1370      return ValueTuple.MODIFIED;
1371    } else {
1372      return vt.getModified(value_index);
1373    }
1374  }
1375
1376  @Pure
1377  public boolean isUnmodified(ValueTuple vt) {
1378    return ValueTuple.modIsUnmodified(getModified(vt));
1379  }
1380
1381  @Pure
1382  public boolean isModified(ValueTuple vt) {
1383    return ValueTuple.modIsModified(getModified(vt));
1384  }
1385
1386  @Pure
1387  public boolean isMissingNonsensical(ValueTuple vt) {
1388    return ValueTuple.modIsMissingNonsensical(getModified(vt));
1389  }
1390
1391  @Pure
1392  public boolean isMissingFlow(ValueTuple vt) {
1393    return ValueTuple.modIsMissingFlow(getModified(vt));
1394  }
1395
1396  @Pure
1397  public boolean isMissing(ValueTuple vt) {
1398    return isMissingNonsensical(vt) || isMissingFlow(vt);
1399  }
1400
1401  /**
1402   * Get the value of this variable from a particular sample (ValueTuple).
1403   *
1404   * @param vt the ValueTuple from which to extract the value
1405   */
1406  public @Interned Object getValue(ValueTuple vt) {
1407    if (is_static_constant) {
1408      @SuppressWarnings("nullness") // derived: is_static_constant == true
1409      @NonNull Object result = static_constant_value;
1410      return result;
1411    } else {
1412      return vt.getValue(value_index);
1413    }
1414  }
1415
1416  /** Use of this method is discouraged. */
1417  public @Nullable @Interned Object getValueOrNull(ValueTuple vt) {
1418    if (is_static_constant) {
1419      return static_constant_value;
1420    } else {
1421      return vt.getValueOrNull(value_index);
1422    }
1423  }
1424
1425  /**
1426   * Returns the parent relation with the specified parent_relation_id; returns null if the relation
1427   * is not specified.
1428   */
1429  private @Nullable VarParent get_parent(int parent_relation_id) {
1430    for (VarParent vp : parents) {
1431      if (vp.parent_relation_id == parent_relation_id) {
1432        return vp;
1433      }
1434    }
1435    return null;
1436  }
1437
1438  /** Returns the parent variable name for the specified parent_relation_id. */
1439  private String parent_var_name(int parent_relation_id) {
1440    VarParent parent = get_parent(parent_relation_id);
1441    if (parent == null) {
1442      throw new IllegalArgumentException(
1443          "invalid parent_relation_id " + parent_relation_id + " for variable " + name());
1444    }
1445    return parent.parent_variable != null ? parent.parent_variable : name();
1446  }
1447
1448  /** Returns true iff the variable has a parent with the given parent_relation_id. */
1449  private boolean has_parent(int parent_relation_id) {
1450    return get_parent(parent_relation_id) != null;
1451  }
1452
1453  private @Nullable String parent_var(int parent_relation_id) {
1454    VarParent parent = get_parent(parent_relation_id);
1455    if (parent == null) {
1456      throw new IllegalArgumentException(
1457          "invalid parent_relation_id " + parent_relation_id + " for variable " + name());
1458    }
1459    return parent.parent_variable;
1460  }
1461
1462  /** Return the value of this long variable (as an integer) */
1463  public int getIndexValue(ValueTuple vt) {
1464    Object raw = getValue(vt);
1465    if (raw == null) {
1466      throw new Error(
1467          "getIndexValue: getValue returned null "
1468              + this.name()
1469              + " index="
1470              + this.varinfo_index
1471              + " vt="
1472              + vt);
1473    }
1474    return ((Long) raw).intValue();
1475  }
1476
1477  /** Return the value of this long variable (as a long) */
1478  public long getIntValue(ValueTuple vt) {
1479    Object raw = getValue(vt);
1480    if (raw == null) {
1481      throw new Error(
1482          "getIntValue: getValue returned null "
1483              + this.name()
1484              + " index="
1485              + this.varinfo_index
1486              + " vt="
1487              + vt);
1488    }
1489    return ((Long) raw).longValue();
1490  }
1491
1492  /** Return the value of an long[] variable. */
1493  public long[] getIntArrayValue(ValueTuple vt) {
1494    Object raw = getValue(vt);
1495    if (raw == null) {
1496      throw new Error(
1497          "getIntArrayValue: getValue returned null "
1498              + this.name()
1499              + " index="
1500              + this.varinfo_index
1501              + " vt="
1502              + vt);
1503    }
1504    return (long[]) raw;
1505  }
1506
1507  /** Return the value of a double variable. */
1508  public double getDoubleValue(ValueTuple vt) {
1509    Object raw = getValue(vt);
1510    if (raw == null) {
1511      throw new Error(
1512          "getDoubleValue: getValue returned null "
1513              + this.name()
1514              + " index="
1515              + this.varinfo_index
1516              + " vt="
1517              + vt);
1518    }
1519    return ((Double) raw).doubleValue();
1520  }
1521
1522  /** Return the value of a double[] variable. */
1523  public double[] getDoubleArrayValue(ValueTuple vt) {
1524    Object raw = getValue(vt);
1525    if (raw == null) {
1526      throw new Error(
1527          "getDoubleArrayValue: getValue returned null "
1528              + this.name()
1529              + " index="
1530              + this.varinfo_index
1531              + " vt="
1532              + vt);
1533    }
1534    return (double[]) raw;
1535  }
1536
1537  /** Return the value of a String variable. */
1538  public String getStringValue(ValueTuple vt) {
1539    return (String) getValue(vt);
1540  }
1541
1542  /** Reteurn the value of a String[] array variable. */
1543  public String[] getStringArrayValue(ValueTuple vt) {
1544    Object raw = getValue(vt);
1545    if (raw == null) {
1546      throw new Error(
1547          "getDoubleArrayValue: getValue returned null "
1548              + this.name()
1549              + " index="
1550              + this.varinfo_index
1551              + " vt="
1552              + vt);
1553    }
1554    return (String[]) raw;
1555  }
1556
1557  /**
1558   * Whether this VarInfo is the leader of its equality set.
1559   *
1560   * @return true if this VarInfo is the leader of its equality set
1561   */
1562  @Pure
1563  public boolean isCanonical() {
1564    if (equalitySet == null) {
1565      return true;
1566    }
1567    return (equalitySet.leader() == this);
1568  }
1569
1570  /** Canonical representative that's equal to this variable. */
1571  @Pure
1572  public VarInfo canonicalRep() {
1573    if (equalitySet == null) {
1574      System.out.println("equality sets = " + ppt.equality_sets_txt());
1575      assert equalitySet != null
1576          : "Variable " + name() + " in ppt " + ppt.name() + " index = " + varinfo_index;
1577    }
1578    return equalitySet.leader();
1579  }
1580
1581  /** Return true if this is a pointer or reference to another object. */
1582  @Pure
1583  public boolean is_reference() {
1584
1585    // This used to check to see if the item was a list and some other
1586    // odd things, but hashcode seems like the right check.
1587    return rep_type.isHashcode();
1588  }
1589
1590  /**
1591   * Returns the VarInfo for the sequence from which this was derived, or null if this wasn't
1592   * derived from a sequence. Only works for scalars.
1593   */
1594  public @Nullable VarInfo isDerivedSequenceMember() {
1595    if (derived == null) {
1596      return null;
1597    }
1598
1599    if (derived instanceof SequenceScalarSubscript) {
1600      SequenceScalarSubscript sss = (SequenceScalarSubscript) derived;
1601      return sss.seqvar();
1602    } else if (derived instanceof SequenceInitial) {
1603      SequenceInitial se = (SequenceInitial) derived;
1604      return se.seqvar();
1605    } else if (derived instanceof SequenceMax) {
1606      SequenceMax sm = (SequenceMax) derived;
1607      return sm.base;
1608    } else if (derived instanceof SequenceMin) {
1609      SequenceMin sm = (SequenceMin) derived;
1610      return sm.base;
1611    } else {
1612      return null;
1613    }
1614  }
1615
1616  @Pure
1617  public boolean isDerivedSequenceMinMaxSum() {
1618    return ((derived != null)
1619        && ((derived instanceof SequenceMax)
1620            || (derived instanceof SequenceMin)
1621            || (derived instanceof SequenceSum)));
1622  }
1623
1624  /**
1625   * Return the original sequence variable from which this derived sequence was derived. Only works
1626   * for sequences.
1627   */
1628  public @Nullable VarInfo isDerivedSubSequenceOf() {
1629
1630    if (derived == null) {
1631      return null;
1632    }
1633
1634    if (derived instanceof SequenceScalarSubsequence) {
1635      SequenceScalarSubsequence sss = (SequenceScalarSubsequence) derived;
1636      return sss.seqvar();
1637    } else if (derived instanceof SequenceScalarArbitrarySubsequence) {
1638      SequenceScalarArbitrarySubsequence ssas = (SequenceScalarArbitrarySubsequence) derived;
1639      return ssas.seqvar();
1640    } else {
1641      return null;
1642    }
1643  }
1644
1645  /** Returns the variable (if any) that represents the size of this sequence. */
1646  public @Nullable VarInfo sequenceSize() {
1647    if (sequenceSize != null) {
1648      return sequenceSize;
1649    }
1650    assert rep_type.isArray();
1651    // we know the size follows the variable itself in the list
1652    VarInfo[] vis = ppt.var_infos;
1653    for (int i = varinfo_index + 1; i < vis.length; i++) {
1654      VarInfo vi = vis[i];
1655      if ((vi.derived instanceof SequenceLength) && (((SequenceLength) vi.derived).base == this)) {
1656        sequenceSize = vi;
1657        return sequenceSize;
1658      }
1659    }
1660    // It is possible that this VarInfo never had its size derived,
1661    // since it looked something like this.ary[].field.  In this case,
1662    // we should return size(this.ary[]), since it was derived and
1663    // must be the same values.
1664    if (FileIO.new_decl_format) {
1665      VarInfo base = get_base_array();
1666      VarInfo size = ppt.find_var_by_name("size(" + base.name() + ")");
1667      return size;
1668    } else {
1669      VarInfoName search = this.var_info_name; // vin ok
1670      boolean pre = false;
1671      if (search instanceof VarInfoName.Prestate) {
1672        search = ((VarInfoName.Prestate) search).term;
1673        pre = true;
1674      }
1675      while (search instanceof VarInfoName.Field) {
1676        search = ((VarInfoName.Field) search).term;
1677      }
1678      if (pre) {
1679        search = search.applyPrestate();
1680      }
1681      search = search.applySize();
1682      VarInfo result = ppt.find_var_by_name(search.name());
1683      if (result != null) {
1684        return result;
1685        //        } else {
1686        //      System.out.println("Warning: Size variable " + search + " not found.");
1687        //      System.out.print("Variables: ");
1688        //      for (int i = 0; i<ppt.var_infos.length; i++) {
1689        //        VarInfo vi = ppt.var_infos[i];
1690        //        System.out.print(vi.name + " ");
1691        //      }
1692        //      System.out.println();
1693      }
1694    }
1695    //    throw new Error("Couldn't find size of " + name);
1696    return null;
1697  }
1698
1699  /**
1700   * Returns true if the type in the original program is integer. Should perhaps check
1701   * Daikon.check_program_types and behave differently depending on that.
1702   */
1703  @Pure
1704  public boolean isIndex() {
1705    return (file_rep_type == ProglangType.INT) && type.isIndex();
1706  }
1707
1708  /**
1709   * Returns true if this variable is an array.
1710   *
1711   * @return true if this variable is an array
1712   */
1713  @Pure
1714  public boolean is_array() {
1715    return arr_dims > 0;
1716  }
1717
1718  /**
1719   * Returns false if this variable expression is not legal ESC syntax, except for any necessary
1720   * quantifications (subscripting). We err on the side of returning true, for now.
1721   *
1722   * @return false if this variable expression is not legal ESC syntax, except for any necessary
1723   *     quantifications (subscripting)
1724   */
1725  @Pure
1726  public boolean isValidEscExpression() {
1727    // "myList.length" is invalid
1728    if (derived instanceof SequenceLength) {
1729      SequenceLength sl = (SequenceLength) derived;
1730      if (!sl.base.type.isArray()) {
1731        // VarInfo base = sl.base;
1732        // System.out.printf("%s is not an array%n", base);
1733        // System.out.printf("type = %s%n", base.type);
1734        return false;
1735      }
1736    }
1737
1738    // "myList[]" is invalid, as is myList[foo] (when myList is a list
1739    // of some sort and not an array)
1740    if (FileIO.new_decl_format) {
1741      for (VarInfo vi = this; vi != null; vi = vi.enclosing_var) {
1742        if (vi.file_rep_type.isArray() && !vi.type.isArray()) {
1743          return false;
1744        }
1745        if (vi.isDerived()) {
1746          VarInfo base = vi.derived.getBase(0);
1747          if (base.file_rep_type.isArray() && !base.type.isArray()) {
1748            return false;
1749          }
1750        }
1751      }
1752    } else {
1753      for (VarInfoName next : var_info_name.inOrderTraversal()) { // vin ok
1754        if (next instanceof VarInfoName.Elements) {
1755          VarInfoName.Elements elems = (VarInfoName.Elements) next;
1756          VarInfo seq = ppt.find_var_by_name(elems.term.name());
1757          if (!seq.type.isArray()) {
1758            return false;
1759          }
1760        }
1761      }
1762    }
1763
1764    return true;
1765  }
1766
1767  /**
1768   * Return true if invariants about this quantity are really properties of a pointer, but derived
1769   * variables can refer to properties of the thing pointed to. This distinction is important when
1770   * making logical statements about the object, because in the presence of side effects, the
1771   * pointed-to object can change even when the pointer doesn't. For instance, we might have "obj ==
1772   * orig(obj)", but "obj.color != orig(obj.color)". In such a case, isPointer() would be true of
1773   * obj, and for some forms of output we'd need to translate "obj == orig(obj)" into something like
1774   * "location(obj) == location(orig(obj))".
1775   */
1776  @Pure
1777  public boolean isPointer() {
1778    // This used to check whether the program type had a higher
1779    // dimension than the rep type, or if the rep type was integral
1780    // but the program type wasn't primitive. These rules worked
1781    // pretty well for Java, but not so well for C, where for instance
1782    // you might have rep_type = int and type = size_t.
1783
1784    return file_rep_type.isPointerFileRep();
1785  }
1786
1787  /**
1788   * A wrapper around VarInfoName.simplify_name() that also uses VarInfo information to guess
1789   * whether "obj" should logically be treated as just the hash code of "obj", rather than the whole
1790   * object.
1791   */
1792  public String simplifyFixup(String str) {
1793    if (isPointer()) {
1794      str = "(hash " + str + ")";
1795    }
1796    return str;
1797  }
1798
1799  public String simplifyFixedupName() {
1800    return simplifyFixup(simplify_name());
1801  }
1802
1803  ///////////////////////////////////////////////////////////////////////////
1804  /// Utility functions
1805  ///
1806
1807  // Where do these really belong?
1808
1809  /**
1810   * Given two variables I and J, indicate whether it is necessarily the case that i&le;j or i&ge;j.
1811   * The variables also each have a shift, so the test can really be something like (i+1)&le;(j-1).
1812   * The test is one of:
1813   *
1814   * <ul>
1815   *   <li>i + i_shift &le; j + j_shift (if test_lessequal)
1816   *   <li>i + i_shift &ge; j + j_shift (if !test_lessequal)
1817   * </ul>
1818   *
1819   * This is a dynamic check, and so must not be called while Daikon is inferencing.
1820   */
1821  public static boolean compare_vars(
1822      VarInfo vari, int vari_shift, VarInfo varj, int varj_shift, boolean test_lessequal) {
1823
1824    // System.out.printf("comparing variables %s and %s in ppt %s%n",
1825    //        vari.name(), varj.name(), vari.ppt.name());
1826    // Throwable stack = new Throwable("debug traceback");
1827    // stack.fillInStackTrace();
1828    // stack.printStackTrace();
1829
1830    assert !Daikon.isInferencing;
1831    // System.out.println("compare_vars(" + vari.name + ", " + vari_shift + ", "+ varj.name + ", " +
1832    // varj_shift + ", " + (test_lessequal?"<=":">=") + ")");
1833    if (vari == varj) {
1834      // same variable
1835      return test_lessequal ? (vari_shift <= varj_shift) : (vari_shift >= varj_shift);
1836    }
1837    // different variables
1838    boolean samePpt = (vari.ppt == varj.ppt);
1839    assert samePpt;
1840    PptSlice indices_ppt = vari.ppt.findSlice_unordered(vari, varj);
1841    if (indices_ppt == null) {
1842      return false;
1843    }
1844
1845    boolean vari_is_var1 = (vari == indices_ppt.var_infos[0]);
1846    LinearBinary lb = LinearBinary.find(indices_ppt);
1847    long index_vari_minus_seq = -2222; // valid only if lb != null
1848    if (lb != null) {
1849      if (!lb.enoughSamples()) {
1850        lb = null;
1851      } else if (lb.core.a != 1 || lb.core.b != -1) {
1852        // Do not attempt to deal with anything but y=x+b, aka x-y+b=0.
1853        lb = null;
1854      } else {
1855        // System.out.println("justified LinearBinary: " + lb.format());
1856        // lb.b is var2()-var1().
1857
1858        // a is 1 or -1, and the values are integers, so c must be an integer
1859        long c_int = (long) lb.core.c;
1860        assert lb.core.c == c_int;
1861        index_vari_minus_seq = (vari_is_var1 ? -c_int : c_int);
1862        index_vari_minus_seq += vari_shift - varj_shift;
1863      }
1864    }
1865
1866    boolean vari_lt = false;
1867    boolean vari_le = false;
1868    boolean vari_gt = false;
1869    boolean vari_ge = false;
1870    {
1871      IntLessEqual ile = IntLessEqual.find(indices_ppt);
1872      IntLessThan ilt = IntLessThan.find(indices_ppt);
1873      IntGreaterEqual ige = IntGreaterEqual.find(indices_ppt);
1874      IntGreaterThan igt = IntGreaterThan.find(indices_ppt);
1875      if (ile != null && !ile.enoughSamples()) {
1876        ile = null;
1877      }
1878      if (ilt != null && !ilt.enoughSamples()) {
1879        ilt = null;
1880      }
1881      if (ige != null && !ige.enoughSamples()) {
1882        ige = null;
1883      }
1884      if (igt != null && !igt.enoughSamples()) {
1885        igt = null;
1886      }
1887
1888      if (vari_is_var1) {
1889        vari_lt = ilt != null;
1890        vari_le = ile != null;
1891        vari_gt = igt != null;
1892        vari_ge = ige != null;
1893      } else {
1894        vari_lt = igt != null;
1895        vari_le = ige != null;
1896        vari_gt = ilt != null;
1897        vari_ge = ile != null;
1898      }
1899    }
1900
1901    // System.out.println("test_lessequal=" + test_lessequal
1902    //                    + ", vari_can_be_lt=" + vari_can_be_lt
1903    //                    + ", vari_can_be_eq=" + vari_can_be_eq
1904    //                    + ", vari_can_be_gt=" + vari_can_be_gt);
1905
1906    if (test_lessequal) {
1907      if (lb != null) {
1908        return (index_vari_minus_seq <= 0);
1909      } else {
1910        return ((vari_le && (vari_shift <= varj_shift))
1911            || (vari_lt && (vari_shift - 1 <= varj_shift)));
1912      }
1913    } else {
1914      if (lb != null) {
1915        return (index_vari_minus_seq >= 0);
1916      } else {
1917        return ((vari_ge && (vari_shift >= varj_shift))
1918            || (vari_gt && (vari_shift + 1 >= varj_shift)));
1919      }
1920    }
1921  }
1922
1923  // // takes an "orig()" var and gives a VarInfoName for a variable or
1924  // // expression in the post-state which is equal to this one.
1925  // public VarInfoName postStateEquivalent() {
1926  //   return otherStateEquivalent(true);
1927  // }
1928
1929  // takes a non-"orig()" var and gives a VarInfoName for a variable
1930  // or expression in the pre-state which is equal to this one.
1931  public @Nullable VarInfoName preStateEquivalent() {
1932    return otherStateEquivalent(false);
1933  }
1934
1935  /**
1936   * Return some variable in the other state (pre-state if this is post-state, or vice versa) that
1937   * equals this one, or null if no equal variable exists.
1938   */
1939  // This does *not* try the obvious thing of converting "foo" to
1940  // "orig(foo)"; it creates something new.  I need to clarify the
1941  // documentation.
1942  public @Nullable VarInfoName otherStateEquivalent(boolean post) {
1943
1944    assert !FileIO.new_decl_format;
1945
1946    // Below is equivalent to:
1947    // assert post == isPrestate();
1948    if (post != isPrestate()) {
1949      throw new Error(
1950          "Shouldn't happen (should it?): "
1951              + (post ? "post" : "pre")
1952              + "StateEquivalent("
1953              + name()
1954              + ")");
1955    }
1956
1957    {
1958      List<LinearBinary> lbs = LinearBinary.findAll(this);
1959      for (LinearBinary lb : lbs) {
1960        if (this.equals(lb.var2()) && (post != lb.var1().isPrestate())) {
1961
1962          // a * v1 + b * this + c = 0 or this == (-a/b) * v1 - c/b
1963          double a = lb.core.a, b = lb.core.b, c = lb.core.c;
1964          // if (a == 1) {  // match } for vim
1965          if (-a / b == 1) {
1966            // this = v1 - c/b
1967            // int add = (int) b;
1968            int add = (int) -c / (int) b;
1969            return lb.var1().var_info_name.applyAdd(add); // vin ok
1970          }
1971        }
1972
1973        if (this.equals(lb.var1()) && (post != lb.var2().isPrestate())) {
1974          // v2 = a * this + b <-- not true anymore
1975          // a * this + b * v2 + c == 0 or v2 == (-a/b) * this - c/b
1976          double a = lb.core.a, b = lb.core.b, c = lb.core.c;
1977          // if (a == 1) {  // match } for vim
1978          if (-a / b == 1) {
1979            // this = v2 + c/b
1980            // int add = - ((int) b);
1981            int add = (int) c / (int) b;
1982            return lb.var2().var_info_name.applyAdd(add); // vin ok
1983          }
1984        }
1985      }
1986
1987      // Should also try other exact invariants...
1988    }
1989
1990    // Can't find post-state equivalent.
1991    return null;
1992  }
1993
1994  /** Check if two VarInfos are truly (non guarded) equal to each other right now. */
1995  @Pure
1996  public boolean isEqualTo(VarInfo other) {
1997    assert equalitySet != null;
1998    return this.equalitySet == other.equalitySet;
1999  }
2000
2001  /** Debug tracer. */
2002  private static final Logger debug = Logger.getLogger("daikon.VarInfo");
2003
2004  /** Debug tracer for simplifying expressions. */
2005  private static final Logger debugSimplifyExpression =
2006      Logger.getLogger("daikon.VarInfo.simplifyExpression");
2007
2008  /** Enable assertions that would otherwise reduce run time performance. */
2009  private static final Logger debugEnableAssertions =
2010      Logger.getLogger("daikon.VarInfo.enableAssertions");
2011
2012  // This is problematic because it also enables some debugging output.
2013  // I need something that only enables assertions.
2014  // Slightly gross implementation, using a logger; but the command-line
2015  // options processing code already exists for it:
2016  // --dbg daikon.VarInfo
2017  public static boolean assertionsEnabled() {
2018    return debugEnableAssertions.isLoggable(Level.FINE);
2019  }
2020
2021  /**
2022   * Change the name of this VarInfo by side effect into a more simplified form, which is easier to
2023   * read on display. Don't call this during processing, as I think the system assumes that names
2024   * don't change over time (?).
2025   */
2026  public void simplify_expression() {
2027    if (debugSimplifyExpression.isLoggable(Level.FINE)) {
2028      debugSimplifyExpression.fine("** Simplify: " + name());
2029    }
2030
2031    if (!isDerived()) {
2032      if (debugSimplifyExpression.isLoggable(Level.FINE)) {
2033        debugSimplifyExpression.fine("** Punt because not derived variable");
2034      }
2035      return;
2036    }
2037
2038    // find a ...post(...)... expression to simplify
2039    VarInfoName.Poststate postexpr = null;
2040    for (VarInfoName node : new VarInfoName.InorderFlattener(var_info_name).nodes()) { // vin ok
2041      if (node instanceof VarInfoName.Poststate) {
2042        // Remove temporary var when bug is fixed.
2043        VarInfoName.Poststate tempNode = (VarInfoName.Poststate) node;
2044        postexpr = tempNode;
2045        // old code; reinstate when bug is fixed
2046        // postexpr = (VarInfoName.Poststate) node;
2047        break;
2048      }
2049    }
2050    if (postexpr == null) {
2051      if (debugSimplifyExpression.isLoggable(Level.FINE)) {
2052        debugSimplifyExpression.fine("** Punt because no post()");
2053      }
2054      return;
2055    }
2056
2057    // if we have post(...+k) rewrite as post(...)+k
2058    if (postexpr.term instanceof VarInfoName.Add) {
2059      VarInfoName.Add add = (VarInfoName.Add) postexpr.term;
2060      VarInfoName swapped = add.term.applyPoststate().applyAdd(add.amount);
2061      var_info_name =
2062          new VarInfoName.Replacer(postexpr, swapped)
2063              .replace(var_info_name)
2064              .intern(); // vin ok  // interning bugfix
2065      // start over
2066      simplify_expression();
2067      return;
2068    }
2069
2070    // Stop now if we don't want to replace post vars with equivalent orig
2071    // vars
2072    if (!PrintInvariants.dkconfig_remove_post_vars) {
2073      return;
2074    }
2075
2076    // [[ find the ppt context for the post() term ]] (I used to
2077    // search the expression for this, but upon further reflection,
2078    // there is only one EXIT point which could possibly be associated
2079    // with this VarInfo, so "this.ppt" must be correct.
2080    PptTopLevel post_context = this.ppt;
2081
2082    // see if the contents of the post(...) have an equivalent orig()
2083    // expression.
2084    VarInfo postvar = post_context.find_var_by_name(postexpr.term.name());
2085    if (postvar == null) {
2086      if (debugSimplifyExpression.isLoggable(Level.FINE)) {
2087        debugSimplifyExpression.fine("** Punt because no VarInfo for postvar " + postexpr.term);
2088      }
2089      return;
2090    }
2091    VarInfoName pre_expr = postvar.preStateEquivalent();
2092    if (pre_expr != null) {
2093      // strip off any orig() so we don't get orig(a[orig(i)])
2094      if (pre_expr instanceof VarInfoName.Prestate) {
2095        pre_expr = ((VarInfoName.Prestate) pre_expr).term;
2096      } else if (pre_expr instanceof VarInfoName.Add) {
2097        VarInfoName.Add add = (VarInfoName.Add) pre_expr;
2098        if (add.term instanceof VarInfoName.Prestate) {
2099          pre_expr = ((VarInfoName.Prestate) add.term).term.applyAdd(add.amount);
2100        }
2101      }
2102      var_info_name =
2103          new VarInfoName.Replacer(postexpr, pre_expr)
2104              .replace(var_info_name)
2105              .intern(); // vin ok  // interning bugfix
2106      if (debugSimplifyExpression.isLoggable(Level.FINE)) {
2107        debugSimplifyExpression.fine("** Replaced with: " + var_info_name); // vin ok
2108      }
2109    }
2110
2111    if (debugSimplifyExpression.isLoggable(Level.FINE)) {
2112      debugSimplifyExpression.fine("** Nothing to do (no state equlivalent)");
2113    }
2114  }
2115
2116  /**
2117   * Two variables are "compatible" if their declared types are castable and their comparabilities
2118   * are comparable. This is a reflexive relationship, because it calls
2119   * ProglangType.comparableOrSuperclassEitherWay. However, it is not transitive because it might
2120   * not hold for two children of a superclass, even though it would for each child and the
2121   * superclass.
2122   */
2123  public boolean compatible(VarInfo var2) {
2124    VarInfo var1 = this;
2125    // Can only compare in the same ppt because otherwise
2126    // comparability info may not make sense.
2127    boolean samePpt = (var1.ppt == var2.ppt);
2128    assert samePpt;
2129
2130    if (!comparableByType(var2)) {
2131      return false;
2132    }
2133
2134    if (!Daikon.ignore_comparability && !VarComparability.comparable(var1, var2)) {
2135      return false;
2136    }
2137
2138    return true;
2139  }
2140
2141  /**
2142   * Return true if this sequence variable's element type is compatible with the scalar variable.
2143   */
2144  public boolean eltsCompatible(VarInfo sclvar) {
2145    VarInfo seqvar = this;
2146    if (Daikon.check_program_types) {
2147      ProglangType elttype = seqvar.type.elementType();
2148      if (!elttype.comparableOrSuperclassEitherWay(sclvar.type)) {
2149        // System.out.printf("eltsCompatible: bad program types; elttype(%s)=%s, scltype(%s)=%s%n",
2150        //                   seqvar, elttype, sclvar, sclvar.type);
2151        return false;
2152      }
2153    }
2154    if (!Daikon.ignore_comparability) {
2155      if (!VarComparability.comparable(seqvar.comparability.elementType(), sclvar.comparability)) {
2156        // System.out.printf("eltsCompatible: eltcomp(%s;%s)=%s, sclcomp(%s)=%s%n",
2157        //                   seqvar, seqvar.comparability.elementType(),
2158        //                   seqvar.comparability.elementType(), sclvar, sclvar.comparability);
2159        return false;
2160      }
2161    }
2162    return true;
2163  }
2164
2165  /**
2166   * Without using comparability info, check that this is comparable to var2. This is a reflexive
2167   * relationship, because it calls ProglangType.comparableOrSuperclassEitherWay. However, it is not
2168   * transitive because it might not hold for two children of a superclass, even though it would for
2169   * each child and the superclass. Does not check comparabilities.
2170   */
2171  public boolean comparableByType(VarInfo var2) {
2172    VarInfo var1 = this;
2173
2174    // System.out.printf("comparableByType(%s, %s)%n", var1, var2);
2175
2176    // the check ensures that a scalar or string and elements of an array of the same type are
2177    // labelled as comparable
2178    if (Daikon.check_program_types
2179        && (var1.file_rep_type.isArray() && !var2.file_rep_type.isArray())) {
2180
2181      // System.out.printf("comparableByType: case 1 %s%n", var1.eltsCompatible(var2));
2182      if (var1.eltsCompatible(var2)) {
2183        return true;
2184      }
2185    }
2186
2187    // the check ensures that a scalar or string and elements of an array of the same type are
2188    // labelled as comparable
2189    if (Daikon.check_program_types
2190        && (!var1.file_rep_type.isArray() && var2.file_rep_type.isArray())) {
2191
2192      // System.out.printf("comparableByType: case 2 %s%n", var2.eltsCompatible(var1));
2193      if (var2.eltsCompatible(var1)) {
2194        return true;
2195      }
2196    }
2197
2198    if (Daikon.check_program_types && (var1.file_rep_type != var2.file_rep_type)) {
2199      // System.out.printf("comparableByType: case 4 return false%n");
2200      return false;
2201    }
2202
2203    // If the file rep types match then the variables are comparable unless
2204    // their dimensions are different.
2205    if (!dkconfig_declared_type_comparability) {
2206      if (var1.type.dimensions() != var2.type.dimensions()) {
2207        // debug_print_once ("types %s and %s are not comparable",
2208        //                    var1.type, var2.type);
2209        return false;
2210      }
2211      return true;
2212    }
2213
2214    if (Daikon.check_program_types && !var1.type.comparableOrSuperclassEitherWay(var2.type)) {
2215      // debug_print_once ("types %s and %s are not comparable",
2216      //                     var1.type, var2.type);
2217      return false;
2218    }
2219    // debug_print_once ("types %s and %s are comparable",
2220    //                  var1.type, var2.type);
2221
2222    // System.out.printf("comparableByType: fallthough return true%n");
2223    return true;
2224  }
2225
2226  /**
2227   * Without using comparability info, check that this is comparable to var2. This is a reflexive
2228   * and transitive relationship. Does not check comparabilities.
2229   *
2230   * @param var2 the variable to test comparability with
2231   * @return true if this is comparable to var2
2232   */
2233  public boolean comparableNWay(VarInfo var2) {
2234    VarInfo var1 = this;
2235    if (Daikon.check_program_types && !var1.type.comparableOrSuperclassOf(var2.type)) {
2236      return false;
2237    }
2238    if (Daikon.check_program_types && !var2.type.comparableOrSuperclassOf(var1.type)) {
2239      return false;
2240    }
2241    if (Daikon.check_program_types && (var1.file_rep_type != var2.file_rep_type)) {
2242      return false;
2243    }
2244    return true;
2245  }
2246
2247  /** Return true if this sequence's first index type is compatible with the scalar variable. */
2248  public boolean indexCompatible(VarInfo sclvar) {
2249    VarInfo seqvar = this;
2250    if (Daikon.check_program_types) {
2251      if (!seqvar.is_array() || !sclvar.isIndex()) {
2252        return false;
2253      }
2254    }
2255    if (!Daikon.ignore_comparability) {
2256      if (!VarComparability.comparable(seqvar.comparability.indexType(0), sclvar.comparability)) {
2257        return false;
2258      }
2259    }
2260    return true;
2261  }
2262
2263  // Interning is lost when an object is serialized and deserialized.
2264  // Manually re-intern any interned fields upon deserialization.
2265  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
2266    in.defaultReadObject();
2267    var_info_name = var_info_name.intern(); // vin ok
2268    str_name = str_name.intern();
2269
2270    for (VarParent parent : parents) {
2271      parent.parent_ppt = parent.parent_ppt.intern();
2272      if (parent.parent_variable != null) {
2273        parent.parent_variable = parent.parent_variable.intern();
2274      }
2275    }
2276
2277    if (relative_name != null) {
2278      relative_name = relative_name.intern();
2279    }
2280  }
2281
2282  // /**
2283  //  * It is <b>not</b> safe in general to compare based on VarInfoName
2284  //  * alone, because it is possible for two different program points to have
2285  //  * unrelated variables of the same name.
2286  //  */
2287  // public static class LexicalComparator implements Comparator<VarInfo> {
2288  //   @Pure
2289  //   public int compare(VarInfo vi1, VarInfo vi2) {
2290  //     VarInfoName name1 = vi1.name;
2291  //     VarInfoName name2 = vi2.name;
2292  //     return name1.compareTo(name2);
2293  //   }
2294  // }
2295
2296  // Is this property always guaranteed to be true?  It's placed in a
2297  // slice, but then might it get printed or treated as true?
2298  /**
2299   * Create a guarding predicate for this VarInfo, that is, an invariant that ensures that this
2300   * object is available for access to variables that reference it, such as fields. (The invariant
2301   * is placed in the appropriate slice.) Returns null if no guarding is needed.
2302   */
2303  // Adding a test against null is not quite right for C programs, where *p
2304  // could be nonsensical (uninitialized or freed) even when p is non-null.
2305  // But this is a decent approximation to start with.
2306  public @Nullable Invariant createGuardingPredicate(boolean install) {
2307    // Later for the array, make sure index in bounds
2308    if (!(type.isArray() || type.isObject())) {
2309      String message =
2310          String.format("Unexpected guarding based on %s with type %s%n", name(), type);
2311      System.err.print(message);
2312      throw new Error(message);
2313    }
2314
2315    // For now associating with the variable's PptSlice
2316    PptSlice slice = ppt.get_or_instantiate_slice(this);
2317
2318    Invariant result = Invariant.find(NonZero.class, slice);
2319
2320    // Check whether the predicate already exists
2321    if (result == null) {
2322      // If it doesn't, create a "fake" invariant, which should
2323      // never be printed.  Is it a good idea even to set
2324      // result.falsified to true?  We know it's true because
2325      // result's children were missing.  However, some forms of
2326      // filtering might remove it from slice.
2327      VarInfo[] vis = slice.var_infos;
2328      if (SingleScalar.valid_types_static(vis)) {
2329        result = NonZero.get_proto().instantiate(slice);
2330      } else if (SingleScalarSequence.valid_types_static(vis)) {
2331        result = EltNonZero.get_proto().instantiate(slice);
2332      } else {
2333        throw new Error("Bad VarInfos");
2334      }
2335      if (result == null) {
2336        // Return null if NonZero invariant is not applicable to this variable.
2337        return null;
2338      }
2339      result.isGuardingPredicate = true;
2340      // System.out.printf("Created a guarding predicate: %s at %s%n", result, slice);
2341      // new Error().printStackTrace(System.out);
2342      if (install) {
2343        slice.addInvariant(result);
2344      }
2345    }
2346
2347    return result;
2348  }
2349
2350  static Set<String> addVarMessages = new HashSet<>();
2351
2352  /**
2353   * Finds a list of variables that must be guarded for this VarInfo to be guaranteed to not be
2354   * missing. This list never includes "this", as it can never be null. The variables are returned
2355   * in the order in which their guarding prefixes are supposed to print.
2356   *
2357   * <p>For example, if this VarInfo is "a.b.c", then the guarding list consists of the variables
2358   * "a" and "a.b". If "a" is null or "a.b" is null, then "a.b.c" is missing (does not exist).
2359   */
2360  public List<VarInfo> getGuardingList() {
2361
2362    /**
2363     * The list returned by this visitor always includes the argument itself (if it is testable
2364     * against null; for example, derived variables are not). If the caller does not want the
2365     * argument to be in the list, the caller must must remove the argument.
2366     */
2367    // Inner class because it uses the "ppt" variable.
2368    // Basic structure of each visitor:
2369    //   If the argument should be guarded, recurse.
2370    //   If the argument is testable against null, add it to the result.
2371    // Recursing first arranges that the argument goes at the end,
2372    // after its subparts that need to be guarded.
2373
2374    class GuardingVisitor implements Visitor<List<VarInfo>> {
2375      boolean inPre = false;
2376
2377      private boolean shouldBeGuarded(VarInfoName viname) {
2378        // Not "shouldBeGuarded(ppt.findVar(viname))" because that
2379        // unnecessarily computes ppt.findVar(viname), if
2380        // dkconfig_guardNulls is "always".
2381        // System.out.printf("viname = %s, applyPreMaybe=%s, findvar=%s%n",
2382        //                   viname, applyPreMaybe(viname),
2383        //                   ppt.findVar(applyPreMaybe(viname)));
2384        if (Daikon.dkconfig_guardNulls == "always") { // interned
2385          return true;
2386        }
2387        if (Daikon.dkconfig_guardNulls == "missing") { // interned
2388          VarInfo vi = ppt.find_var_by_name(applyPreMaybe(viname).name());
2389          // Don't guard variables that don't exist.  This happends when
2390          // we incorrectly parse static variable package names as field names
2391          if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2392            Invariant.debugGuarding.fine(
2393                String.format(
2394                    "shouldBeGuarded(%s) [%s] %s %b",
2395                    viname, applyPreMaybe(viname), vi, ((vi == null) ? false : vi.canBeMissing)));
2396          }
2397          if (vi == null) {
2398            return false;
2399          }
2400          return vi.canBeMissing;
2401        }
2402        return false;
2403      }
2404
2405      @Override
2406      public List<VarInfo> visitSimple(Simple o) {
2407        List<VarInfo> result = new ArrayList<>();
2408        // No recursion:  no children
2409        if (!o.name.equals("this")) {
2410          result = addVar(result, o);
2411        }
2412        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2413          Invariant.debugGuarding.fine(String.format("visitSimple(%s) => %s", o.name(), result));
2414        }
2415        return result;
2416      }
2417
2418      @Override
2419      public List<VarInfo> visitSizeOf(SizeOf o) {
2420        List<VarInfo> result = new ArrayList<>();
2421        if (shouldBeGuarded(o)) {
2422          result.addAll(o.sequence.accept(this));
2423        }
2424        // No call to addVar:  derived variable
2425        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2426          Invariant.debugGuarding.fine(String.format("visitSizeOf(%s) => %s", o.name(), result));
2427        }
2428        return result;
2429      }
2430
2431      @Override
2432      public List<VarInfo> visitFunctionOf(FunctionOf o) {
2433        List<VarInfo> result = new ArrayList<>();
2434        if (shouldBeGuarded(o)) {
2435          result.addAll(o.argument.accept(this));
2436        }
2437        result = addVar(result, o);
2438        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2439          Invariant.debugGuarding.fine(
2440              String.format("visitFunctionOf(%s) => %s", o.name(), result));
2441        }
2442        return result;
2443      }
2444
2445      @Override
2446      public List<VarInfo> visitFunctionOfN(FunctionOfN o) {
2447        List<VarInfo> result = new ArrayList<>();
2448        if (shouldBeGuarded(o)) {
2449          for (VarInfoName arg : o.args) {
2450            result.addAll(arg.accept(this));
2451          }
2452        }
2453        result = addVar(result, o);
2454        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2455          Invariant.debugGuarding.fine(
2456              String.format("visitFunctionOfN(%s) => %s", o.name(), result));
2457        }
2458        return result;
2459      }
2460
2461      @Override
2462      public List<VarInfo> visitField(Field o) {
2463        List<VarInfo> result = new ArrayList<>();
2464        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2465          Invariant.debugGuarding.fine(
2466              String.format("visitField: shouldBeGuarded(%s) => %s", o.name(), shouldBeGuarded(o)));
2467        }
2468        if (shouldBeGuarded(o)) {
2469          result.addAll(o.term.accept(this));
2470        }
2471        result = addVar(result, o);
2472        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2473          Invariant.debugGuarding.fine(String.format("visitField(%s) => %s", o.name(), result));
2474        }
2475        return result;
2476      }
2477
2478      @Override
2479      public List<VarInfo> visitTypeOf(TypeOf o) {
2480        List<VarInfo> result = new ArrayList<>();
2481        if (shouldBeGuarded(o)) {
2482          result.addAll(o.term.accept(this));
2483        }
2484        // No call to addVar:  derived variable
2485        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2486          Invariant.debugGuarding.fine(String.format("visitTypeOf(%s) => %s", o.name(), result));
2487        }
2488        return result;
2489      }
2490
2491      @Override
2492      public List<VarInfo> visitPrestate(Prestate o) {
2493        assert inPre == false;
2494        inPre = true;
2495        List<VarInfo> result = o.term.accept(this);
2496        assert inPre == true;
2497        inPre = false;
2498        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2499          Invariant.debugGuarding.fine(String.format("visitPrestate(%s) => %s", o.name(), result));
2500        }
2501        return result;
2502      }
2503
2504      @Override
2505      public List<VarInfo> visitPoststate(Poststate o) {
2506        assert inPre == true;
2507        inPre = false;
2508        List<VarInfo> result = o.term.accept(this);
2509        assert inPre == false;
2510        inPre = true;
2511        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2512          Invariant.debugGuarding.fine(String.format("visitPostState(%s) => %s", o.name(), result));
2513        }
2514        return result;
2515      }
2516
2517      @Override
2518      public List<VarInfo> visitAdd(Add o) {
2519        List<VarInfo> result = new ArrayList<>();
2520        if (shouldBeGuarded(o)) {
2521          result.addAll(o.term.accept(this));
2522        }
2523        // No call to addVar:  derived variable
2524        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2525          Invariant.debugGuarding.fine(String.format("visitAdd(%s) => %s", o.name(), result));
2526        }
2527        return result;
2528      }
2529
2530      @Override
2531      public List<VarInfo> visitElements(Elements o) {
2532        List<VarInfo> result = new ArrayList<>();
2533        if (shouldBeGuarded(o)) {
2534          result.addAll(o.term.accept(this));
2535        }
2536        // No call to addVar:  derived variable
2537        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2538          Invariant.debugGuarding.fine(String.format("visitElements(%s) => %s", o.name(), result));
2539        }
2540        return result;
2541      }
2542
2543      @Override
2544      public List<VarInfo> visitSubscript(Subscript o) {
2545        List<VarInfo> result = new ArrayList<>();
2546        if (shouldBeGuarded(o)) {
2547          result.addAll(o.sequence.accept(this));
2548          result.addAll(o.index.accept(this));
2549        }
2550        result = addVar(result, o);
2551        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2552          Invariant.debugGuarding.fine(String.format("visitSubscript(%s) => %s", o.name(), result));
2553        }
2554        return result;
2555      }
2556
2557      @Override
2558      public List<VarInfo> visitSlice(Slice o) {
2559        List<VarInfo> result = new ArrayList<>();
2560        if (shouldBeGuarded(o)) {
2561          result.addAll(o.sequence.accept(this));
2562          if (o.i != null) {
2563            result.addAll(o.i.accept(this));
2564          }
2565          if (o.j != null) {
2566            result.addAll(o.j.accept(this));
2567          }
2568        }
2569        // No call to addVar:  derived variable
2570        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2571          Invariant.debugGuarding.fine(String.format("visitSlice(%s) => %s", o.name(), result));
2572        }
2573        return result;
2574      }
2575
2576      // Convert to prestate variable name if appropriate
2577      VarInfoName applyPreMaybe(VarInfoName vin) {
2578        if (inPre) {
2579          return vin.applyPrestate();
2580        } else {
2581          return vin;
2582        }
2583      }
2584
2585      private List<VarInfo> addVar(List<VarInfo> result, VarInfoName vin) {
2586        VarInfo vi = ppt.find_var_by_name(applyPreMaybe(vin).name());
2587        // vi could be null because some variable's prefix is not a
2588        // variable.  Example: for static variable "Class.staticvar",
2589        // "Class" is not a varible, even though for variable "a.b.c",
2590        // typically "a" and "a.b" are also variables.
2591        if (vi == null) {
2592          // String message =
2593          //     String.format(
2594          //         "getGuardingList(%s, %s): did not find variable %s [inpre=%s]",
2595          //         name(), ppt.name(), vin.name(), inPre);
2596          // // Only print the error message at most once per variable.
2597          // if (addVarMessages.add(vin.name())) {
2598          //   // For now, don't print at all:  it's generally innocuous
2599          //   // (class prefix of a static variable).
2600          //   // System.err.println(message);
2601          // }
2602          // // System.out.println("vars: " + ppt.varNames());
2603          // // System.out.flush();
2604          // // throw new Error(String.format(message));
2605          return result;
2606        } else {
2607          return addVarInfo(result, vi);
2608        }
2609      }
2610
2611      /**
2612       * Add the given variable to the result list. Does nothing if the variable is of primitive
2613       * type.
2614       */
2615      // Should this operate by side effect on a global variable?
2616      // (Then what is the type of the visitor; what does everything return?)
2617      private List<VarInfo> addVarInfo(List<VarInfo> result, VarInfo vi) {
2618        assert vi != null;
2619        assert !vi.isDerived() || vi.isDerived() : "addVar on derived variable: " + vi;
2620        // Don't guard primitives
2621        if ( // TODO: ***** make changes here *****
2622        // vi.file_rep_type.isScalar() &&
2623        !vi.type.isScalar()
2624        // (vi.type.isArray() || vi.type.isObject())
2625        ) {
2626          result.add(vi);
2627        } else {
2628          if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2629            Invariant.debugGuarding.fine(
2630                String.format(
2631                    "addVarInfo did not add %s: %s (%s) %s (%s)",
2632                    vi,
2633                    vi.file_rep_type.isScalar(),
2634                    vi.file_rep_type,
2635                    vi.type.isScalar(),
2636                    vi.type));
2637          }
2638        }
2639        if (Invariant.debugGuarding.isLoggable(Level.FINE)) {
2640          Invariant.debugGuarding.fine(String.format("addVarInfo(%s) => %s", vi, result));
2641        }
2642        return result;
2643      }
2644    } // end of class GuardingVisitor
2645
2646    if (!FileIO.new_decl_format) {
2647      List<VarInfo> result = var_info_name.accept(new GuardingVisitor()); // vin ok
2648      result.remove(ppt.find_var_by_name(var_info_name.name())); // vin ok
2649      assert !ArraysPlume.anyNull(result);
2650      return result;
2651    } else { // new format
2652      List<VarInfo> result = new ArrayList<>();
2653
2654      if (Daikon.dkconfig_guardNulls == "never") { // interned
2655        return result;
2656      }
2657
2658      // If this is never missing, nothing to guard
2659      if ((Daikon.dkconfig_guardNulls == "missing") // interned
2660          && !canBeMissing) {
2661        return result;
2662      }
2663
2664      // Create a list of variables to be guarded from the list of all
2665      // enclosing variables.
2666      for (VarInfo vi : get_all_enclosing_vars()) {
2667        // if (var_flags.contains(VarFlags.CLASSNAME)) {
2668        //   System.err.printf(
2669        //       "%s file_rep_type = %s, canbemissing = %b%n", vi, vi.file_rep_type,
2670        // vi.canBeMissing);
2671        // }
2672        if (!vi.file_rep_type.isHashcode()) {
2673          continue;
2674        }
2675        result.add(0, vi);
2676        if ((Daikon.dkconfig_guardNulls == "missing") // interned
2677            && !vi.canBeMissing) {
2678          break;
2679        }
2680      }
2681      return result;
2682    }
2683  }
2684
2685  /**
2686   * Returns a list of all of the variables that enclose this one. If this is derived, this includes
2687   * all of the enclosing variables of all of the bases.
2688   */
2689  public List<VarInfo> get_all_enclosing_vars() {
2690    List<VarInfo> result = new ArrayList<>();
2691    if (isDerived()) {
2692      for (VarInfo base : derived.getBases()) {
2693        result.addAll(base.get_all_enclosing_vars());
2694      }
2695    } else { // not derived
2696      for (VarInfo vi = this.enclosing_var; vi != null; vi = vi.enclosing_var) {
2697        result.add(vi);
2698      }
2699    }
2700    return result;
2701  }
2702
2703  /** Compare names by index. */
2704  public static final class IndexComparator implements Comparator<VarInfo>, Serializable {
2705    // This needs to be serializable because Equality invariants keep
2706    // a TreeSet of variables sorted by theInstance.
2707
2708    // We are Serializable, so we specify a version to allow changes to
2709    // method signatures without breaking serialization.  If you add or
2710    // remove fields, you should change this number to the current date.
2711    static final long serialVersionUID = 20050923L;
2712
2713    private IndexComparator() {}
2714
2715    @Pure
2716    @Override
2717    public int compare(VarInfo vi1, VarInfo vi2) {
2718      if (vi1.varinfo_index < vi2.varinfo_index) {
2719        return -1;
2720      } else if (vi1.varinfo_index == vi2.varinfo_index) {
2721        return 0;
2722      } else {
2723        return 1;
2724      }
2725    }
2726
2727    public static IndexComparator getInstance() {
2728      return theInstance;
2729    }
2730
2731    public static final IndexComparator theInstance = new IndexComparator();
2732  }
2733
2734  /**
2735   * Looks for an OBJECT ppt that corresponds to the type of this variable. Returns null if such a
2736   * point is not found.
2737   *
2738   * @param all_ppts map of all program points
2739   */
2740  public @Nullable PptTopLevel find_object_ppt(PptMap all_ppts) {
2741
2742    // Arrays don't have types
2743    if (is_array()) {
2744      return null;
2745    }
2746
2747    // build the name of the object ppt based on the variable type
2748    String type_str = type.base().replaceFirst("\\$", ".");
2749    PptName objname = new PptName(type_str, null, FileIO.object_suffix);
2750    return all_ppts.get(objname);
2751  }
2752
2753  /**
2754   * Class used to contain a pair of VarInfos and their sample count. Currently used for equality
2755   * set merging as a way to store pairs of equal variables. The variable with the smaller index is
2756   * always stored first.
2757   *
2758   * <p>Pairs are equal if both of their VarInfos are identical. Note that the content of the
2759   * VarInfos are not compared, only their pointer values.
2760   */
2761  public static class Pair {
2762
2763    public VarInfo v1;
2764    public VarInfo v2;
2765    public int samples;
2766
2767    public Pair(VarInfo v1, VarInfo v2, int samples) {
2768      if (v1.varinfo_index < v2.varinfo_index) {
2769        this.v1 = v1;
2770        this.v2 = v2;
2771      } else {
2772        this.v1 = v2;
2773        this.v2 = v1;
2774      }
2775      this.samples = samples;
2776    }
2777
2778    @EnsuresNonNullIf(result = true, expression = "#1")
2779    @Pure
2780    @Override
2781    public boolean equals(@GuardSatisfied Pair this, @GuardSatisfied @Nullable Object obj) {
2782      if (!(obj instanceof Pair)) {
2783        return false;
2784      }
2785
2786      Pair o = (Pair) obj;
2787      return (o.v1 == v1) && (o.v2 == v2);
2788    }
2789
2790    @Pure
2791    @Override
2792    public int hashCode(@GuardSatisfied Pair this) {
2793      return v1.hashCode() + v2.hashCode();
2794    }
2795
2796    @SideEffectFree
2797    @Override
2798    public String toString(@GuardSatisfied Pair this) {
2799      return v1.name() + " = " + v2.name();
2800    }
2801  }
2802
2803  /** Return the set of values that have been seen so far for this variable. */
2804  public ValueSet get_value_set() {
2805
2806    // Static constants don't have value sets, so we must make one
2807    if (is_static_constant) {
2808      ValueSet vs = ValueSet.factory(this);
2809      assert static_constant_value != null
2810          : "@AssumeAssertion(nullness): dependent: is_static_constant";
2811      vs.add(static_constant_value);
2812      return vs;
2813    }
2814
2815    return ppt.value_sets[value_index];
2816  }
2817
2818  public String get_value_info() {
2819    return name() + "- " + get_value_set().repr_short();
2820  }
2821
2822  /**
2823   * Returns the number of elements in the variable's equality set. Returns 1 if the equality
2824   * optimization is turned off.
2825   */
2826  public int get_equalitySet_size() {
2827    if (equalitySet == null) {
2828      return 1;
2829    } else {
2830      return equalitySet.size();
2831    }
2832  }
2833
2834  /**
2835   * Returns the vars_info in the variable's equality set. Returns a set with just itself if the
2836   * equality optimization is turned off.
2837   */
2838  public Set<VarInfo> get_equalitySet_vars() {
2839    if (equalitySet == null) {
2840      HashSet<VarInfo> set = new HashSet<>();
2841      set.add(this);
2842      return set;
2843    } else {
2844      return equalitySet.getVars();
2845    }
2846  }
2847
2848  /**
2849   * Returns the leader in the variable's equality set. Returns itself if the equality optimization
2850   * is turned off.
2851   */
2852  public VarInfo get_equalitySet_leader() {
2853    // if (equalitySet == null && VarInfo.use_equality_optimization == false) {  // match } for vim
2854    if (equalitySet == null) {
2855      return this;
2856    } else {
2857      return equalitySet.leader();
2858    }
2859  }
2860
2861  private static Set<String> out_strings = new LinkedHashSet<>();
2862
2863  /** If the message is new print it, otherwise discard it. */
2864  @FormatMethod
2865  static void debug_print_once(String format, @Nullable Object... args) {
2866    String msg = String.format(format, args);
2867    if (!out_strings.contains(msg)) {
2868      System.out.println(msg);
2869      out_strings.add(msg);
2870    }
2871  }
2872
2873  /** Returns whether or not this variable is a parameter. */
2874  @Pure
2875  public boolean isParam() {
2876    if (FileIO.new_decl_format) {
2877      return var_flags.contains(VarFlags.IS_PARAM);
2878    } else {
2879      return aux.isParam(); // VIN
2880    }
2881  }
2882
2883  /** Set this variable as a parameter. */
2884  public void set_is_param() {
2885    // System.out.printf("setting is_param for %s %n", name());
2886    if (FileIO.new_decl_format) {
2887      var_flags.add(VarFlags.IS_PARAM);
2888    }
2889    aux = aux.setValue(VarInfoAux.IS_PARAM, VarInfoAux.TRUE); // VIN
2890  }
2891
2892  /** Set whether or not this variable is a parameter. */
2893  public void set_is_param(boolean set) {
2894    if (set) {
2895      set_is_param();
2896    } else {
2897      if (FileIO.new_decl_format) {
2898        var_flags.remove(VarFlags.IS_PARAM);
2899      }
2900      aux = aux.setValue(VarInfoAux.IS_PARAM, VarInfoAux.FALSE); // VIN
2901    }
2902  }
2903
2904  /**
2905   * Adds a subscript (or sequence) to an array variable. This should really just just substitute
2906   * for '..', but the dots are currently removed for back compatability.
2907   */
2908  public String apply_subscript(String subscript) {
2909    if (FileIO.new_decl_format) {
2910      assert arr_dims == 1 : "Can't apply subscript to " + name();
2911      return name().replace("..", subscript);
2912    } else {
2913      assert name().contains("[]") : "Can't apply subscript to " + name();
2914      return apply_subscript(name(), subscript);
2915    }
2916  }
2917
2918  /**
2919   * Adds a subscript (or subsequence) to an array name. This should really just substitute for
2920   * '..', but the dots are currently removed for back compatibility.
2921   */
2922  public static String apply_subscript(String sequence, String subscript) {
2923    if (FileIO.new_decl_format) {
2924      return sequence.replace("[..]", "[" + subscript + "]");
2925    } else {
2926      return sequence.replace("[]", "[" + subscript + "]");
2927    }
2928  }
2929
2930  /**
2931   * For array variables, returns the variable that is a simple array. If this variable is a slice,
2932   * it returns the array variable that is being sliced. If this variable is a simple array itself,
2933   * returns this.
2934   */
2935  public VarInfo get_array_var() {
2936    assert file_rep_type.isArray();
2937    if (isDerived()) {
2938      return derived.get_array_var();
2939    } else {
2940      return this;
2941    }
2942  }
2943
2944  /**
2945   * Returns the VarInfo that represents the base array of this array. For example, if the array is
2946   * a[].b.c, returns a[].
2947   */
2948  @Pure
2949  public VarInfo get_base_array() {
2950    assert file_rep_type.isArray() : this;
2951    if (FileIO.new_decl_format) {
2952      VarInfo var = this;
2953      while (var.var_kind != VarKind.ARRAY) {
2954        if (var.enclosing_var == null) {
2955          // error condition; print some debugging output before assertion failure
2956          for (VarInfo vi = this; vi != null; vi = vi.enclosing_var) {
2957            System.out.printf("%s %s%n", vi, vi.var_kind);
2958          }
2959          assert var.enclosing_var != null : this + " " + var;
2960        }
2961        assert var.enclosing_var != null : "@AssumeAssertion(nullness): just tested";
2962        var = var.enclosing_var;
2963      }
2964      return var;
2965    } else {
2966      Elements elems = new ElementsFinder(var_info_name).elems(); // vin ok
2967      return ppt.find_var_by_name(elems.name());
2968    }
2969  }
2970
2971  /**
2972   * Returns the VarInfo that represents the hashcode of the base array of this array. For example,
2973   * if the array is a[].b.c, returns a. Returns null if there is no such variable.
2974   */
2975  @Pure
2976  public @Nullable VarInfo get_base_array_hashcode() {
2977    if (FileIO.new_decl_format) {
2978      return get_base_array().enclosing_var;
2979    } else {
2980      Elements elems = new ElementsFinder(var_info_name).elems(); // vin ok
2981      // System.out.printf("term.name() = %s%n", elems.term.name());
2982      return ppt.find_var_by_name(elems.term.name());
2983    }
2984  }
2985
2986  /** Returns the lower bound of the array or slice. */
2987  public Quantify.Term get_lower_bound() {
2988    assert file_rep_type.isArray() : "var " + name() + " rep " + file_rep_type;
2989    if (isDerived()) {
2990      return derived.get_lower_bound();
2991    } else {
2992      return new Quantify.Constant(0);
2993    }
2994  }
2995
2996  /** Returns the upper bound of the array or slice. */
2997  public Quantify.Term get_upper_bound() {
2998    assert file_rep_type.isArray();
2999    if (isDerived()) {
3000      return derived.get_upper_bound();
3001    } else {
3002      return new Quantify.Length(this, -1);
3003    }
3004  }
3005
3006  /**
3007   * Returns the length of this array. The array can be an array or a list. It cannot be a slice.
3008   */
3009  public Quantify.Term get_length() {
3010    assert file_rep_type.isArray() && !isDerived() : this;
3011    return new Quantify.Length(this, 0);
3012  }
3013
3014  /**
3015   * Updates any references to other variables that should be within this ppt by looking them up
3016   * within the ppt. Necessary if a variable is moved to a different program point or if cloned
3017   * variable is placed in a new program point (such as is done when combined exits are created).
3018   */
3019  public void update_after_moving_to_new_ppt() {
3020    if (enclosing_var != null) {
3021      // enclosing_var exists but is in the wrong ppt; update it
3022      enclosing_var = ppt.find_var_by_name(enclosing_var.name());
3023      assert enclosing_var != null;
3024    }
3025  }
3026
3027  /**
3028   * Temporary to let things compile now that name is private. Eventually this should be removed.
3029   */
3030  public VarInfoName get_VarInfoName() {
3031    return var_info_name; // vin ok
3032  }
3033
3034  private static boolean isStatic(String variable, String enclosing) {
3035    return !variable.startsWith(enclosing) || variable.charAt(enclosing.length()) != '.';
3036  }
3037
3038  // Map java objects to C# objects.
3039  private static final Map<String, String> csharp_types = new HashMap<>();
3040
3041  static {
3042    csharp_types.put("java.lang.String", "string");
3043    csharp_types.put("java.lang.String[]", "string[]");
3044    csharp_types.put("java.lang.Object", "object");
3045    csharp_types.put("java.lang.Object[]", "object[]");
3046    csharp_types.put("boolean", "bool");
3047  }
3048
3049  /** Transforms a Daikon type representation into a valid C# type. */
3050  public static String fix_csharp_type_name(String type) {
3051    if (csharp_types.containsKey(type)) {
3052      return csharp_types.get(type);
3053    } else {
3054      return type;
3055    }
3056  }
3057
3058  /**
3059   * If the variable is an array, returns a valid C# 'Select' statement representing the array. For
3060   * example, this.Array[].field would become this.Array.Select(x &rArr; x.field)
3061   *
3062   * <p>If the variable is not an array, csharp_name() is returned.
3063   */
3064  public String csharp_collection_string() {
3065    String[] split = csharp_array_split();
3066    if (split[1].equals("")) {
3067      return split[0];
3068    } else {
3069      return split[0] + ".Select(x => x" + split[1] + ")";
3070    }
3071  }
3072
3073  /**
3074   * Splits an array variable into the array and field portions. For example, if the variable
3075   * this.Array[].field then
3076   *
3077   * <pre>
3078   * result[0] = this.Array[]
3079   * result[1] = field
3080   * </pre>
3081   *
3082   * If the variable is not an array then
3083   *
3084   * <pre>
3085   * result[0] = csharp_name()
3086   * result[1] = ""
3087   * </pre>
3088   *
3089   * (there is no splitting).
3090   */
3091  public String[] csharp_array_split() {
3092    String[] results = new String[2];
3093
3094    if (!is_array()) {
3095      results[0] = csharp_name();
3096      results[1] = "";
3097      return results;
3098    }
3099
3100    String fields = "";
3101    VarInfo v = this;
3102    // Go backwards from v until we reach the array portion.
3103    while (v.var_kind != VarInfo.VarKind.ARRAY && v.enclosing_var != null) {
3104      if (v.relative_name != null) {
3105        if (v.relative_name.equals("GetType()")) {
3106          fields = "." + v.relative_name;
3107        } else {
3108          fields = "." + v.relative_name + fields;
3109        }
3110      }
3111      v = v.enclosing_var;
3112    }
3113
3114    results[0] = v.csharp_name();
3115    results[1] = fields;
3116    return results;
3117  }
3118
3119  /** Returns the name of this variable in the specified format. */
3120  public String name_using(OutputFormat format) {
3121    if (format == OutputFormat.DAIKON) {
3122      return name();
3123    }
3124    if (format == OutputFormat.SIMPLIFY) {
3125      return simplify_name();
3126    }
3127    if (format == OutputFormat.ESCJAVA) {
3128      return esc_name();
3129    }
3130    if (format == OutputFormat.JAVA) {
3131      return java_name();
3132    }
3133    if (format == OutputFormat.JML) {
3134      return jml_name();
3135    }
3136    if (format == OutputFormat.DBCJAVA) {
3137      return dbc_name();
3138    }
3139    if (format == OutputFormat.CSHARPCONTRACT) {
3140      return csharp_name();
3141    }
3142    throw new UnsupportedOperationException("Unknown format requested: " + format);
3143  }
3144
3145  /** Returns the name of this variable as a valid C# Code Contract. */
3146  @SideEffectFree
3147  public String csharp_name() {
3148    return csharp_name(null);
3149  }
3150
3151  /**
3152   * Returns the name of this variable as a valid C# Code Contract.
3153   *
3154   * @param index an an array index. Must be null for a non-array variable.
3155   * @return the name of this variable as a valid C# Code Contract
3156   */
3157  @SideEffectFree
3158  public String csharp_name(@Nullable String index) {
3159    if (index != null) {
3160      assert file_rep_type.isArray();
3161    }
3162
3163    if (postState != null) {
3164      return "Contract.OldValue(" + postState.csharp_name(index) + ")";
3165    }
3166
3167    if (derived != null) {
3168      return derived.csharp_name(index);
3169    }
3170
3171    switch (var_kind) {
3172      case FIELD:
3173        assert relative_name != null : this;
3174
3175        if (enclosing_var != null) {
3176          if (isStatic(str_name, enclosing_var.name())) {
3177            return str_name;
3178          }
3179          return enclosing_var.csharp_name(index) + "." + relative_name;
3180        }
3181
3182        return str_name;
3183
3184      case FUNCTION:
3185        if (var_flags.contains(VarFlags.TO_STRING)) {
3186          return enclosing_var.csharp_name(index);
3187        }
3188
3189        if (var_flags.contains(VarFlags.CLASSNAME)) {
3190          if (arr_dims > 0) {
3191            return csharp_collection_string();
3192          } else {
3193            return enclosing_var.csharp_name(index) + ".GetType()";
3194          }
3195        }
3196
3197        if (enclosing_var != null) {
3198
3199          if (isStatic(str_name, enclosing_var.name())) {
3200            String qualifiedName = str_name.substring(0, str_name.indexOf("("));
3201            return qualifiedName + "(" + enclosing_var.csharp_name(index) + ")";
3202          } else if (var_flags.contains(VarFlags.IS_PROPERTY)) {
3203            return enclosing_var.csharp_name(index) + "." + relative_name;
3204          } else {
3205            return enclosing_var.csharp_name(index) + "." + relative_name + "()";
3206          }
3207        } else {
3208          return str_name;
3209        }
3210
3211      case ARRAY:
3212        if (index == null) {
3213          return enclosing_var.csharp_name(null);
3214        }
3215        return enclosing_var.csharp_name(null) + "[" + index + "]";
3216
3217      case VARIABLE:
3218        assert enclosing_var == null;
3219        return str_name;
3220
3221      case RETURN:
3222        return "Contract.Result<" + fix_csharp_type_name(type.toString()) + ">()";
3223
3224      default:
3225        throw new Error("can't drop through switch statement.");
3226    }
3227  }
3228
3229  /** Returns the name in Java format. This is the same as JML. */
3230  public String java_name() {
3231    if (!FileIO.new_decl_format) {
3232      return var_info_name.java_name(this); // vin ok
3233    }
3234
3235    return jml_name();
3236  }
3237
3238  /** Returns the name in DBC format. This is the same as JML. */
3239  public String dbc_name() {
3240    if (!FileIO.new_decl_format) {
3241      return var_info_name.dbc_name(this); // vin ok
3242    }
3243
3244    return jml_name();
3245  }
3246
3247  /** Returns the name of this variable in ESC format. */
3248  @SideEffectFree
3249  public String esc_name() {
3250    if (!FileIO.new_decl_format) {
3251      return var_info_name.esc_name(); // vin ok
3252    }
3253
3254    return esc_name(null);
3255  }
3256
3257  /**
3258   * Returns the name of this variable in ESC format. If an index is specified, it is used as an
3259   * array index. It is an error to specify an index on a non-array variable.
3260   */
3261  @SideEffectFree
3262  public String esc_name(@Nullable String index) {
3263
3264    // System.out.printf("esc_name for %s, flags %s, enclosing-var %s "
3265    //                  + " poststate %s index %s rname %s ppt %s%n", str_name,
3266    //                    var_flags, enclosing_var, postState, index,
3267    //                    relative_name, ppt.name());
3268    if (index != null) {
3269      assert file_rep_type.isArray();
3270    }
3271
3272    // If this is an orig variable, use the post version to generate the name
3273    if (postState != null) {
3274      return "\\old(" + postState.esc_name(index) + ")";
3275    }
3276
3277    // If this is a derived variable, the derivations builds the name
3278    if (derived != null) {
3279      return derived.esc_name(index);
3280    }
3281
3282    // Build the name by processing back through all of the enclosing variables
3283    switch (var_kind) {
3284      case FIELD:
3285        assert relative_name != null : this;
3286        if (enclosing_var != null) {
3287          return enclosing_var.esc_name(index) + "." + relative_name;
3288        }
3289        return str_name;
3290      case FUNCTION:
3291        // function_args      assert function_args == null : "function args not implemented";
3292        if (var_flags.contains(VarFlags.CLASSNAME)) {
3293          return ("\\typeof(" + enclosing_var.esc_name(index) + ")");
3294        }
3295        if (var_flags.contains(VarFlags.TO_STRING)) {
3296          return enclosing_var.esc_name(index) + ".toString";
3297        }
3298        if (enclosing_var != null) {
3299          return enclosing_var.esc_name(index) + "." + relative_name + "()";
3300        }
3301        return str_name;
3302      case ARRAY:
3303        if (index == null) {
3304          return enclosing_var.esc_name(null) + "[]";
3305        }
3306        return enclosing_var.esc_name(null) + "[" + index + "]";
3307      case VARIABLE:
3308        assert enclosing_var == null;
3309        return str_name;
3310      case RETURN:
3311        return "\\result";
3312      default:
3313        throw new Error("can't drop through switch statement");
3314    }
3315  }
3316
3317  /** Returns the name of this variable in JML format. */
3318  @SideEffectFree
3319  public String jml_name() {
3320    if (!FileIO.new_decl_format) {
3321      return var_info_name.jml_name(this); // vin ok
3322    }
3323
3324    return jml_name(null);
3325  }
3326
3327  /**
3328   * Returns the name of this variable in JML format.
3329   *
3330   * @param index an array index. Must be null for a non-array variable.
3331   * @return the name of this variable in JML format
3332   */
3333  public String jml_name(@Nullable String index) {
3334
3335    if (index != null) {
3336      assert file_rep_type.isArray();
3337    }
3338
3339    // If this is an orig variable, use the post version to generate the name
3340    if (postState != null) {
3341      return "\\old(" + postState.jml_name(index) + ")";
3342    }
3343
3344    // If this is a derived variable, the derivations builds the name
3345    if (derived != null) {
3346      return derived.jml_name(index);
3347    }
3348
3349    // If this is an array of fields, collect the fields into a collection
3350    if ((arr_dims > 0) && (var_kind != VarKind.ARRAY) && !var_flags.contains(VarFlags.CLASSNAME)) {
3351      String field_name = relative_name;
3352      ;
3353      VarInfo vi = this.enclosing_var;
3354      for (; vi.var_kind != VarKind.ARRAY; vi = vi.enclosing_var) {
3355        field_name = vi.relative_name + "." + field_name;
3356      }
3357      return String.format("daikon.Quant.collectObject(%s, \"%s\")", vi.jml_name(), field_name);
3358    }
3359
3360    // Build the name by processing back through all of the enclosing variables
3361    switch (var_kind) {
3362      case FIELD:
3363        assert relative_name != null : this;
3364        if (enclosing_var != null) {
3365          return enclosing_var.jml_name(index) + "." + relative_name;
3366        }
3367        return str_name;
3368      case FUNCTION:
3369        // function_args      assert function_args == null : "function args not implemented";
3370        if (var_flags.contains(VarFlags.CLASSNAME)) {
3371          if (arr_dims > 0) {
3372            return String.format("daikon.Quant.typeArray(%s)", enclosing_var.jml_name(index));
3373          } else {
3374            return enclosing_var.jml_name(index) + DaikonVariableInfo.class_suffix;
3375          }
3376        }
3377        if (var_flags.contains(VarFlags.TO_STRING)) {
3378          return enclosing_var.jml_name(index) + ".toString()";
3379        }
3380        if (enclosing_var != null) {
3381          return enclosing_var.jml_name(index) + "." + relative_name + "()";
3382        }
3383        return str_name;
3384      case ARRAY:
3385        if (index == null) {
3386          return enclosing_var.jml_name(null);
3387        }
3388        return enclosing_var.jml_name(null) + "[" + index + "]";
3389      case VARIABLE:
3390        assert enclosing_var == null;
3391        return str_name;
3392      case RETURN:
3393        return "\\result";
3394      default:
3395        throw new Error("can't drop through switch statement");
3396    }
3397  }
3398
3399  /** Returns the name of this variable in simplify format. */
3400  @SideEffectFree
3401  public String simplify_name() {
3402    return simplify_name(null);
3403  }
3404
3405  /**
3406   * Returns the name of this variable in simplify format. If an index is specified, it is used as
3407   * an array index. It is an error to specify an index on a non-array variable.
3408   */
3409  public String simplify_name(@Nullable String index) {
3410    if (!FileIO.new_decl_format) {
3411      return var_info_name.simplify_name(); // vin ok
3412    }
3413
3414    assert (index == null) || file_rep_type.isArray() : index + " " + name();
3415
3416    // If this is a derived variable, the derivations builds the name
3417    if (derived != null) {
3418      return derived.simplify_name();
3419    }
3420
3421    // Build the name by processing back through all of the enclosing variables
3422    switch (var_kind) {
3423      case FIELD:
3424        assert relative_name != null : this;
3425        return String.format("(select |%s| %s)", relative_name, enclosing_var.simplify_name(index));
3426      case FUNCTION:
3427        // function_args      assert function_args == null : "function args not implemented";
3428        if (var_flags.contains(VarFlags.CLASSNAME)) {
3429          return ("(typeof " + enclosing_var.simplify_name(index) + ")");
3430        }
3431        if (var_flags.contains(VarFlags.TO_STRING)) {
3432          return String.format("(select |toString| %s)", enclosing_var.simplify_name(index));
3433        }
3434        if (enclosing_var != null) {
3435          return enclosing_var.simplify_name(index) + "." + relative_name + "()";
3436        }
3437        return str_name;
3438      case ARRAY:
3439        if (index == null) {
3440          return String.format("(select elems %s)", enclosing_var.simplify_name());
3441        }
3442        // if (index.equals("|0|")) {
3443        //   System.err.printf("index = %s%n", index);
3444        //   Throwable t = new Throwable();
3445        //   t.printStackTrace();
3446        // }
3447        return String.format("(select (select elems %s) %s)", enclosing_var.simplify_name(), index);
3448      case VARIABLE:
3449        if (dkconfig_constant_fields_simplify && str_name.contains(".")) {
3450          String sel;
3451          String[] fields;
3452          if (postState != null) {
3453            fields = postState.name().split("\\.");
3454            sel = String.format("(select |%s| |__orig__%s|)", fields[1], fields[0]);
3455          } else { // not orig variable
3456            fields = str_name.split("\\.");
3457            sel = String.format("(select |%s| |%s|)", fields[1], fields[0]);
3458          }
3459          for (int ii = 2; ii < fields.length; ii++) {
3460            sel = String.format("(select |%s| %s)", fields[ii], sel);
3461          }
3462          return sel;
3463        }
3464
3465        assert enclosing_var == null;
3466        if (postState != null) {
3467          return "|__orig__" + postState.name() + "|";
3468        }
3469        return "|" + str_name + "|";
3470      case RETURN:
3471        return "|return|";
3472      default:
3473        throw new Error("can't drop through switch statement");
3474    }
3475  }
3476
3477  /** Return the name of this variable in its prestate (orig). */
3478  @SideEffectFree
3479  public @Interned String prestate_name() {
3480    return ("orig(" + name() + ")").intern();
3481  }
3482
3483  /**
3484   * Returns the name of the size variable that correponds to this array variable in simplify
3485   * format. Returns null if this variable is not an array or the size name can't be constructed for
3486   * other reasons. Note that isArray seems to distinguish between actual arrays and other sequences
3487   * (such as java.util.list). Simplify uses (it seems) the same length approach for both, so we
3488   * don't check isArray().
3489   */
3490  public @Nullable String get_simplify_size_name() {
3491    // Implement the method in two ways, to double-check results.
3492
3493    @Interned String result;
3494    if (!file_rep_type.isArray() || isDerived()) {
3495      result = null;
3496    } else {
3497      // System.out.printf("Getting size name for %s [%s]%n", name(),
3498      //                    get_length());
3499      result = get_length().simplify_name().intern();
3500    }
3501
3502    @Interned String old_result;
3503    if (!var_info_name.isApplySizeSafe()) { // vin ok
3504      old_result = null;
3505    } else {
3506      old_result = var_info_name.applySize().simplify_name().intern(); // vin ok
3507    }
3508    if (FileIO.new_decl_format && (old_result != result)) {
3509      throw new Error(
3510          String.format(
3511              "%s: '%s' '%s'%n basehashcode = %s%n",
3512              this, result, old_result, get_base_array_hashcode()));
3513    }
3514
3515    return old_result;
3516  }
3517
3518  /** Returns true if this variable contains a simple variable whose name is varname. */
3519  public boolean includes_simple_name(String varname) {
3520    if (!FileIO.new_decl_format) {
3521      return var_info_name.includesSimpleName(varname); // vin ok
3522    }
3523
3524    if (isDerived()) {
3525      for (VarInfo base : derived.getBases()) {
3526        if (base.includes_simple_name(varname)) {
3527          return true;
3528        }
3529      }
3530    } else {
3531      for (VarInfo vi = this; vi != null; vi = vi.enclosing_var) {
3532        if ((vi.var_kind == VarKind.VARIABLE) && vi.name().equals(varname)) {
3533          return true;
3534        }
3535      }
3536    }
3537    return false;
3538  }
3539
3540  /**
3541   * Quantifies over the specified array variables in ESC format. Returns an array with 2 more
3542   * elements than the argument. Element 0 is the quantification, Element 1 is the indexed form of
3543   * variable 1, Element 2 is the indexed form of variable 3, and Element 4 is syntax such as close
3544   * parentheses.
3545   */
3546  public static String[] esc_quantify(VarInfo... vars) {
3547    return esc_quantify(true, vars);
3548  }
3549
3550  /**
3551   * Quantifies over the specified array variables in ESC format. Returns an array with 2 more
3552   * elements than the argument. Element 0 is the quantification, Element 1 is the indexed form of
3553   * variable 1, Element 2 is the indexed form of variable 3, and Element 4 is syntax such as close
3554   * parentheses.
3555   */
3556  public static String[] esc_quantify(boolean elementwise, VarInfo... vars) {
3557
3558    if (FileIO.new_decl_format) {
3559      Quantify.ESCQuantification quant =
3560          new Quantify.ESCQuantification(Quantify.get_flags(elementwise), vars);
3561      if (vars.length == 1) {
3562        return new String[] {quant.get_quantification(), quant.get_arr_vars_indexed(0), ")"};
3563      } else if ((vars.length == 2) && vars[1].file_rep_type.isArray()) {
3564        return new String[] {
3565          quant.get_quantification(),
3566          quant.get_arr_vars_indexed(0),
3567          quant.get_arr_vars_indexed(1),
3568          ")"
3569        };
3570      } else {
3571        return new String[] {
3572          quant.get_quantification(), quant.get_arr_vars_indexed(0), vars[1].esc_name(), ")"
3573        };
3574      }
3575    } else {
3576      VarInfoName vin[] = new VarInfoName[vars.length];
3577      for (int ii = 0; ii < vars.length; ii++) {
3578        vin[ii] = vars[ii].var_info_name; // vin ok
3579      }
3580      return VarInfoName.QuantHelper.format_esc(vin, elementwise);
3581    }
3582  }
3583
3584  /**
3585   * Returns a string array with 3 elements. The first element is the sequence, the second element
3586   * is the lower bound, and the third element is the upper bound. Returns null if this is not a
3587   * direct array or slice.
3588   */
3589  public String @Nullable [] simplifyNameAndBounds() {
3590    if (!FileIO.new_decl_format) {
3591      return VarInfoName.QuantHelper.simplifyNameAndBounds(var_info_name); // vin ok
3592    }
3593
3594    String[] results = new String[3];
3595    if (is_direct_non_slice_array() || (derived instanceof SequenceSubsequence)) {
3596      results[0] = get_base_array_hashcode().simplify_name();
3597      results[1] = get_lower_bound().simplify_name();
3598      results[2] = get_upper_bound().simplify_name();
3599      return results;
3600    }
3601
3602    return null;
3603  }
3604
3605  /**
3606   * Returns the upper and lower bounds of the slice in simplify format. The implementation is
3607   * somewhat different that simplifyNameAndBounds (I don't know why).
3608   */
3609  public String @Nullable [] get_simplify_slice_bounds() {
3610    if (!FileIO.new_decl_format) {
3611      @Interned VarInfoName[] bounds = var_info_name.getSliceBounds(); // vin ok
3612      if (bounds == null) {
3613        return null;
3614      }
3615      String[] str_bounds = new String[2];
3616      str_bounds[0] = bounds[0].simplify_name();
3617      str_bounds[1] = bounds[1].simplify_name();
3618      return str_bounds;
3619    }
3620
3621    String[] results;
3622    if (derived instanceof SequenceSubsequence) {
3623      results = new String[2];
3624      results[0] = get_lower_bound().simplify_name().intern();
3625      results[1] = get_upper_bound().simplify_name().intern();
3626    } else {
3627      results = null;
3628    }
3629
3630    return results;
3631  }
3632
3633  /**
3634   * Return a string in simplify format that will seclect the (index_base + index_off)-th element of
3635   * the sequence specified by this variable.
3636   *
3637   * @param simplify_index_name name of the index. If free is false, this must be a number or null
3638   *     (null implies an index of 0).
3639   * @param free true of simplify_index_name is variable name
3640   * @param index_off offset from the index
3641   */
3642  public String get_simplify_selectNth(String simplify_index_name, boolean free, int index_off) {
3643
3644    // Remove the simplify bars if present from the index name
3645    if ((simplify_index_name != null)
3646        && simplify_index_name.startsWith("|")
3647        && simplify_index_name.endsWith("|"))
3648      simplify_index_name = simplify_index_name.substring(1, simplify_index_name.length() - 1);
3649
3650    // Use VarInfoName to handle the old format
3651    if (!FileIO.new_decl_format) {
3652      VarInfoName select =
3653          VarInfoName.QuantHelper.selectNth(
3654              this.var_info_name, // vin ok
3655              simplify_index_name,
3656              free,
3657              index_off);
3658      // System.out.printf("sNth: index %s, free %b, off %d, result '%s'%n",
3659      //                     simplify_index_name, free, index_off,
3660      //                     select.simplify_name());
3661      return select.simplify_name();
3662    }
3663
3664    // Calculate the index (including the offset if non-zero)
3665    String complete_index;
3666    if (!free) {
3667      int index = 0;
3668      if (simplify_index_name != null) {
3669        index = Integer.decode(simplify_index_name);
3670      }
3671      index += index_off;
3672      complete_index = String.format("%d", index);
3673    } else {
3674      if (index_off != 0) {
3675        complete_index = String.format("(+ |%s| %d)", simplify_index_name, index_off);
3676      } else {
3677        complete_index = String.format("|%s|", simplify_index_name);
3678      }
3679    }
3680
3681    // Return the array properly indexed
3682    return simplify_name(complete_index);
3683  }
3684
3685  /**
3686   * Return a string in simplify format that will seclect the index_off element in a sequence that
3687   * has a lower bound.
3688   *
3689   * @param index_off offset from the index
3690   */
3691  public String get_simplify_selectNth_lower(int index_off) {
3692
3693    // Use VarInfoName to handle the old format
3694    if (!FileIO.new_decl_format) {
3695      @Interned VarInfoName[] bounds = var_info_name.getSliceBounds();
3696      VarInfoName lower = null;
3697      if (bounds != null) {
3698        lower = bounds[0];
3699      }
3700      VarInfoName select =
3701          VarInfoName.QuantHelper.selectNth(
3702              var_info_name, // vin ok
3703              lower,
3704              index_off);
3705      return select.simplify_name();
3706    }
3707
3708    // Calculate the index (including the offset if non-zero)
3709    String complete_index;
3710    Quantify.Term lower = get_lower_bound();
3711    String lower_name = lower.simplify_name();
3712    if (!(lower instanceof Quantify.Constant)) {
3713      lower_name = String.format("|%s|", lower_name);
3714    }
3715    if (index_off != 0) {
3716      if (lower instanceof Quantify.Constant) {
3717        complete_index = String.format("%d", ((Quantify.Constant) lower).get_value() + index_off);
3718      } else {
3719        complete_index = String.format("(+ %s %d)", lower_name, index_off);
3720      }
3721    } else {
3722      complete_index = String.format("%s", lower_name);
3723    }
3724
3725    // Return the array properly indexed
3726    // System.err.printf("lower bound type = %s [%s] %s%n", lower,
3727    //                   lower.getClass(), complete_index);
3728    return simplify_name(complete_index);
3729  }
3730
3731  /** Get a fresh variable name that doesn't appear in the given variable in simplify format. */
3732  public static String get_simplify_free_index(VarInfo... vars) {
3733    if (!FileIO.new_decl_format) {
3734      VarInfoName[] vins = new VarInfoName[vars.length];
3735      for (int ii = 0; ii < vars.length; ii++) {
3736        vins[ii] = vars[ii].var_info_name; // vin ok
3737      }
3738      return VarInfoName.QuantHelper.getFreeIndex(vins).simplify_name();
3739    }
3740
3741    // Get a free variable for each variable and return the first one
3742    QuantifyReturn[] qret = Quantify.quantify(vars);
3743    return qret[0].index.simplify_name();
3744  }
3745
3746  /** Get a 2 fresh variable names that doesn't appear in the given variable in simplify format. */
3747  public static String[] get_simplify_free_indices(VarInfo... vars) {
3748    if (!FileIO.new_decl_format) {
3749      if (vars.length == 1) {
3750        VarInfoName index1_vin =
3751            VarInfoName.QuantHelper.getFreeIndex(vars[0].var_info_name); // vin ok
3752        String index2 =
3753            VarInfoName.QuantHelper.getFreeIndex(vars[0].var_info_name, index1_vin)
3754                .simplify_name(); // vin ok
3755        return new String[] {index1_vin.name(), index2};
3756      } else if (vars.length == 2) {
3757        VarInfoName index1_vin =
3758            VarInfoName.QuantHelper.getFreeIndex(
3759                vars[0].var_info_name, vars[1].var_info_name); // vin ok
3760        String index2 =
3761            VarInfoName.QuantHelper.getFreeIndex(
3762                    vars[0].var_info_name, vars[1].var_info_name, index1_vin) // vin ok
3763                .simplify_name();
3764        return new String[] {index1_vin.name(), index2};
3765      } else {
3766        throw new Error("unexpected length " + vars.length);
3767      }
3768    }
3769
3770    // Get a free variable for each variable
3771    if (vars.length == 1) {
3772      vars = new VarInfo[] {vars[0], vars[0]};
3773    }
3774    QuantifyReturn qret[] = Quantify.quantify(vars);
3775    return new String[] {qret[0].index.simplify_name(), qret[1].index.simplify_name()};
3776  }
3777
3778  /**
3779   * Quantifies over the specified array variables in Simplify format. Returns a string array that
3780   * contains the quantification, indexed form of each variable, optionally the index itself, and
3781   * the closer.
3782   *
3783   * <p>If elementwise is true, include the additional contraint that the indices (there must be
3784   * exactly two in this case) refer to corresponding positions. If adjacent is true, include the
3785   * additional constraint that the second index be one more than the first. If distinct is true,
3786   * include the constraint that the two indices are different. If includeIndex is true, return
3787   * additional strings, after the roots but before the closer, with the names of the index
3788   * variables.
3789   */
3790  public static String[] simplify_quantify(EnumSet<QuantFlags> flags, VarInfo... vars) {
3791
3792    if (!FileIO.new_decl_format) {
3793      // Get the names for each variable.
3794      VarInfoName vin[] = new VarInfoName[vars.length];
3795      for (int ii = 0; ii < vars.length; ii++) {
3796        vin[ii] = vars[ii].var_info_name; // vin ok
3797      }
3798
3799      return VarInfoName.QuantHelper.format_simplify(
3800          vin,
3801          flags.contains(QuantFlags.ELEMENT_WISE),
3802          flags.contains(QuantFlags.ADJACENT),
3803          flags.contains(QuantFlags.DISTINCT),
3804          flags.contains(QuantFlags.INCLUDE_INDEX));
3805    }
3806
3807    Quantify.SimplifyQuantification quant = new Quantify.SimplifyQuantification(flags, vars);
3808    boolean include_index = flags.contains(QuantFlags.INCLUDE_INDEX);
3809    if ((vars.length == 1) && include_index) {
3810      return new String[] {
3811        quant.get_quantification(),
3812        quant.get_arr_vars_indexed(0),
3813        quant.get_index(0),
3814        quant.get_closer()
3815      };
3816    } else if (vars.length == 1) {
3817      return new String[] {
3818        quant.get_quantification(), quant.get_arr_vars_indexed(0), quant.get_closer()
3819      };
3820    } else if ((vars.length == 2) && include_index) {
3821      return new String[] {
3822        quant.get_quantification(),
3823        quant.get_arr_vars_indexed(0),
3824        quant.get_arr_vars_indexed(1),
3825        quant.get_index(0),
3826        quant.get_index(1),
3827        quant.get_closer()
3828      };
3829    } else { // must be length 2 and no index
3830      return new String[] {
3831        quant.get_quantification(),
3832        quant.get_arr_vars_indexed(0),
3833        quant.get_arr_vars_indexed(1),
3834        quant.get_closer()
3835      };
3836    }
3837  }
3838
3839  /**
3840   * See {@link #simplify_quantify(EnumSet, VarInfo[])}.
3841   *
3842   * @see #simplify_quantify(EnumSet, VarInfo[])
3843   */
3844  public static String[] simplify_quantify(VarInfo... vars) {
3845    return simplify_quantify(EnumSet.noneOf(QuantFlags.class), vars);
3846  }
3847
3848  /**
3849   * Returns a rough indication of the complexity of the variable. Higher numbers indicate more
3850   * complexity.
3851   */
3852  public int complexity() {
3853    if (!FileIO.new_decl_format) {
3854      // System.out.printf("%s - %s%n", this, var_info_name.repr());
3855      return var_info_name.inOrderTraversal().size(); // vin ok
3856    }
3857
3858    int cnt = 0;
3859    if (isDerived()) {
3860      cnt += derived.complexity();
3861      VarInfo[] bases = derived.getBases();
3862      for (VarInfo vi : bases) {
3863        cnt += vi.complexity();
3864      }
3865      // Adjust for the complexity change when a prestate is nested in
3866      // another prestate.  This is just done to match the old version
3867      if ((bases.length == 2) && bases[0].isPrestate()) {
3868        if (bases[1].isPrestate()) {
3869          cnt--;
3870        } else {
3871          cnt++;
3872        }
3873      }
3874    } else {
3875      if (isPrestate()) {
3876        cnt++;
3877      }
3878      for (VarInfo vi = this; vi != null; vi = vi.enclosing_var) {
3879        cnt++;
3880      }
3881    }
3882
3883    // int old_cnt = var_info_name.inOrderTraversal().size();
3884    // if (cnt != old_cnt)
3885    //   System.out.printf("var %s, new cnt = %d, old cnt = %d [%s]%n",
3886    //                 name(), cnt, old_cnt, var_info_name.inOrderTraversal());
3887    return cnt;
3888  }
3889
3890  /**
3891   * Returns true if this variable can be assigned to. Currently this is presumed true of all
3892   * variable except the special variable for the type of a variable and the size of a sequence. It
3893   * should include pure functions as well.
3894   */
3895  @Pure
3896  public boolean is_assignable_var() {
3897    if (!FileIO.new_decl_format) {
3898      return !((var_info_name instanceof VarInfoName.TypeOf) // vin ok
3899          || (var_info_name instanceof VarInfoName.SizeOf)); // vin ok
3900    }
3901
3902    return !(is_typeof() || is_size());
3903  }
3904
3905  /**
3906   * Returns whether or not this variable represents the type of a variable (eg,
3907   * a.getClass().getName()). Note that this will miss prestate variables such as
3908   * 'orig(a.getClass().getName())'.
3909   */
3910  @Pure
3911  public boolean is_typeof() {
3912    if (!FileIO.new_decl_format) {
3913      return (var_info_name instanceof VarInfoName.TypeOf); // vin ok
3914    }
3915
3916    // The isPrestate check doesn't seem necessary, but is required to
3917    // match old behavior.
3918    return !isPrestate() && var_flags.contains(VarFlags.CLASSNAME);
3919  }
3920
3921  /**
3922   * Returns whether or not this variable represents the type of a variable (eg,
3923   * a.getClass().getName()). This version finds prestate variables such as
3924   * 'org(a.getClass().getName())'.
3925   */
3926  public boolean has_typeof() {
3927    if (!FileIO.new_decl_format) {
3928      return var_info_name.hasTypeOf(); // vin ok
3929    }
3930
3931    if (isPrestate()) {
3932      return postState.has_typeof();
3933    }
3934    return is_typeof();
3935  }
3936
3937  /** Returns whether or not this variable is the 'this' variable. */
3938  @Pure
3939  public boolean is_this() {
3940    return name().equals("this");
3941    // return get_VarInfoName().equals(VarInfoName.THIS);
3942  }
3943
3944  /**
3945   * Returns whether or not this variable is the 'this' variable. True for both normal and prestate
3946   * versions of the variable.
3947   */
3948  @Pure
3949  public boolean isThis() {
3950    return var_info_name.isThis();
3951  }
3952
3953  /** Returns whether this is a size of an array or a prestate thereof. */
3954  @Pure
3955  public boolean is_size() {
3956    return (derived instanceof SequenceLength);
3957  }
3958
3959  /** Returns wehther or not this variable is a field. */
3960  @Pure
3961  public boolean is_field() {
3962    return (var_info_name instanceof VarInfoName.Field);
3963  }
3964
3965  /** Returns whether or not this variable has an integer offset (eg, a+2) */
3966  @Pure
3967  public boolean is_add() {
3968    return (var_info_name instanceof VarInfoName.Add);
3969  }
3970
3971  /**
3972   * Returns the integer offset if this variable is an addition such as a+2. Throws an exception of
3973   * this variable is not an addition.
3974   *
3975   * @see #is_add()
3976   */
3977  public int get_add_amount() {
3978    return ((VarInfoName.Add) var_info_name).amount;
3979  }
3980
3981  /**
3982   * Returns whether or not this variable is an actual array as opposed to an array that is created
3983   * over fields/methods of an array. For example, 'a[]' is a direct array, but 'a[].b' is not.
3984   */
3985  @Pure
3986  public boolean is_direct_array() {
3987    // Must be an array to be a direct array
3988    if (!rep_type.isArray()) {
3989      return false;
3990    }
3991
3992    // If $Field or $Type appears before $Elements, false.
3993    // System.out.printf("%s flatten %s%n", name(), name);
3994    for (VarInfoName node : new VarInfoName.InorderFlattener(var_info_name).nodes()) {
3995      if (node instanceof VarInfoName.Field) {
3996        return false;
3997      }
3998      if (node instanceof VarInfoName.TypeOf) {
3999        return false;
4000      }
4001      if (node instanceof VarInfoName.Elements) {
4002        break;
4003      }
4004    }
4005
4006    return true;
4007  }
4008
4009  /**
4010   * Returns whether or not this variable is an actual array as opposed to an array that is created
4011   * over fields/methods of an array or a slice. For example, 'a[]' is a direct array, but 'a[].b'
4012   * and 'a[i..]' are not.
4013   */
4014  @Pure
4015  public boolean is_direct_non_slice_array() {
4016    return (var_info_name instanceof VarInfoName.Elements);
4017  }
4018
4019  /**
4020   * Returns whether or not two variables have the same enclosing variable. If either variable is
4021   * not a field, returns false.
4022   */
4023  public boolean has_same_parent(VarInfo other) {
4024    if (!is_field() || !other.is_field()) {
4025      return false;
4026    }
4027
4028    VarInfoName.Field name1 = (VarInfoName.Field) var_info_name;
4029    VarInfoName.Field name2 = (VarInfoName.Field) other.var_info_name;
4030
4031    return name1.term.equals(name2.term);
4032  }
4033
4034  /**
4035   * Returns the variable that encloses this one. For example if this variable is 'x.a.b', the
4036   * enclosing variable is 'x.a'.
4037   */
4038  public @Nullable VarInfo get_enclosing_var() {
4039    if (FileIO.new_decl_format) {
4040      return enclosing_var;
4041    } else {
4042      List<VarInfoName> traversal = new VarInfoName.InorderFlattener(var_info_name).nodes();
4043      if (traversal.size() <= 1) {
4044        // System.out.printf("size <= 1, traversal = %s%n", traversal);
4045        return null;
4046      } else {
4047        VarInfo enclosing_vi = ppt.find_var_by_name(traversal.get(1).name());
4048        // if (enclosing_vi == null)
4049        //  System.out.printf("Can't find '%s' in %s%n",
4050        //                      traversal.get(1).name(), ppt.varNames());
4051        return enclosing_vi;
4052      }
4053    }
4054  }
4055
4056  /**
4057   * Replaces all instances of 'this' in the variable with the name of arg. Used to match up
4058   * enter/exit variables with object variables.
4059   */
4060  public String replace_this(VarInfo arg) {
4061    VarInfoName parent_name = var_info_name.replaceAll(VarInfoName.THIS, arg.var_info_name);
4062    return parent_name.name();
4063  }
4064
4065  /**
4066   * Creates a VarInfo that is a subsequence that begins at begin and ends at end with the specified
4067   * shifts. The begin or the end can be null, but a non-zero shift is only allowed with non-null
4068   * variables.
4069   */
4070  public static VarInfo make_subsequence(
4071      VarInfo seq, @Nullable VarInfo begin, int begin_shift, @Nullable VarInfo end, int end_shift) {
4072
4073    String begin_str = inside_name(begin, seq.isPrestate(), begin_shift);
4074    if (begin_str.equals("")) { // interned if the null string, not interned otherwise
4075      begin_str = "0";
4076    }
4077    String end_str = inside_name(end, seq.isPrestate(), end_shift);
4078
4079    VarInfoName begin_name;
4080    String parent_format = "%s..";
4081    if (begin == null) {
4082      begin_name = null;
4083    } else {
4084      begin_name = (begin != null) ? begin.var_info_name : null;
4085      if (begin_shift == -1) {
4086        begin_name = begin_name.applyDecrement();
4087        parent_format = "%s-1..";
4088      } else if (begin_shift == 1) {
4089        begin_name = begin_name.applyIncrement();
4090        parent_format = "%s+1..";
4091      } else {
4092        assert begin_shift == 0;
4093      }
4094    }
4095
4096    VarInfoName end_name;
4097    if (end == null) {
4098      end_name = null;
4099      parent_format += "%s";
4100    } else {
4101      end_name = end.var_info_name;
4102      if (end_shift == -1) {
4103        end_name = end_name.applyDecrement();
4104        parent_format += "%s-1";
4105      } else if (end_shift == 1) {
4106        end_name = end_name.applyIncrement();
4107        parent_format += "%s+1";
4108      } else {
4109        assert end_shift == 0;
4110        parent_format += "%s";
4111      }
4112    }
4113
4114    VarInfoName new_name = seq.var_info_name.applySlice(begin_name, end_name);
4115
4116    VarInfo vi = new VarInfo(new_name, seq.type, seq.file_rep_type, seq.comparability, seq.aux);
4117    vi.setup_derived_base(seq, begin, end);
4118    vi.str_name =
4119        seq.apply_subscript(String.format("%s..%s", begin_str, end_str))
4120            .intern(); // interning bugfix
4121
4122    // If there is a parent ppt (set in setup_derived_base), set the
4123    // parent variable accordingly.  If all of the original variables used
4124    // the default name, this can as well.  Otherwise, build the parent
4125    // name.
4126    for (VarParent parent : vi.parents) {
4127      int rid = parent.parent_relation_id;
4128
4129      if ((seq.get_parent(rid) == null)
4130          && ((begin == null) || !begin.has_parent(rid) || (begin.parent_var(rid) == null))
4131          && ((end == null) || !end.has_parent(rid) || (end.parent_var(rid) == null))) {
4132
4133        parent.parent_variable = null;
4134      } else {
4135        String begin_pname =
4136            (begin == null || !begin.has_parent(rid)) ? "0" : begin.parent_var_name(rid);
4137        String end_pname = (end == null || !end.has_parent(rid)) ? "" : end.parent_var_name(rid);
4138        @SuppressWarnings(
4139            "formatter") // format string is constructed above using make_subsequence's arguments
4140        String res =
4141            apply_subscript(
4142                seq.parent_var_name(rid), String.format(parent_format, begin_pname, end_pname));
4143        parent.parent_variable = res;
4144        // System.out.printf("-- set parent var from '%s' '%s' '%s' '%s'%n",
4145        //       seq.parent_var_name(), parent_format, begin_pname, end_pname);
4146      }
4147      // System.out.printf("Parent for %s:%s is %s:%s%n",
4148      //                ((seq.ppt != null)? seq.ppt.name() : "none"), vi.name(),
4149      //                  vi.parent_ppt, vi.parent_variable);
4150    }
4151
4152    return vi;
4153  }
4154
4155  /**
4156   * Returns the name to use for vi inside of a array reference. If the array reference is orig,
4157   * then orig is implied. This removes orig from orig variales and adds post to post variables.
4158   */
4159  private static String inside_name(@Nullable VarInfo vi, boolean in_orig, int shift) {
4160    if (vi == null) {
4161      return "";
4162    }
4163
4164    String shift_str = "";
4165    if (shift != 0) {
4166      shift_str = String.format("%+d", shift);
4167    }
4168
4169    if (in_orig) {
4170      if (vi.isPrestate()) {
4171        return vi.postState.name() + shift_str;
4172      } else {
4173        return String.format("post(%s)%s", vi.name(), shift_str);
4174      }
4175    } else {
4176      return vi.name() + shift_str;
4177    }
4178  }
4179
4180  /**
4181   * Creates a VarInfo that is an index into a sequence. The type, file_rep_type, etc are taken from
4182   * the element type of the sequence.
4183   */
4184  public static VarInfo make_subscript(VarInfo seq, @Nullable VarInfo index, int index_shift) {
4185
4186    String index_str = inside_name(index, seq.isPrestate(), index_shift);
4187
4188    VarInfoName index_name;
4189    if (index == null) {
4190      index_name = VarInfoName.parse(String.valueOf(index_shift));
4191    } else {
4192      index_name = index.var_info_name;
4193      if (index_shift == -1) {
4194        index_name = index_name.applyDecrement();
4195      } else {
4196        assert index_shift == 0 : "bad shift " + index_shift + " for " + index;
4197      }
4198    }
4199
4200    VarInfoName new_name = seq.var_info_name.applySubscript(index_name);
4201    VarInfo vi =
4202        new VarInfo(
4203            new_name,
4204            seq.type.elementType(),
4205            seq.file_rep_type.elementType(),
4206            seq.comparability.elementType(),
4207            VarInfoAux.getDefault());
4208    vi.setup_derived_base(seq, index);
4209    vi.var_kind = VarInfo.VarKind.FIELD;
4210    vi.str_name = seq.apply_subscript(index_str).intern(); // interning bugfix
4211    for (VarParent parent : vi.parents) {
4212      int rid = parent.parent_relation_id;
4213
4214      if ((seq.parent_var(rid) == null)
4215          && ((index == null) || !index.has_parent(rid) || (index.parent_var(rid) == null))) {
4216        parent.parent_variable = null;
4217      } else { // one of the two bases has a different parent variable name
4218        String subscript_parent = String.valueOf(index_shift);
4219        if (index != null && index.has_parent(rid)) {
4220          subscript_parent = index.parent_var_name(rid);
4221
4222          if (seq.isPrestate() && !index.isPrestate()) {
4223            // Wrap the index in POST if the sequence is original
4224            subscript_parent = VarInfoName.parse(subscript_parent).applyPoststate().name_impl();
4225          } else if (seq.isPrestate() && index.isPrestate()) {
4226            // Remove redundant ORIG
4227            subscript_parent = ((Prestate) VarInfoName.parse(subscript_parent)).term.name_impl();
4228          }
4229
4230          if (index_shift == -1) {
4231            subscript_parent = subscript_parent + "-1";
4232          }
4233        }
4234        parent.parent_variable = apply_subscript(seq.parent_var_name(rid), subscript_parent);
4235      }
4236    }
4237    return vi;
4238  }
4239
4240  /**
4241   * Create a VarInfo that is a function over one or more other variables. The type, rep_type, etc.
4242   * of the new function are taken from the first variable.
4243   */
4244  public static VarInfo make_function(String function_name, VarInfo... vars) {
4245
4246    VarInfoName[] vin = new VarInfoName[vars.length];
4247    for (int ii = 0; ii < vars.length; ii++) {
4248      vin[ii] = vars[ii].var_info_name;
4249    }
4250
4251    VarInfo vi =
4252        new VarInfo(
4253            VarInfoName.applyFunctionOfN(function_name, vin),
4254            vars[0].type,
4255            vars[0].file_rep_type,
4256            vars[0].comparability,
4257            vars[0].aux);
4258    vi.setup_derived_function(function_name, vars);
4259    return vi;
4260  }
4261
4262  /*
4263   * Creates the derived variable func(seq) from seq.
4264   *
4265   * @param func_name name of the function
4266   * @param type return type of the function.  If null, the return type is
4267   *             the element type of the sequence.
4268   * @param seq sequence variable
4269   * @param shift value to add or subtract from the function.  Legal values
4270   *              are -1, 0, and 1.
4271   */
4272  public static VarInfo make_scalar_seq_func(
4273      String func_name, @Nullable ProglangType type, VarInfo seq, int shift) {
4274
4275    VarInfoName viname = seq.var_info_name.applyFunction(func_name);
4276    if (func_name.equals("size")) {
4277      viname = seq.var_info_name.applySize();
4278    }
4279    String shift_name = "";
4280    if (shift == -1) {
4281      viname = viname.applyDecrement();
4282      shift_name = "_minus1";
4283    } else if (shift == 1) {
4284      viname = viname.applyIncrement();
4285      shift_name = "_plus1";
4286    } else {
4287      assert shift == 0;
4288    }
4289
4290    @NonNull ProglangType ptype = type;
4291    @NonNull ProglangType frtype = type;
4292    VarComparability comp = seq.comparability.indexType(0);
4293    VarInfoAux aux = VarInfoAux.getDefault();
4294    if (type == null) {
4295      ptype = seq.type.elementType();
4296      frtype = seq.file_rep_type.elementType();
4297      comp = seq.comparability.elementType();
4298      aux = seq.aux;
4299    }
4300    VarInfo vi = new VarInfo(viname, ptype, frtype, comp, aux);
4301    vi.setup_derived_base(seq);
4302    vi.var_kind = VarInfo.VarKind.FUNCTION;
4303    vi.enclosing_var = seq;
4304    vi.arr_dims = 0;
4305    // null is initial value:  vi.function_args = null;
4306    vi.relative_name = func_name + shift_name;
4307
4308    // Calculate the string to add for the shift.
4309    String shift_str = "";
4310    if (shift != 0) {
4311      shift_str = String.format("%+d", shift);
4312    }
4313
4314    // Determine whether orig should be swapped with the function.
4315    // The original VarInfoName code did this only for the size
4316    // function (though it makes the same sense for all functions over
4317    // sequences).
4318    boolean swap_orig =
4319        func_name.equals("size") && seq.isPrestate() && !VarInfoName.dkconfig_direct_orig;
4320
4321    // Force orig to the outside if specified.
4322    if (swap_orig) {
4323      vi.str_name =
4324          String.format("orig(%s(%s))%s", func_name, seq.postState.name(), shift_str)
4325              .intern(); // interning bugfix
4326    } else {
4327      vi.str_name =
4328          String.format("%s(%s)%s", func_name, seq.name(), shift_str).intern(); // interning bugfix
4329    }
4330
4331    for (VarParent parent : vi.parents) {
4332      int rid = parent.parent_relation_id;
4333      if (!seq.has_parent(rid) || seq.parent_var(rid) == null) {
4334        parent.parent_variable = null;
4335      } else {
4336        if (func_name.equals("size")) {
4337          // Special handling for the case where the parent var name is orig(array[...]).
4338          // With swapping, the parent should be orig(size(array[..])), however it's stored
4339          // as a string so the swap can't be done textually.
4340          VarInfoName parentName = VarInfoName.parse(seq.parent_var_name(rid));
4341
4342          // Can't use the more general applyFunction method here because it doesn't take into
4343          // account prestate values as the applySize method explicitly does
4344          parentName = parentName.applySize();
4345
4346          parent.parent_variable = String.format("%s%s", parentName.name(), shift_str);
4347        } else {
4348          assert !swap_orig : "swap orig with parent " + vi;
4349          parent.parent_variable =
4350              String.format("%s(%s)%s", func_name, seq.parent_var_name(rid), shift_str);
4351        }
4352      }
4353    }
4354    return vi;
4355  }
4356
4357  /*
4358   * Creates the derived variable func(str) from string.
4359   *
4360   * @param func_name name of the function
4361   * @param type return type of the function
4362   * @param str sequence variable
4363   */
4364  public static VarInfo make_scalar_str_func(String func_name, ProglangType type, VarInfo str) {
4365
4366    VarInfoName viname = str.var_info_name.applyFunction(func_name);
4367
4368    ProglangType ptype = type;
4369    ProglangType frtype = type;
4370    VarComparability comp = str.comparability.string_length_type();
4371    VarInfoAux aux = VarInfoAux.getDefault();
4372    VarInfo vi = new VarInfo(viname, ptype, frtype, comp, aux);
4373    vi.setup_derived_base(str);
4374    vi.var_kind = VarInfo.VarKind.FUNCTION;
4375    vi.enclosing_var = str;
4376    vi.arr_dims = 0;
4377    // null is initial value:  vi.function_args = null;
4378    vi.relative_name = func_name;
4379
4380    vi.str_name = String.format("%s.%s()", str.name(), func_name).intern(); // interning bugfix
4381
4382    for (VarParent parent : vi.parents) {
4383      int rid = parent.parent_relation_id;
4384      if (str.get_parent(rid).parent_variable == null) {
4385        parent.parent_variable = null;
4386      } else {
4387        parent.parent_variable =
4388            String.format("%s.%s()", str.get_parent(rid).parent_variable, func_name);
4389      }
4390    }
4391    return vi;
4392  }
4393
4394  /**
4395   * Returns true if vi is the prestate version of this. If this is a derived variable, vi must be
4396   * the same derivation using prestate versions of each base variable.
4397   */
4398  @Pure
4399  public boolean is_prestate_version(VarInfo vi) {
4400
4401    // If both variables are not derived
4402    if ((derived == null) && (vi.derived == null)) {
4403
4404      // true if vi is the prestate version of this
4405      return !isPrestate() && vi.isPrestate() && name().equals(vi.postState.name());
4406
4407      // else if both variables are derived
4408    } else if ((derived != null) && (vi.derived != null)) {
4409
4410      return derived.is_prestate_version(vi.derived);
4411
4412      // one is derived and the other isn't
4413    } else {
4414      return false;
4415    }
4416  }
4417
4418  /** Returns true if this is an array or a slice. */
4419  @Pure
4420  public boolean isArray() {
4421    return type.isArray();
4422  }
4423
4424  /** Returns true if this is a slice. */
4425  @Pure
4426  public boolean isSlice() {
4427    return isArray() && isDerived();
4428  }
4429
4430  /** Converts a variable name or expression to the old style of names. */
4431  public static String old_var_names(String name) {
4432    if (PrintInvariants.dkconfig_old_array_names && FileIO.new_decl_format) {
4433      return name.replace("[..]", "[]");
4434    } else {
4435      return name;
4436    }
4437  }
4438
4439  /** Returns the old style variable name for this name. */
4440  public String old_var_name() {
4441    return old_var_names(name());
4442  }
4443
4444  /** Rough check to ensure that the variable name and derivation match up. */
4445  public void var_check() {
4446
4447    if (false) {
4448      if (derived instanceof SequenceSubsequence) {
4449        if (name().contains("-1")) {
4450          SequenceSubsequence ss = (SequenceSubsequence) derived;
4451          // System.out.printf("checking %s [%s] with derived %s[%s]%n",
4452          //                   this, System.identityHashCode(this), derived,
4453          //                   System.identityHashCode(derived));
4454          assert ss.index_shift == -1
4455              : "bad var "
4456                  + this
4457                  + " derived "
4458                  + derived
4459                  + " shift "
4460                  + ss.index_shift
4461                  + " in ppt "
4462                  + ppt.name();
4463        }
4464      }
4465    }
4466  }
4467}