All files / transpiler/output/codegen/assignment/handlers SpecialHandlers.ts

95.83% Statements 23/24
91.66% Branches 11/12
100% Functions 3/3
95.83% Lines 23/24

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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119                                  30x                         59x     59x 52x       7x 4x       4x       3x 3x                           12x 12x       12x                             47x 47x         47x 2x     45x   45x           44x 44x 44x       1x           30x            
/**
 * Special assignment handlers (ADR-065).
 *
 * Handles special compound assignment operations:
 * - ATOMIC_RMW: atomic counter +<- 1
 * - OVERFLOW_CLAMP: clamp u8 saturated +<- 200
 */
import AssignmentKind from "../AssignmentKind";
import IAssignmentContext from "../IAssignmentContext";
import TypeCheckUtils from "../../../../../utils/TypeCheckUtils";
import TAssignmentHandler from "./TAssignmentHandler";
import CodeGenState from "../../../../state/CodeGenState";
import TTypeInfo from "../../../../types/TTypeInfo";
import QualifiedNameGenerator from "../../utils/QualifiedNameGenerator";
import AdrProvenance from "../../../../state/AdrProvenance";
 
/** Maps C operators to clamp helper operation names */
const CLAMP_OP_MAP: Record<string, string> = {
  "+=": "add",
  "-=": "sub",
  "*=": "mul",
};
 
/**
 * Get typeInfo for assignment target.
 * Handles simple identifiers, this.member, and global.member patterns.
 */
function getTargetTypeInfo(ctx: IAssignmentContext): {
  typeInfo: TTypeInfo | undefined;
} {
  const id = ctx.identifiers[0];
 
  // Simple identifier
  if (ctx.isSimpleIdentifier) {
    return { typeInfo: CodeGenState.getVariableTypeInfo(id) };
  }
 
  // this.member: lookup using scoped name
  if (ctx.isSimpleThisAccess && CodeGenState.currentScope) {
    const scopedName = QualifiedNameGenerator.forMember(
      CodeGenState.currentScope,
      id,
    );
    return { typeInfo: CodeGenState.getVariableTypeInfo(scopedName) };
  }
 
  // global.member: lookup using direct name
  Eif (ctx.isSimpleGlobalAccess) {
    return { typeInfo: CodeGenState.getVariableTypeInfo(id) };
  }
 
  // Fallback to direct lookup
  return { typeInfo: CodeGenState.getVariableTypeInfo(id) };
}
 
/**
 * Handle atomic read-modify-write: atomic counter +<- 1
 *
 * Delegates to CodeGenerator's generateAtomicRMW which uses
 * LDREX/STREX on supported platforms or PRIMASK otherwise.
 */
function handleAtomicRMW(ctx: IAssignmentContext): string {
  const { typeInfo } = getTargetTypeInfo(ctx);
  const target = CodeGenState.requireGenerator().generateAssignmentTarget(
    ctx.targetCtx,
  );
 
  return CodeGenState.requireGenerator().generateAtomicRMW(
    target,
    ctx.cOp,
    ctx.generatedValue,
    typeInfo!,
  );
}
 
/**
 * Handle overflow-clamped compound assignment: clamp u8 saturated +<- 200
 *
 * Generates calls to cnx_clamp_add_u8, cnx_clamp_sub_u8, etc.
 * Only applies to integers (floats use native C arithmetic with infinity).
 */
function handleOverflowClamp(ctx: IAssignmentContext): string {
  const { typeInfo } = getTargetTypeInfo(ctx);
  const target = CodeGenState.requireGenerator().generateAssignmentTarget(
    ctx.targetCtx,
  );
 
  // Floats use native C arithmetic (overflow to infinity)
  if (TypeCheckUtils.usesNativeArithmetic(typeInfo!.baseType)) {
    return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
  }
 
  const helperOp = CLAMP_OP_MAP[ctx.cOp];
 
  if (helperOp) {
    // #1241: ADR-044's rule firing on the COMPOUND form. The expression form
    // (`a <- a + b`) records in BinaryExprGenerator; this is the `a +<- b` path,
    // which reaches a different decision and would otherwise leave every
    // compound-only fixture without a derivable context. Recorded past the float and
    // helper-lookup gates, so only an actually-lowered clamp claims a cell.
    AdrProvenance.record("044", ctx.targetCtx.start?.line);
    CodeGenState.markClampOpUsed(helperOp, typeInfo!.baseType);
    return `${target} = cnx_clamp_${helperOp}_${typeInfo!.baseType}(${target}, ${ctx.generatedValue});`;
  }
 
  // Fallback for operators without clamp helpers (e.g., /=)
  return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
}
 
/**
 * All special handlers for registration.
 */
const specialHandlers: ReadonlyArray<[AssignmentKind, TAssignmentHandler]> = [
  [AssignmentKind.ATOMIC_RMW, handleAtomicRMW],
  [AssignmentKind.OVERFLOW_CLAMP, handleOverflowClamp],
];
 
export default specialHandlers;