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