All files / TRANSPILE/2-Plan PlanTyping.ts

95% Statements 19/20
90% Branches 18/20
100% Functions 4/4
93.33% Lines 14/15

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76                                    11x                         1214x 1071x 965x                                     147x 147x 210x 210x 162x 162x   39x             210x 162x     162x          
/**
 * #1668: 2.2's readings of the one operand typer's facts -- the policy rows
 * for the plan's typing sites, which the design (ยง5) keeps in one 2-Plan
 * module because several sites share them. The typer reports facts; each row
 * here decides what one site does with them, and nowhere else does.
 */
import type IOperandType from "../../types/IOperandType";
import type TOverflowBehavior from "../../types/TOverflowBehavior";
 
class PlanTyping {
  /**
   * The type a cast converts FROM, for ADR-024's saturation decision: the
   * typer's type, so a call, a callback, a struct field, an element and a C
   * or C++ header's value are typed like a variable, and a float among them
   * saturates. A Boolean -- `!k`, a comparison -- is the typer's `bool`,
   * whatever its operand was: `(u32)!k` converts 0 or 1, nothing to saturate.
   */
  static castSourceType(t: IOperandType | null): string | null {
    return t?.typeName ?? null;
  }
 
  /**
   * The type 2.2 reads an expression AS, for the sites that take one type for
   * the whole of it: the MISRA 10.3 cast on an initializer, a simple
   * assignment, a slice source, a call argument, `~`'s width. A composite or a
   * ternary has no one type there -- its operands are typed one by one, by
   * CompositeType -- so it is null, as it always was; anything else is the
   * typer's type: `int` for an unsuffixed literal, the suffix's for a
   * suffixed one, `bool` for a Boolean.
   */
  static directTypeName(t: IOperandType | null): string | null {
    if (t === null) return null;
    if (t.form.kind === "composite" || t.form.kind === "ternary") return null;
    return t.typeName;
  }
 
  /**
   * ADR-044: whether a composite's arithmetic saturates or wraps, from its
   * value leaves. Safety wins a mix: it wraps only when every counted integer
   * leaf was declared `wrap`, so one saturating operand makes the result
   * saturate (#231's bounds guards). Null when no leaf is counted, and the
   * expression is left alone.
   *
   * A leaf counts only when it is a whole named variable -- an element, a
   * field or a call result has no declared behavior of its own (#1411, #1703
   * stay out). A parameter (#1681) and a `for` variable (#1667) count by
   * their declarations like any local: a parameter has no modifier in the
   * grammar, so it clamps.
   */
  static overflowOf(
    leaves: ReadonlyArray<IOperandType | null>,
  ): TOverflowBehavior | null {
    let counted = false;
    for (const leaf of leaves) {
      const behavior = PlanTyping.countedBehavior(leaf);
      if (behavior === undefined) continue;
      counted = true;
      if (behavior === "clamp") return "clamp";
    }
    return counted ? "wrap" : null;
  }
 
  /** A leaf's behavior when it counts, null for a counted leaf with none */
  private static countedBehavior(
    leaf: IOperandType | null,
  ): TOverflowBehavior | null | undefined {
    if (!leaf?.binding) return undefined;
    Iif (leaf.category !== "signed" && leaf.category !== "unsigned") {
      return undefined;
    }
    return leaf.overflow;
  }
}
 
export default PlanTyping;