001package daikon.inv;
002
003import daikon.PptSlice;
004import daikon.PptTopLevel;
005import daikon.VarInfo;
006import daikon.split.PptSplitter;
007import java.util.Arrays;
008import java.util.List;
009import java.util.logging.Level;
010import java.util.logging.Logger;
011import org.checkerframework.checker.formatter.qual.FormatMethod;
012import org.checkerframework.checker.initialization.qual.UnknownInitialization;
013import org.checkerframework.checker.lock.qual.GuardSatisfied;
014import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
015import org.checkerframework.checker.nullness.qual.Nullable;
016import org.checkerframework.dataflow.qual.Pure;
017import org.checkerframework.dataflow.qual.SideEffectFree;
018import typequals.prototype.qual.NonPrototype;
019import typequals.prototype.qual.Prototype;
020
021// Here Implication is reimplemented as an extension of the new general
022// Joiner class
023
024/**
025 * The Implication invariant class is used internally within Daikon to handle invariants that are
026 * only true when certain other conditions are also true (splitting).
027 */
028public class Implication extends Joiner {
029  static final long serialVersionUID = 20030822L;
030
031  // orig_left and orig_right are the original invariants, in the original
032  // context (the parent of the conditional ppt where predicate and
033  // consequent appear).  predicate and consequent might be permuted from
034  // the original.  orig_left and orig_right are used in computeConfidence,
035  // and orig_right is used in isObvious*.
036  /** The original predicate invariant from its original conditional ppt. */
037  private Invariant orig_left;
038
039  /**
040   * The original consequent invariant from its original conditional ppt. Or, right itself if right
041   * is a DummyInvariant from a splitter file.
042   */
043  private Invariant orig_right;
044
045  public Invariant predicate() {
046    return left;
047  }
048
049  public Invariant consequent() {
050    return right;
051  }
052
053  public boolean iff;
054
055  protected Implication(
056      PptSlice ppt,
057      Invariant predicate,
058      Invariant consequent,
059      boolean iff,
060      Invariant orig_predicate,
061      Invariant orig_consequent) {
062    super(ppt, predicate, consequent);
063    assert (predicate != null);
064    assert (consequent != null);
065    assert (orig_predicate != null);
066    assert (orig_consequent != null);
067    this.iff = iff;
068    this.orig_left = orig_predicate;
069    this.orig_right = orig_consequent;
070  }
071
072  /**
073   * Creates a new Implication Invariant and adds it to the PptTopLevel.
074   *
075   * @return null if predicate and the consequent are the same, or if the PptTopLevel already
076   *     contains this Implication
077   */
078  public static @Nullable Implication makeImplication(
079      PptTopLevel ppt,
080      Invariant predicate,
081      Invariant consequent,
082      boolean iff,
083      Invariant orig_predicate,
084      Invariant orig_consequent) {
085    if (predicate.isSameInvariant(consequent)) {
086      PptSplitter.debug.fine(
087          "Not creating implication (pred==conseq): " + predicate + " ==> " + consequent);
088      return null;
089    }
090
091    Implication result =
092        new Implication(
093            ppt.joiner_view, predicate, consequent, iff, orig_predicate, orig_consequent);
094
095    // Don't add this Implication to the program point if the program
096    // point already has this implication.
097    if (ppt.joiner_view.hasImplication(result)) {
098      return null;
099    }
100
101    if (PptSplitter.debug.isLoggable(Level.FINE)) {
102      PptSplitter.debug.fine("Creating implication " + predicate + " ==> " + consequent);
103    }
104    return result;
105  }
106
107  @Override
108  protected double computeConfidence() {
109    double pred_conf = orig_left.computeConfidence();
110    double cons_conf = orig_right.computeConfidence();
111    if ((pred_conf == CONFIDENCE_NEVER) || (cons_conf == CONFIDENCE_NEVER)) {
112      return CONFIDENCE_NEVER;
113    }
114    double result = confidence_and(pred_conf, cons_conf);
115    log("Confidence %s %s/%s for %s", result, pred_conf, cons_conf, format());
116    return result;
117  }
118
119  @Override
120  public String repr(@GuardSatisfied Implication this) {
121    return "[Implication: " + left.repr() + " => " + right.repr() + "]";
122  }
123
124  @SideEffectFree
125  @Override
126  public String format_using(@GuardSatisfied Implication this, OutputFormat format) {
127    String pred_fmt = left.format_using(format);
128    String consq_fmt = right.format_using(format);
129    if (format == OutputFormat.DAIKON || format == OutputFormat.JML) {
130      String arrow = (iff ? "  <==>  " : "  ==>  "); // "interned"
131      return "(" + pred_fmt + ")" + arrow + "(" + consq_fmt + ")";
132    } else if (format == OutputFormat.ESCJAVA) {
133      String arrow = (iff ? "  ==  " : "  ==>  "); // "interned"
134      return "(" + pred_fmt + ")" + arrow + "(" + consq_fmt + ")";
135    } else if (format == OutputFormat.JAVA) {
136      String mid = (iff ? " == " : " || !"); // "interned"
137      return "(" + consq_fmt + ")" + mid + "(" + pred_fmt + ")";
138    } else if (format == OutputFormat.SIMPLIFY) {
139      String cmp = (iff ? "IFF" : "IMPLIES");
140      return "(" + cmp + " " + pred_fmt + " " + consq_fmt + ")";
141    } else if (format == OutputFormat.DBCJAVA) {
142      if (iff) {
143        return "((" + pred_fmt + ") == (" + consq_fmt + "))";
144      } else {
145        return "(" + pred_fmt + " $implies " + consq_fmt + ")";
146      }
147    } else if (format == OutputFormat.CSHARPCONTRACT) {
148      if (iff) {
149        return "(" + pred_fmt + ") == (" + consq_fmt + ")";
150      } else {
151        return "(" + pred_fmt + ").Implies(" + consq_fmt + ")";
152      }
153    } else {
154      return format_unimplemented(format);
155    }
156  }
157
158  @Pure
159  @Override
160  public @Nullable DiscardInfo isObviousStatically(VarInfo[] vis) {
161    assert vis.length > 0;
162    for (int ii = 0; ii < vis.length; ii++) {
163      assert vis[ii] != null;
164    }
165    return orig_right.isObviousStatically(vis);
166  }
167
168  @Pure
169  @Override
170  public @Nullable DiscardInfo isObviousDynamically(VarInfo[] vis) {
171    assert vis.length > 0;
172    for (int ii = 0; ii < vis.length; ii++) {
173      assert vis[ii] != null;
174    }
175    DiscardInfo di = orig_right.isObviousDynamically(vis);
176    if (di != null) {
177      log("failed isObviousDynamically with vis = %s", Arrays.toString(vis));
178      return di;
179    }
180
181    return null;
182  }
183
184  /**
185   * Return true if the right side of the implication and some equality combinations of its member
186   * variables are statically obvious. For example, if a == b, and f(a) is obvious, then so is f(b).
187   * We use the someInEquality (or least interesting) method during printing so we only print an
188   * invariant if all its variables are interesting, since a single, static, non interesting
189   * occurance means all the equality combinations aren't interesting.
190   *
191   * <p>This must be overridden for Implication because the right side is the invariant of interest.
192   * The standard version passes the vis from the slice containing the implication itself (slice 0).
193   */
194  @Pure
195  @Override
196  public @Nullable DiscardInfo isObviousStatically_SomeInEquality() {
197    return orig_right.isObviousStatically_SomeInEquality();
198    //     DiscardInfo result = isObviousStatically (orig_right.ppt.var_infos);
199    //     if (result != null) {
200    //       return result;
201    //     }
202    //     assert orig_right.ppt.var_infos.length > 0;
203    //     for (int ii = 0; ii < orig_right.ppt.var_infos.length; ii++ )
204    //       assert orig_right.ppt.var_infos[ii] != null;
205    //     return isObviousStatically_SomeInEqualityHelper (orig_right.ppt.var_infos,
206    //                      new VarInfo[orig_right.ppt.var_infos.length], 0);
207  }
208
209  /**
210   * Return true if the rightr side of the implication some equality combinations of its member
211   * variables are dynamically obvious. For example, a == b, and f(a) is obvious, so is f(b). We use
212   * the someInEquality (or least interesting) method during printing so we only print an invariant
213   * if all its variables are interesting, since a single, dynamic, non interesting occurance means
214   * all the equality combinations aren't interesting.
215   *
216   * <p>This must be overridden for Implication because the right side is the invariant of interest.
217   * The standard version passes the vis from the slice containing the implication itself (slice 0).
218   */
219  @Pure
220  @Override
221  public @Nullable DiscardInfo isObviousDynamically_SomeInEquality() {
222
223    // If the consequent is ni-suppressed in its original program point,
224    // then it is obvious from some set of other invariants.  Those invariants
225    // could be other implications or they could be true at both conditional
226    // points.
227    // JHP: Seemingly it would be better if this invariant was never
228    // created, but somehow that creates other implications.  See the
229    // disabled code in PptSplitter.add_implication()
230    if (orig_right.is_ni_suppressed()) {
231      return new DiscardInfo(
232          this, DiscardCode.obvious, "consequent " + orig_right.format() + " is ni suppressed");
233    }
234
235    return orig_right.isObviousDynamically_SomeInEquality();
236    //     DiscardInfo result = isObviousDynamically (orig_right.ppt.var_infos);
237    //     if (result != null)
238    //       return result;
239    //     return isObviousDynamically_SomeInEqualityHelper (orig_right.ppt.var_infos,
240    //                                  new VarInfo[right.ppt.var_infos.length], 0);
241  }
242
243  @Pure
244  @Override
245  public boolean isSameFormula(Invariant other) {
246    Implication other_implic = (Implication) other;
247    return (iff == other_implic.iff) && super.isSameFormula(other_implic);
248  }
249
250  @EnsuresNonNullIf(result = true, expression = "#1")
251  @Pure
252  @Override
253  public boolean isSameInvariant(Invariant other) {
254    if (other == null) {
255      return false;
256    }
257    if (!(other instanceof Implication)) {
258      return false;
259    }
260    if (iff != ((Implication) other).iff) {
261      return false;
262    }
263    return super.isSameInvariant(other);
264  }
265
266  // If a constant managed to appear in a predicate, that's
267  // interesting enough for us.
268  @Override
269  public boolean hasUninterestingConstant() {
270    return consequent().hasUninterestingConstant();
271  }
272
273  @Pure
274  @Override
275  public boolean isAllPrestate() {
276    return predicate().isAllPrestate() && consequent().isAllPrestate();
277  }
278
279  // Using `@link` leads to javadoc -Xdoclint:all crashing with:
280  // "com.sun.tools.javac.code.Type$AnnotatedType cannot be cast to
281  // com.sun.tools.javac.code.Type$ClassType"
282  /**
283   * Logs a description of the invariant and the specified msg via the logger as described in {@code
284   * daikon.Debug#log(Logger, Class, Ppt, VarInfo[], String)}. Uses the consequent as the logger.
285   */
286  @Override
287  public void log(
288      /*NOT: @UnknownInitialization(Implication.class) Implication this,*/ Logger log, String msg) {
289
290    right.log(
291        log,
292        msg
293            + "[for implication "
294            + format()
295            + " ("
296            + (orig_right == null ? "null" : orig_right.format())
297            + ")]");
298  }
299
300  // Using `@link` leads to javadoc -Xdoclint:all crashing with:
301  // "com.sun.tools.javac.code.Type$AnnotatedType cannot be cast to
302  // com.sun.tools.javac.code.Type$ClassType"
303  /**
304   * Logs a description of the invariant and the specified msg via the logger as described in {@code
305   * daikon.Debug#log(Logger, Class, Ppt, VarInfo[], String)}. Uses the consequent as the logger.
306   *
307   * @return whether or not it logged anything
308   */
309  @Override
310  @FormatMethod
311  @SuppressWarnings({
312    "nullness:override.receiver", // sound overriding, not expressible in Checker Framework
313  })
314  public boolean log(
315      @UnknownInitialization(Implication.class) Implication this,
316      String format,
317      @Nullable Object... args) {
318    String msg = (args.length == 0) ? format : String.format(format, args);
319    @SuppressWarnings("nullness:method.invocation")
320    String formatted = format();
321    return right.log(
322        "%s [for implication %s (%s)]",
323        msg, formatted, (orig_right == null ? "null" : orig_right.format()));
324  }
325
326  @Override
327  public boolean enabled(@Prototype Implication this) {
328    throw new Error("do not invoke " + getClass() + ".enabled()");
329  }
330
331  @Override
332  public boolean valid_types(@Prototype Implication this, VarInfo[] vis) {
333    throw new Error("do not invoke " + getClass() + ".valid_types()");
334  }
335
336  @Override
337  protected @NonPrototype Invariant instantiate_dyn(@Prototype Implication this, PptSlice slice) {
338    throw new Error("do not invoke " + getClass() + ".instantiate_dyn()");
339  }
340
341  @Override
342  public @Nullable @NonPrototype Implication merge(
343      @Prototype Implication this, List<@NonPrototype Invariant> invs, PptSlice parent_ppt) {
344    throw new Error("do not merge implications");
345  }
346}