001package daikon.suppress;
002
003import daikon.Daikon;
004import daikon.Debug;
005import daikon.PptSlice;
006import daikon.PptTopLevel;
007import daikon.ValueTuple;
008import daikon.VarComparability;
009import daikon.VarInfo;
010import daikon.inv.Invariant;
011import daikon.inv.InvariantStatus;
012import daikon.inv.ValueSet;
013import daikon.inv.binary.BinaryInvariant;
014import daikon.inv.binary.twoScalar.IntEqual;
015import java.util.ArrayList;
016import java.util.Iterator;
017import java.util.LinkedHashMap;
018import java.util.LinkedHashSet;
019import java.util.List;
020import java.util.ListIterator;
021import java.util.Map;
022import java.util.Set;
023import java.util.concurrent.TimeUnit;
024import java.util.logging.Level;
025import java.util.logging.Logger;
026import org.checkerframework.checker.initialization.qual.UnknownInitialization;
027import org.checkerframework.checker.lock.qual.GuardSatisfied;
028import org.checkerframework.checker.mustcall.qual.MustCallUnknown;
029import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
030import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
031import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
032import org.checkerframework.checker.nullness.qual.Nullable;
033import org.checkerframework.checker.nullness.qual.RequiresNonNull;
034import org.checkerframework.dataflow.qual.Pure;
035import org.checkerframework.dataflow.qual.SideEffectFree;
036import typequals.prototype.qual.Prototype;
037
038// Outstanding NIS todo list
039//
040//  - Merging is slow when there are multiple children.
041//
042//  - Move the missingOutOfBounds check to is_slice_ok()
043//
044
045/** Main class for non-instantiating suppression. Handles setup and other overall functions. */
046public class NIS {
047
048  @SuppressWarnings("initialization.fields.uninitialized") // never instantiated
049  public NIS() {
050    throw new Error("Do not instantiate");
051  }
052
053  /** Debug tracer. */
054  public static final Logger debug = Logger.getLogger("daikon.suppress.NIS");
055
056  /** Debug Tracer for antecedent method. */
057  public static final Logger debugAnt = Logger.getLogger("daikon.suppress.NIS.Ant");
058
059  /** Boolean. If true, enable non-instantiating suppressions. */
060  public static boolean dkconfig_enabled = true;
061
062  /** Signifies which algorithm is used by NIS to process suppressions. */
063  public enum SuppressionProcessor {
064    HYBRID,
065    ANTECEDENT,
066    FALSIFIED
067  }
068
069  /**
070   * Specifies the algorithm that NIS uses to process suppressions. Possible selections are
071   * 'HYBRID', 'ANTECEDENT', and 'FALSIFIED'. The default is the hybrid algorithm which uses the
072   * falsified algorithm when only a small number of suppressions need to be processed and the
073   * antecedent algorithm when a large number of suppressions are processed.
074   */
075  public static SuppressionProcessor dkconfig_suppression_processor = SuppressionProcessor.HYBRID;
076
077  /**
078   * Boolean. If true, use antecedent method for NIS processing. If false, use falsified method for
079   * processing falsified invariants for NISuppressions. Note this flag is for internal use only and
080   * is controlled by NIS.dkconfig_suppression_processor.
081   */
082  public static boolean antecedent_method = true;
083
084  /**
085   * Boolean. If true, use a combination of the falsified method for a small number of suppressions
086   * to be processed and the antecedent method for a large number. Number is determined by
087   * NIS.dkconfig_hybrid_threshhold. Note this flag is for internal use only and is controlled by
088   * NIS.dkconfig_suppression_processor.
089   */
090  public static boolean hybrid_method = true;
091
092  /**
093   * Int. Less and equal to this number means use the falsified method in the hybrid method of
094   * processing falsified invariants, while greater than this number means use the antecedent
095   * method. Empirical data shows that number should not be more than 10000.
096   */
097  public static int dkconfig_hybrid_threshhold = 2500;
098
099  /**
100   * Boolean. If true, use the specific list of suppressor related invariant prototypes when
101   * creating constant invariants in the antecedent method.
102   */
103  public static boolean dkconfig_suppressor_list = true;
104
105  /**
106   * Boolean. If true, skip variables of file rep type {@code hashcode} when creating invariants
107   * over constants in the antecedent method.
108   */
109  public static boolean dkconfig_skip_hashcode_type = true;
110
111  /**
112   * Possible states for suppressors and suppressions. When a suppression is checked, it sets one of
113   * these states on each suppressor.
114   */
115  public enum SuppressState {
116    /** initial state -- suppressor has not been checked yet */
117    NONE,
118    /** suppressor matches the falsified invariant */
119    MATCH,
120    /** suppressor is true */
121    VALID,
122    /** suppressor is not true */
123    INVALID,
124    /** suppressor contains a variable that has always been nonsensical */
125    NONSENSICAL
126  }
127
128  /**
129   * Map from invariant class to a list of all of the suppression sets that contain a suppressor of
130   * that class.
131   */
132  public static @MonotonicNonNull Map<Class<? extends Invariant>, List<NISuppressionSet>>
133      suppressor_map;
134
135  /**
136   * Map from invariant class to the number of suppressions that contain a suppressor of that class.
137   */
138  public static @MonotonicNonNull Map<Class<? extends Invariant>, Integer>
139      suppressor_map_suppression_count;
140
141  /** List of all suppressions. Is set by {@link #init_ni_suppression}. */
142  static @MonotonicNonNull List<NISuppressionSet> all_suppressions;
143
144  /** List of suppressor invariant prototypes. */
145  public static @MonotonicNonNull List<@Prototype Invariant> suppressor_proto_invs;
146
147  /**
148   * List of invariants that are unsuppressed by the current sample. The {@link #falsified} and
149   * {@link #process_falsified_invs} methods add created invariants to this list. This list is
150   * cleared by {@link #apply_samples}.
151   */
152  public static List<Invariant> new_invs = new ArrayList<>();
153
154  /**
155   * List of invariants that are unsuppressed and then falsified by the current sample. This list is
156   * cleared at the beginning of apply_samples() and falsified invariants are added as the current
157   * sample is applied to invariants in new_invs. The list is only used when the falsified method is
158   * used for processing suppressions.
159   */
160  public static List<Invariant> newly_falsified = new ArrayList<>();
161
162  // Statistics that are kept during processing.  Some of these are kept
163  // and/or make sense for some approaches and not for others
164
165  /** Whether or not to keep statistics. */
166  public static boolean keep_stats = false;
167
168  /** Number of falsified invariants in the program point. */
169  public static int false_cnts = 0;
170
171  /** Number of falsified invariants in the program point that are potential suppressors. */
172  public static int false_invs = 0;
173
174  /** Number of suppressions processed. */
175  public static int suppressions_processed = 0;
176
177  /** Number of suppressions processed by the falsified method. */
178  public static int suppressions_processed_falsified = 0;
179
180  /** Number of invariants that are no longer suppressed by a suppression. */
181  static int new_invs_cnt = 0;
182
183  /** Number of new_invs_cnt that are falsified by the sample. */
184  public static int false_invs_cnt = 0;
185
186  /** Number of invariants actually created. */
187  public static int created_invs_cnt = 0;
188
189  /** Number of invariants that are still suppressed. */
190  static int still_suppressed_cnt = 0;
191
192  /** Total time spent in NIS processing. */
193  public static long duration = 0;
194
195  /** First execution of dump_stats(). Used to dump a header. */
196  static boolean first_time = true;
197
198  /**
199   * Sets up non-instantiation suppression. Primarily this includes setting up the map from
200   * suppressor classes to all of the suppression sets associated with that suppressor invariant.
201   */
202  @EnsuresNonNull({
203    "suppressor_map",
204    "suppressor_map_suppression_count",
205    "all_suppressions",
206    "suppressor_proto_invs"
207  })
208  public static void init_ni_suppression() {
209
210    // Creating these here, rather than where they are declared, allows
211    // this method to be called multiple times without a problem.
212    suppressor_map = new LinkedHashMap<>(256);
213    suppressor_map_suppression_count = new LinkedHashMap<>(256);
214    all_suppressions = new ArrayList<NISuppressionSet>();
215    suppressor_proto_invs = new ArrayList<@Prototype Invariant>();
216
217    // This should be the first statement in the method, but put it after the
218    // field initalizations so that the Initialization Checker doesn't complain.
219    if (!dkconfig_enabled) {
220      return;
221    }
222
223    // Get all defined suppressions.
224    for (Invariant inv : Daikon.proto_invs) {
225      NISuppressionSet ss = inv.get_ni_suppressions();
226      if (ss != null) {
227        for (int j = 0; j < ss.suppression_set.length; j++) {
228          NISuppression sup = ss.suppression_set[j];
229          if (true) {
230            assert inv.getClass() == sup.suppressee.sup_class
231                : "class "
232                    + inv.getClass()
233                    + " doesn't match "
234                    + sup
235                    + "/"
236                    + sup.suppressee.sup_class;
237            assert inv.getClass() == ss.suppression_set[j].suppressee.sup_class
238                : "class " + inv.getClass() + " doesn't match " + ss.suppression_set[j];
239          }
240        }
241        all_suppressions.add(ss);
242      }
243    }
244
245    // map suppressor classes to suppression sets
246    for (NISuppressionSet suppression_set : all_suppressions) {
247      suppression_set.add_to_suppressor_map(suppressor_map);
248    }
249
250    // If any suppressor is itself suppressed, augment the suppressions
251    // where the suppressor is used with the suppressor's suppressions.
252    for (List<NISuppressionSet> ss_list : suppressor_map.values()) {
253      for (NISuppressionSet ss : ss_list) {
254        NISuppressee suppressee = ss.get_suppressee();
255        List<NISuppressionSet> suppressor_ss_list = suppressor_map.get(suppressee.sup_class);
256        if (suppressor_ss_list == null) {
257          continue;
258        }
259        for (NISuppressionSet suppressor_ss : suppressor_ss_list) {
260          suppressor_ss.recurse_definitions(ss);
261        }
262      }
263    }
264
265    // map suppressor classes to suppression sets
266    // (now that recursive definitions are in place)
267    suppressor_map.clear();
268    for (NISuppressionSet suppression_set : all_suppressions) {
269      suppression_set.add_to_suppressor_map(suppressor_map);
270    }
271
272    if (NIS.dkconfig_suppressor_list) {
273      // Set up the list of suppressor invariant prototypes
274      for (@Prototype Invariant i : Daikon.proto_invs) {
275        if (suppressor_map.containsKey(i.getClass())) {
276          suppressor_proto_invs.add(i);
277        }
278      }
279    }
280
281    // count the number of suppressions associated with each
282    // suppressor
283    //  if (NIS.hybrid_method) {
284    for (Class<? extends Invariant> a : suppressor_map.keySet()) {
285
286      int x = 0;
287      List<NISuppressionSet> ss_list = suppressor_map.get(a);
288      for (NISuppressionSet ss : ss_list) {
289        x += ss.suppression_set.length;
290      }
291
292      suppressor_map_suppression_count.put(a, x);
293    }
294    // }
295
296    if (Debug.logDetail() && debug.isLoggable(Level.FINE)) {
297      dump(debug);
298    }
299  }
300
301  /**
302   * Instantiates any invariants that are no longer suppressed because inv has been falsified.
303   *
304   * <p>Note: this method is should NOT be used with the antecedent approach.
305   *
306   * @see #process_falsified_invs
307   */
308  @RequiresNonNull("suppressor_map")
309  public static void falsified(Invariant inv) {
310
311    if (!dkconfig_enabled || antecedent_method) {
312      return;
313    }
314
315    if (NIS.dkconfig_skip_hashcode_type) {
316
317      boolean hashFound = false;
318
319      for (VarInfo vi : inv.ppt.var_infos) {
320        if (vi.file_rep_type.isScalar()
321            && !vi.file_rep_type.isIntegral()
322            && !vi.file_rep_type.isArray()) {
323          hashFound = true;
324        }
325      }
326
327      if (hashFound) {
328        return;
329      }
330    }
331
332    // Get the suppression sets (if any) associated with this invariant
333    List<NISuppressionSet> ss_list = suppressor_map.get(inv.getClass());
334    if (ss_list == null) {
335      return;
336    }
337
338    long startTime = 0;
339    // Count the number of falsified invariants that are antecedents
340    if (keep_stats) {
341      startTime = System.nanoTime();
342      if (PptTopLevel.first_pass_with_sample && suppressor_map.containsKey(inv.getClass())) {
343        false_invs++;
344      }
345    }
346
347    // Process each suppression set
348    for (NISuppressionSet ss : ss_list) {
349      ss.clear_state();
350      if (debug.isLoggable(Level.FINE)) {
351        debug.fine("processing suppression set " + ss + " over falsified inv " + inv.format());
352      }
353      ss.falsified(inv, new_invs);
354      suppressions_processed += ss.suppression_set.length;
355    }
356
357    if (keep_stats) {
358      duration += (System.nanoTime() - startTime);
359    }
360  }
361
362  /**
363   * Applies sample values to all of the newly created invariants (kept in new_invs). The sample
364   * should never falsify the invariant (since we don't create an invariant if the sample would
365   * invalidate it). The sample still needs to be applied, however, for sample-dependent invariants.
366   *
367   * <p>Clears the new_invs list after processing. Currently this routine checks to insure that the
368   * newly falsified invariant is not itself a possible NI suppressor.
369   */
370  public static void apply_samples(ValueTuple vt, int count) {
371    newly_falsified.clear();
372
373    if (NIS.debug.isLoggable(Level.FINE)) {
374      NIS.debug.fine("Applying samples to " + new_invs.size() + " new invariants");
375    }
376
377    // Loop through each invariant
378    for (Invariant inv : new_invs) {
379      if (inv.is_false()) {
380        assert !inv.is_false()
381            : String.format(
382                "inv %s in ppt %s is false before sample is applied ", inv.format(), inv.ppt);
383      }
384
385      // Looks to see if any variables are missing.  This can happen
386      // when a variable not involved in the suppressor is missing on
387      // this sample.
388      boolean missing = false;
389      for (int j = 0; j < inv.ppt.var_infos.length; j++) {
390        if (inv.ppt.var_infos[j].isMissing(vt)) {
391          missing = true;
392          break;
393        }
394      }
395
396      // If no variables are missing, apply the sample
397      if (!missing) {
398        InvariantStatus result = inv.add_sample(vt, count);
399        if (result == InvariantStatus.FALSIFIED) {
400          if (NIS.antecedent_method) {
401            throw new Error(
402                "inv "
403                    + inv.format()
404                    + " falsified by sample "
405                    + Debug.toString(inv.ppt.var_infos, vt)
406                    + " at ppt "
407                    + inv.ppt);
408          } else {
409            inv.falsify();
410            newly_falsified.add(inv);
411          }
412        }
413      }
414
415      // Add the invariant to its slice
416      if (Debug.dkconfig_internal_check) {
417        assert inv.ppt.parent.findSlice(inv.ppt.var_infos) == inv.ppt;
418      }
419      inv.ppt.addInvariant(inv);
420      if (Debug.logOn()) {
421        inv.log("%s added to slice", inv.format());
422      }
423
424      if (NIS.antecedent_method) {
425        created_invs_cnt++;
426      }
427    }
428
429    // Make a second pass through the new invariants and make sure that
430    // they are still not suppressed.  They can become suppressed when
431    // there are recursive suppressions and the new suppressor wasn't
432    // yet created above when the invariant was first checked to see
433    // if it was suppressed.
434    for (Iterator<Invariant> i = new_invs.iterator(); i.hasNext(); ) {
435      Invariant inv = i.next();
436      // inv.log ("Considering whether still suppressed in second pass");
437      if (inv.is_ni_suppressed()) {
438        still_suppressed_cnt++;
439        inv.log("removed, still suppressed in second pass");
440        inv.ppt.invs.remove(inv);
441        i.remove();
442      }
443    }
444
445    new_invs.clear();
446  }
447
448  /** Clears the current NIS statistics and enables the keeping of statistics. */
449  public static void clear_stats() {
450
451    keep_stats = true;
452    duration = 0;
453    false_invs = 0;
454    false_cnts = 0;
455    suppressions_processed = 0;
456    suppressions_processed_falsified = 0;
457    new_invs_cnt = 0;
458    false_invs_cnt = 0;
459    created_invs_cnt = 0;
460    still_suppressed_cnt = 0;
461  }
462
463  public static void clear_sample_stats() {
464    keep_stats = true;
465    false_invs = 0;
466    false_cnts = 0;
467    suppressions_processed = 0;
468    suppressions_processed_falsified = 0;
469    new_invs_cnt = 0;
470    false_invs_cnt = 0;
471    created_invs_cnt = 0;
472    still_suppressed_cnt = 0;
473  }
474
475  public static void stats_header(Logger log) {
476
477    log.fine(
478        "false invs  : "
479            + "suppressions processed  : "
480            + "new invs cnt  : "
481            + "false invs cnt  : "
482            + "created invs cnt  : "
483            + "still suppressed cnt  : "
484            + "elapsed time msecs : "
485            + "ppt name");
486  }
487
488  /** dump statistics on NIS to the specified logger. */
489  public static void dump_stats(Logger log, PptTopLevel ppt) {
490
491    if (first_time) {
492      stats_header(log);
493      first_time = false;
494    }
495
496    if (false_invs > 0) {
497      log.fine(
498          false_invs
499              + " : "
500              + suppressions_processed
501              + " : "
502              + new_invs_cnt
503              + " : "
504              + false_invs_cnt
505              + " : "
506              + created_invs_cnt
507              + " : "
508              + still_suppressed_cnt
509              + " : "
510              + TimeUnit.NANOSECONDS.toMillis(duration)
511              + " msecs "
512              // + build_ants_msecs + " " + process_ants_msecs + " : "
513              + ppt.name);
514    }
515  }
516
517  /**
518   * Creates any invariants that were previously suppressed, but are no longer suppressed. Must be
519   * called after the sample has been processed and any invariants falsified by the sample are
520   * marked as such, but before they have been removed.
521   */
522  @RequiresNonNull({
523    "suppressor_map",
524    "suppressor_map_suppression_count",
525    "all_suppressions",
526    "NIS.suppressor_proto_invs"
527  })
528  public static void process_falsified_invs(PptTopLevel ppt, ValueTuple vt) {
529
530    // if using the hybrid method, need to know the number of falsified suppressor
531    // invariants before deciding which method to use
532    if (NIS.hybrid_method) {
533      int count = 0;
534      for (Iterator<Invariant> i = ppt.invariants_iterator(); i.hasNext(); ) {
535        Invariant inv = i.next();
536
537        if (NIS.dkconfig_skip_hashcode_type) {
538
539          boolean hashFound = false;
540
541          for (VarInfo vi : inv.ppt.var_infos) {
542            if (vi.file_rep_type.isHashcode()) {
543              hashFound = true;
544            }
545          }
546
547          if (hashFound) {
548            continue;
549          }
550        }
551
552        if (inv.is_false()) {
553          false_cnts++;
554
555          if (suppressor_map.containsKey(inv.getClass())) {
556
557            // use the following count update when splitting the hybrid method by the
558            // number of total suppressions associated with the falsified invariants
559            @SuppressWarnings("nullness") // map:  same keys in suppressor_map and
560            // suppressor_map_suppression_count
561            int map_count = suppressor_map_suppression_count.get(inv.getClass());
562            count += map_count;
563            suppressions_processed_falsified += map_count;
564          }
565        }
566      }
567
568      if (count > NIS.dkconfig_hybrid_threshhold) {
569        antecedent_method = true;
570      } else {
571        antecedent_method = false;
572      }
573    }
574
575    if (!dkconfig_enabled || !antecedent_method) {
576      return;
577    }
578
579    if (false) {
580      System.out.println("Variables for ppt " + ppt.name());
581      for (int i = 0; i < ppt.var_infos.length; i++) {
582        VarInfo v = ppt.var_infos[i];
583        ValueSet vs = v.get_value_set();
584        System.out.printf(
585            "  %s %s %s %b %s%n",
586            v.comparability, v.name(), v.file_rep_type, ppt.is_constant(v), vs.repr_short());
587      }
588    }
589
590    // If there are no falsified invariants that are suppressors, there is nothing to do
591    int false_cnt = 0;
592    for (Iterator<Invariant> i = ppt.invariants_iterator(); i.hasNext(); ) {
593      Invariant inv = i.next();
594      if (inv.is_false() && suppressor_map.containsKey(inv.getClass())) {
595        false_cnt++;
596      }
597    }
598
599    // System.out.printf("Invariants for ppt %s: %d%n", ppt, inv_cnt);
600    if (false_cnt == 0) {
601      return;
602    }
603
604    if (debugAnt.isLoggable(Level.FINE)) {
605      debugAnt.fine("at ppt " + ppt.name + " false_cnt = " + false_cnt);
606    }
607    // false_invs = false_cnt;
608
609    if (debugAnt.isLoggable(Level.FINE)) {
610      ppt.debug_invs(debugAnt);
611    }
612
613    // Find all antecedents and organize them by their variables comparability
614    Map<VarComparability, Antecedents> comp_ants = new LinkedHashMap<>();
615    store_antecedents_by_comparability(ppt.views_iterator(), comp_ants);
616
617    if (ppt.constants != null) {
618      store_antecedents_by_comparability(
619          ppt.constants.create_constant_invs().iterator(), comp_ants);
620    }
621
622    if (debugAnt.isLoggable(Level.FINE)) {
623      for (Antecedents ants : comp_ants.values()) {
624        debugAnt.fine(ants.toString());
625      }
626    }
627
628    // Add always-comparable antecedents to each of the other maps.
629    merge_always_comparable(comp_ants);
630
631    if (false) {
632      for (Antecedents ants : comp_ants.values()) {
633        List<Invariant> eq_invs = ants.get(IntEqual.class);
634        if ((eq_invs != null) && (eq_invs.size() > 1000)) {
635          Map<VarInfo, Count> var_map = new LinkedHashMap<>();
636          System.out.printf(
637              "ppt %s, comparability %s has %s equality invs%n",
638              ppt.name, ants.comparability, eq_invs.size());
639          for (Invariant inv : eq_invs) {
640            IntEqual ie = (IntEqual) inv;
641            VarInfo v1 = ie.ppt.var_infos[0];
642            VarInfo v2 = ie.ppt.var_infos[1];
643            if (ppt.is_constant(v1) && ppt.is_constant(v2)) {
644              System.out.printf("inv %s has two constant variables%n", ie.format());
645            }
646            if (!v1.compatible(v2)) {
647              System.out.printf("inv %s has incompatible variables%n", ie.format());
648            }
649            Count cnt = var_map.computeIfAbsent(v1, __ -> new Count(0));
650            cnt.val++;
651            cnt = var_map.computeIfAbsent(v2, __ -> new Count(0));
652            cnt.val++;
653          }
654          System.out.printf("%d distinct variables%n", var_map.size());
655          for (VarInfo key : var_map.keySet()) {
656            Count cnt = var_map.get(key);
657            System.out.printf(" %s %s %d %n", key.comparability, key.name(), cnt.val);
658          }
659        }
660      }
661    }
662
663    // Remove any Antecedents without any falsified invariants.  They can't
664    // possibly create any newly unsuppressed invariants
665    for (Iterator<Antecedents> i = comp_ants.values().iterator(); i.hasNext(); ) {
666      Antecedents ants = i.next();
667      if (ants.false_cnt == 0) {
668        i.remove();
669      }
670    }
671    if (debugAnt.isLoggable(Level.FINE)) {
672      for (Antecedents ants : comp_ants.values()) {
673        debugAnt.fine(ants.toString());
674      }
675    }
676
677    // Loop through each suppression creating each invariant that
678    // is suppressed by that suppression.  Each set of comparable antecedents
679    // is processed separately
680    Set<SupInv> unsuppressed_invs = new LinkedHashSet<>();
681    for (NISuppressionSet ss : all_suppressions) {
682      for (NISuppression sup : ss) {
683        suppressions_processed++;
684        for (Antecedents ants : comp_ants.values()) {
685          sup.find_unsuppressed_invs(unsuppressed_invs, ants);
686        }
687      }
688    }
689
690    if (debugAnt.isLoggable(Level.FINE)) {
691      debugAnt.fine(
692          "Found " + unsuppressed_invs.size() + " unsuppressed invariants: " + unsuppressed_invs);
693    }
694
695    // Create each new unsuppressed invariant that is not still suppressed
696    // by a different suppression.  Skip any that will be falsified by
697    // the sample.  Checking the sample is faster than checking suppression
698    // and removes the invariant more often, so it is checked first
699    for (SupInv supinv : unsuppressed_invs) {
700      new_invs_cnt++;
701      if (supinv.check(vt) == InvariantStatus.FALSIFIED) {
702        supinv.log("unsuppressed inv falsified by sample");
703        false_invs_cnt++;
704        continue;
705      }
706      if (supinv.is_ni_suppressed()) {
707        supinv.log("unsuppresed inv still suppressed");
708        still_suppressed_cnt++;
709        continue;
710      }
711      Invariant inv = supinv.instantiate(ppt);
712      if (inv != null) {
713        if (Debug.dkconfig_internal_check) {
714          assert !inv.is_ni_suppressed() : "Still suppressed: " + inv.format();
715          if (inv.ppt.find_inv_exact(inv) != null) {
716            throw new Error("inv " + inv.format() + " already exists in ppt " + ppt.name);
717          }
718        }
719        new_invs.add(inv);
720      }
721    }
722  }
723
724  /**
725   * Merges the always-comparable antecedents (if any) into each of the other sets of antecedents.
726   * Also removes the always-comparable set of antecedents as a separate set (since it is now merged
727   * into each of the other sets). Updates comp_ants accordingly.
728   *
729   * <p>In general, in implicit comparability, the variables at a program point are partioned into
730   * disjoint sets of comparable variables. However, implicit comparability also allows some
731   * variables to be comparable to all others (always-comparable). An invariant is always-comparable
732   * if all of its variables are always-comparable. Since always-comparable invariants can form
733   * suppressions with all other invariants, they must be added to each of set of comparable
734   * antecedents.
735   */
736  @RequiresNonNull("NIS.suppressor_map")
737  static void merge_always_comparable(Map<VarComparability, Antecedents> comp_ants) {
738
739    // Find the antecedents that are always comparable (if any)
740    Antecedents compare_all = null;
741    for (VarComparability vc : comp_ants.keySet()) {
742      if (vc.alwaysComparable()) {
743        compare_all = comp_ants.get(vc);
744        break;
745      }
746    }
747
748    // Add always comparable antecedents to each of the other maps.
749    if ((compare_all != null) && (comp_ants.size() > 1)) {
750      for (Antecedents ants : comp_ants.values()) {
751        if (ants.alwaysComparable()) {
752          continue;
753        }
754        ants.add(compare_all);
755      }
756      comp_ants.remove(compare_all.comparability);
757    }
758  }
759
760  /**
761   * Creates all suppressed invariants for the specified ppt and places them in their associated
762   * slices.
763   *
764   * @return a list of created invariants
765   */
766  @RequiresNonNull({"all_suppressions", "suppressor_map"})
767  public static List<Invariant> create_suppressed_invs(PptTopLevel ppt) {
768
769    // Find all antecedents and organize them by their variables comparability
770    Map<VarComparability, Antecedents> comp_ants = new LinkedHashMap<>();
771    store_antecedents_by_comparability(ppt.views_iterator(), comp_ants);
772
773    // Add always-comparable antecedents to each of the other maps.
774    merge_always_comparable(comp_ants);
775
776    // Loop through each suppression creating each invariant that
777    // is suppressed by that suppression.  Each set of comparable antecedents
778    // is processed separately.
779    Set<SupInv> suppressed_invs = new LinkedHashSet<>();
780    for (NISuppressionSet ss : all_suppressions) {
781      for (NISuppression sup : ss) {
782        for (Antecedents ants : comp_ants.values()) {
783          sup.find_suppressed_invs(suppressed_invs, ants);
784        }
785      }
786    }
787
788    // Create each invariant and add it to its slice.
789    List<Invariant> created_invs = new ArrayList<>(suppressed_invs.size());
790    for (SupInv supinv : suppressed_invs) {
791      Invariant inv = supinv.instantiate(ppt);
792      if (inv != null) {
793        if (Debug.dkconfig_internal_check) {
794          assert inv.ppt.find_inv_exact(inv) == null;
795        }
796        inv.ppt.addInvariant(inv);
797        created_invs.add(inv);
798      }
799    }
800
801    return created_invs;
802  }
803
804  /**
805   * Adds each antecedent invariant in the specified slices to the Antecedents object in comp_ants
806   * with the corresponding VarComparability.
807   */
808  @RequiresNonNull("suppressor_map")
809  static void store_antecedents_by_comparability(
810      Iterator<PptSlice> slice_iterator, Map<VarComparability, Antecedents> comp_ants) {
811
812    for (Iterator<PptSlice> i = slice_iterator; i.hasNext(); ) {
813      PptSlice slice = i.next();
814
815      if (NIS.dkconfig_skip_hashcode_type) {
816
817        boolean hashFound = false;
818
819        for (VarInfo vi : slice.var_infos) {
820          if (vi.file_rep_type.isHashcode()) {
821            hashFound = true;
822          }
823        }
824
825        if (hashFound) {
826          continue;
827        }
828      }
829
830      for (Invariant inv : slice.invs) {
831        if (!is_suppressor(inv.getClass())) {
832          continue;
833        }
834
835        if (inv.is_false()) {
836          false_invs++;
837        }
838
839        VarComparability vc = inv.get_comparability();
840        Antecedents ants = comp_ants.computeIfAbsent(vc, Antecedents::new);
841        ants.add(inv);
842        // if (Debug.logOn())
843        //  inv.log ("Added to antecedent map " + inv.format() + " compare = "
844        //           + vc);
845      }
846    }
847  }
848
849  /**
850   * Processes each slice in slice_iterator and fills the specified map with a list of all of the
851   * antecedent invariants for each class. @return the number of false antecedents found
852   */
853  @RequiresNonNull("suppressor_map")
854  static int find_antecedents(
855      Iterator<PptSlice> slice_iterator,
856      Map<Class<? extends Invariant>, List<Invariant>> antecedent_map) {
857
858    int false_cnt = 0;
859
860    while (slice_iterator.hasNext()) {
861      PptSlice slice = slice_iterator.next();
862      for (Invariant inv : slice.invs) {
863        if (!is_suppressor(inv.getClass())) {
864          continue;
865        }
866        if (inv.is_false()) {
867          false_cnt++;
868        }
869        List<Invariant> antecedents =
870            antecedent_map.computeIfAbsent(inv.getClass(), __ -> new ArrayList<Invariant>());
871        antecedents.add(inv);
872      }
873    }
874
875    return false_cnt;
876  }
877
878  /** Removes any invariants in the specified ppt that are suppressed. */
879  public static void remove_suppressed_invs(PptTopLevel ppt) {
880
881    for (PptSlice slice : ppt.views_iterable()) {
882      // Old-style for loop with Iterator because it will be side-effected
883      for (Iterator<Invariant> j = slice.invs.iterator(); j.hasNext(); ) {
884        Invariant inv = j.next();
885        if (inv.is_ni_suppressed()) {
886          inv.log("Removed because suppressed %s", inv.format());
887          j.remove();
888        }
889      }
890    }
891  }
892
893  /**
894   * Returns true if the specified class is an antecedent in any NI suppression.
895   *
896   * @param cls the class for some subtype of Invariant
897   * @return true if the specified class is an antecedent in any NI suppression
898   */
899  @RequiresNonNull("NIS.suppressor_map")
900  @Pure
901  public static boolean is_suppressor(Class<? extends @MustCallUnknown Invariant> cls) {
902    return suppressor_map.containsKey(cls);
903  }
904
905  /** Dump out the suppressor map. */
906  @RequiresNonNull("suppressor_map")
907  public static void dump(Logger log) {
908
909    if (!log.isLoggable(Level.FINE)) {
910      return;
911    }
912
913    for (Class<? extends Invariant> sclass : suppressor_map.keySet()) {
914      List<NISuppressionSet> suppression_set_list = suppressor_map.get(sclass);
915      for (ListIterator<NISuppressionSet> j = suppression_set_list.listIterator(); j.hasNext(); ) {
916        NISuppressionSet ss = j.next();
917        if (j.previousIndex() > 0) {
918          log.fine(String.format("        : %s", ss));
919        } else {
920          log.fine(String.format("%s: %s", sclass, ss));
921        }
922      }
923    }
924  }
925
926  /**
927   * Class used to describe invariants without instantiating the invariant. The invariant is defined
928   * by its NISuppressee and variables (Its ppt is also stored, but not used in comparisions, its
929   * presumed that only SupInvs from the same ppt will every be compared.)
930   */
931  static class SupInv {
932    NISuppressee suppressee;
933    VarInfo[] vis;
934    PptTopLevel ppt;
935
936    /** Create an invariant definition for a suppressed invariant. */
937    public SupInv(NISuppressee suppressee, VarInfo[] vis, PptTopLevel ppt) {
938      this.suppressee = suppressee;
939      this.vis = vis;
940      this.ppt = ppt;
941      if (Debug.logOn()) {
942        log("Created " + suppressee);
943      }
944    }
945
946    /** Track Log the specified message. */
947    public void log(@UnknownInitialization(SupInv.class) SupInv this, String message) {
948      if (Debug.logOn()) {
949        Debug.log(suppressee.sup_class, ppt, vis, message);
950      }
951    }
952
953    /** Equal iff classes / swap variable / and variables match exactly. */
954    @EnsuresNonNullIf(result = true, expression = "#1")
955    @Pure
956    @Override
957    public boolean equals(@GuardSatisfied SupInv this, @GuardSatisfied @Nullable Object obj) {
958      if (!(obj instanceof SupInv)) {
959        return false;
960      }
961
962      // Class and variables must match
963      SupInv sinv = (SupInv) obj;
964      if (sinv.suppressee.sup_class != suppressee.sup_class) {
965        return false;
966      }
967      if (vis.length != sinv.vis.length) {
968        return false;
969      }
970      for (int i = 0; i < vis.length; i++) {
971        if (vis[i] != sinv.vis[i]) {
972          return false;
973        }
974      }
975
976      // Binary invariants must match swap var as well
977      if (suppressee.var_count == 2) {
978        if (sinv.suppressee.get_swap() != suppressee.get_swap()) {
979          return false;
980        }
981      }
982
983      return true;
984    }
985
986    /** Hash on class and variables. */
987    @Pure
988    @Override
989    public int hashCode(@GuardSatisfied SupInv this) {
990      int code = suppressee.sup_class.hashCode();
991      for (int i = 0; i < vis.length; i++) {
992        code += vis[i].hashCode();
993      }
994      return code;
995    }
996
997    /** Check this invariant against the sample and return the result. */
998    public InvariantStatus check(ValueTuple vt) {
999      return suppressee.check(vt, vis);
1000    }
1001
1002    /** Returns true if the invariant is still suppressed. */
1003    @SuppressWarnings("all:purity") // new object is not returned
1004    @Pure
1005    public boolean is_ni_suppressed() {
1006
1007      NISuppressionSet ss = suppressee.sample_inv.get_ni_suppressions();
1008      assert ss != null
1009          : "@AssumeAssertion(nullness):  dependent:  this invariant's class can be suppressed, so"
1010              + " ss != null";
1011      return ss.suppressed(ppt, vis);
1012    }
1013
1014    /** Instantiate this invariant on the specified ppt. */
1015    public @Nullable Invariant instantiate(PptTopLevel ppt) {
1016      return suppressee.instantiate(vis, ppt);
1017    }
1018
1019    /**
1020     * Returns the invariant if it already exists, or null otherwise. Unary and and ternary
1021     * invariant must match by class (there are no permutations for unary invariants and ternary
1022     * invariants handle permutations as different classes). Binary invariants must match the class
1023     * and if there is an internal swap variable for variable order, that must match as well.
1024     */
1025    public @Nullable Invariant already_exists() {
1026      Invariant cinv = ppt.find_inv_by_class(vis, suppressee.sup_class);
1027      if (cinv == null) {
1028        return null;
1029      }
1030      if (suppressee.var_count != 2) {
1031        return cinv;
1032      }
1033      BinaryInvariant binv = (BinaryInvariant) cinv;
1034      if (binv.is_symmetric()) {
1035        return cinv;
1036      }
1037      if (binv.get_swap() != suppressee.get_swap()) {
1038        return null;
1039      }
1040      return cinv;
1041    }
1042
1043    /** Return string representation of the suppressed invariant. */
1044    @SideEffectFree
1045    @Override
1046    public String toString(@GuardSatisfied SupInv this) {
1047      String[] names = new String[vis.length];
1048      for (int i = 0; i < vis.length; i++) {
1049        names[i] = vis[i].name();
1050      }
1051      return suppressee + "[" + String.join(", ", names) + "]";
1052    }
1053  }
1054
1055  /** Class that organizes all of the antecedent invariants with the same comparability by class. */
1056  static class Antecedents {
1057
1058    /**
1059     * Comparability of the variables in the antecedents. Only variables that are comparable should
1060     * be stored here.
1061     */
1062    VarComparability comparability;
1063
1064    /**
1065     * Map from the antecedent invariants class to a list of the antecedent invariants of that
1066     * class. Allows fast access to invariants by type.
1067     */
1068    Map<Class<? extends Invariant>, List<Invariant>> antecedent_map;
1069
1070    /** Number of antecedents that are false. */
1071    int false_cnt = 0;
1072
1073    /** Create with specified comparability. */
1074    public Antecedents(VarComparability comparability) {
1075
1076      antecedent_map = new LinkedHashMap<>();
1077      this.comparability = comparability;
1078    }
1079
1080    /** Returns true if this contains antecedents that are always comparable. */
1081    public boolean alwaysComparable() {
1082      return comparability.alwaysComparable();
1083    }
1084
1085    /**
1086     * Adds the specified invariant to the list for its class. Falsified invariants are added to the
1087     * beginning of the list, non-falsified ones to the end.
1088     */
1089    @RequiresNonNull("NIS.suppressor_map")
1090    public void add(Invariant inv) {
1091
1092      // Only possible antecedents need to be added
1093      if (!is_suppressor(inv.getClass())) {
1094        return;
1095      }
1096
1097      // Only antecedents comparable to this one should be added
1098      assert VarComparability.comparable(inv.get_comparability(), comparability);
1099
1100      // Ignore antecedents that are missing out of bounds.  They can't
1101      // create any valid invariants (since the suppressee is always over
1102      // the same variables
1103      for (int i = 0; i < inv.ppt.var_infos.length; i++) {
1104        VarInfo v = inv.ppt.var_infos[i];
1105        if (v.missingOutOfBounds()) {
1106          return;
1107        }
1108      }
1109
1110      if (inv.is_false()) {
1111        false_cnt++;
1112      }
1113
1114      // Add the invariant to the map for its class
1115      List<Invariant> antecedents = get(inv.getClass());
1116      if (antecedents == null) {
1117        antecedents = new ArrayList<Invariant>();
1118        antecedent_map.put(inv.getClass(), antecedents);
1119      }
1120      if (inv.is_false()) {
1121        antecedents.add(0, inv);
1122      } else {
1123        antecedents.add(inv);
1124      }
1125    }
1126
1127    /** Adds all of the antecedents specified to the lists for their class. */
1128    @RequiresNonNull("NIS.suppressor_map")
1129    public void add(Antecedents ants) {
1130
1131      for (List<Invariant> invs : ants.antecedent_map.values()) {
1132        for (Invariant inv : invs) {
1133          add(inv);
1134        }
1135      }
1136    }
1137
1138    /**
1139     * Returns a list of all of the antecedent invariants of the specified class. Returns null if
1140     * there are none of that class.
1141     */
1142    public @Nullable List<Invariant> get(Class<? extends Invariant> cls) {
1143
1144      return antecedent_map.get(cls);
1145    }
1146
1147    /** Returns a string representation of all of the antecedents by class. */
1148    @SideEffectFree
1149    @Override
1150    public String toString(@GuardSatisfied Antecedents this) {
1151
1152      String out = "Comparability " + comparability + " : ";
1153
1154      for (Class<? extends Invariant> iclass : antecedent_map.keySet()) {
1155        out += iclass.getSimpleName() + " : ";
1156        List<Invariant> ilist = antecedent_map.get(iclass);
1157        for (Invariant inv : ilist) {
1158          if (inv.is_false()) {
1159            out += inv.format() + "[FALSE] ";
1160          } else {
1161            out += inv.format() + " ";
1162          }
1163        }
1164        out += " : ";
1165      }
1166
1167      return out;
1168    }
1169  }
1170
1171  static class Count {
1172    public int val;
1173
1174    Count(int val) {
1175      this.val = val;
1176    }
1177  }
1178}