All files / TRANSPILE/3-Render/codegen/generators/expressions CastExprGenerator.ts

100% Statements 13/13
100% Branches 4/4
100% Functions 2/2
100% Lines 12/12

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                                                                          21x 36x 21x           9x 9x     9x 9x 1x 1x   8x             8x 8x        
/**
 * Cast Expression Generator
 *
 * Renders `(type)expr`, including ADR-024's float-to-integer clamp (#632):
 * C leaves an out-of-range float-to-int conversion undefined, and C-Next's
 * default is to saturate, so the cast expands to a bounded ternary.
 *
 * #1445 box 3: takes the target type and the operand ALREADY RENDERED, not
 * the node. The node was read for exactly four things -- the target type's
 * generated form, its source text, the operand's generated form, and the
 * operand's type -- and inspected for none of them.
 *
 * The two rendered strings are values rather than thunks ON PURPOSE. Both are
 * produced unconditionally by the caller, in that order, and both have side
 * effects: `generateType` can register an include for the target type, and
 * rendering the operand can allocate a `cnx_tmp<N>`. Passing thunks would move
 * the ORDER of those two effects into this module, where a later edit could
 * reverse it and rename every temp in the emitted C -- a diff no test
 * asserts directly. Rendering stays with the walker; this decides shape only.
 */
import invariant from "../../../../../utils/invariant";
import CppModeHelper from "../../helpers/CppModeHelper";
import SaturatingCast from "../../helpers/SaturatingCast";
import ReservedCnxName from "../../../../../utils/ReservedCnxName";
import type IPlannedCast from "../../types/IPlannedCast";
import type TranspileState from "../../../../TranspileState";
 
/**
 * Render a cast expression, as the plan decided its form.
 *
 * Issue #267/#644: C++ mode emits `static_cast` for MISRA compliance, which
 * `CppModeHelper.cast` decides. ADR-024 / Issue #632: a float-to-integer cast
 * saturates; #1668: when its operand has a side effect it calls the
 * single-evaluation helper, because the inline ternary reads the operand up
 * to three times.
 */
function generateCast(plan: IPlannedCast, state: TranspileState): string {
  const cast = (type: string, expr: string): string =>
    CppModeHelper.cast(type, expr, state);
  if (plan.clampForm === null) return cast(plan.targetType, plan.operandCode);
 
  // The plan saturates only a float source into a C-Next integer target
  // (`CastRequirement.requiresClamping`), and every one of those has limit
  // macros. Render re-checked the limits and fell back to a raw cast, a path
  // the plan's decision left unreachable (#1668 review).
  const sourceType = plan.operandType;
  invariant(sourceType !== null, "the plan saturates a typed float source");
 
  // The limit macros come from <limits.h>
  state.requireInclude("limits");
  if (plan.clampForm === "helper") {
    state.markCastHelperUsed(sourceType, plan.targetTypeName);
    return `${ReservedCnxName.castHelper(sourceType, plan.targetTypeName)}(${plan.operandCode})`;
  }
  const expression = SaturatingCast.expression(
    plan.operandCode,
    sourceType,
    plan.targetTypeName,
    plan.targetType,
    cast,
  );
  invariant(expression !== null, "every C-Next integer target has limits");
  return expression;
}
 
export default generateCast;