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