All files / utils CompositeType.ts

100% Statements 24/24
100% Branches 24/24
100% Functions 4/4
100% Lines 18/18

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 77                                                          427x 405x 405x 405x 797x 792x 611x 608x 797x   402x                               731x 731x 817x 28x 19x   722x         538x   931x                
/**
 * #1668: the one rule for the integer type of a composite -- `a + b * c` --
 * from its value leaves, shared by 2.1 (the conversion checks) and 2.2 (the
 * ADR-044 clamp helper's width).
 *
 * Both passes used to apply their own copy (`PrimitiveKindUtils.
 * widestIntegerOf` over operand-type strings each pass collected its own
 * way); this reads the leaves `OperandTyper.valueLeaves` collects, so the
 * two passes cannot count a different set.
 */
import type IOperandType from "../types/IOperandType";
 
class CompositeType {
  /**
   * The widest integer among the counted leaves, signed if the first counted
   * leaf is, or null when the composite is not integer arithmetic:
   *
   * - a floating leaf vetoes it, and so does one whose category is unknown
   *   because a C++ overload set disagrees -- integer clamp helpers must not
   *   see either. A header's float macro (`#define SCALE 2.5f`) is not seen
   *   as floating at all, having no type C-Next can read, so the veto does
   *   not cover it (#1688);
   * - an integer leaf of unknown width (a C `int_fast16_t`) vetoes it too:
   *   the helper would be sized by a guess;
   * - leaves with no essential category (an unsuffixed literal, a struct),
   *   Boolean leaves and single bits are not counted;
   * - a bit range counts at its width; a suffixed literal at its suffix's.
   */
  static integerOf(leaves: ReadonlyArray<IOperandType | null>): string | null {
    if (CompositeType.anyFloating(leaves)) return null;
    let sign: "i" | "u" | null = null;
    let width = 0;
    for (const leaf of leaves) {
      if (leaf === null) continue;
      if (leaf.category !== "signed" && leaf.category !== "unsigned") continue;
      if (leaf.bitWidth === null) return null;
      sign ??= leaf.category === "signed" ? "i" : "u";
      width = Math.max(width, leaf.bitWidth);
    }
    return sign !== null && width > 0 ? `${sign}${width}` : null;
  }
 
  /**
   * The floating type of a composite -- or a conditional's two arms -- whose
   * leaves are floating: C's usual arithmetic conversions give f64 when any
   * floating leaf is f64, else the floating leaves' type. Null when no leaf
   * is floating. Leaves with no essential category (an unsuffixed literal)
   * are not counted, and the caller has settled that no leaf is an integer.
   *
   * #1760 review: a composite whose leaves had different type names --
   * `k * 2.0`, f32 x f64 literal -- was untyped, so a cast of it skipped
   * ADR-024's saturation and was emitted raw, which is undefined for an
   * out-of-range float.
   */
  static floatingOf(leaves: ReadonlyArray<IOperandType | null>): string | null {
    let floating: string | null = null;
    for (const leaf of leaves) {
      if (leaf?.category !== "floating") continue;
      if (leaf.typeName === "f64") return "f64";
      floating ??= leaf.typeName;
    }
    return floating;
  }
 
  /** Whether a floating or indeterminate leaf makes this non-integer arithmetic */
  static anyFloating(leaves: ReadonlyArray<IOperandType | null>): boolean {
    return leaves.some(
      (leaf) =>
        leaf !== null &&
        (leaf.category === "floating" ||
          (leaf.form.kind === "foreign" && leaf.form.indeterminate)),
    );
  }
}
 
export default CompositeType;