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