All files / transpiler/output/codegen/subscript SubscriptClassifier.ts

100% Statements 12/12
100% Branches 16/16
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 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                                                                                                                            73x     73x 2x       71x   71x   49x       22x                                       77x     5x       72x 48x       24x          
/**
 * Issue #579 / Issue #1100: Shared subscript classifier for array vs bit access
 *
 * This utility unifies the classification logic used by:
 * - AssignmentClassifier (assignment path)
 * - CodeGenerator._generatePostfixExpr (expression path)
 *
 * The classification rule is:
 * 1. If isArray or isString -> array access
 * 2. Otherwise -> bit manipulation
 *
 * This rule is identical for parameters and local variables. ADR-006 requires
 * array parameters to use explicit array syntax (`u8[8] buf`), which sets
 * isArray on the parameter's type info exactly like a local array declaration
 * does. A scalar-typed parameter (no array brackets) is therefore classified
 * the same way a scalar local variable is: bit manipulation (ADR-007 — "Any
 * integer type can be indexed to access individual bits", with no carve-out
 * for parameters).
 *
 * Issue #1100: A prior version of this classifier treated ANY function
 * parameter without explicit array syntax as array access (`isParameter &&
 * !isArray -> array`), added in #579 to support buffer-style parameters
 * declared without brackets (e.g. `void fillBuffer(u8 buf)`). That blanket
 * rule was a divergent decision path from local-variable classification: it
 * silently broke ADR-007 bit-indexing for scalar parameters (`u32 v; ...
 * v[4]` inside a function became `v[4]` array-subscript C code on a pointer
 * instead of a shift-and-mask bit read), producing invalid/incorrect C
 * for the extremely common embedded pattern of reading a bit out of a
 * hardware-register-shaped scalar parameter. Buffer-style parameters must now
 * use explicit array syntax (`u8[N] buf`), which was already the ADR-006
 * documented and supported spelling — see tests/params/param-array-indexing.test.cnx.
 */
import TSubscriptKind from "./TSubscriptKind";
import TTypeInfo from "../types/TTypeInfo";
 
/**
 * Context needed for subscript classification
 */
interface ISubscriptContext {
  /** Type information for the variable being subscripted, null if unknown */
  typeInfo: TTypeInfo | null;
  /** Number of subscript expressions (1 for single index, 2 for range/slice) */
  subscriptCount: number;
  /** Whether this is a register access (registers always use bit manipulation) */
  isRegisterAccess?: boolean;
}
 
/**
 * Classifies subscript operations as array or bit access.
 *
 * This class provides a single source of truth for determining whether
 * a subscript operation like `x[i]` or `x[a, b]` should be treated as
 * array element access or bit manipulation.
 */
class SubscriptClassifier {
  /**
   * Classify a subscript operation.
   *
   * @param ctx - Classification context with type info and subscript count
   * @returns The kind of subscript operation (array or bit access)
   */
  static classify(ctx: ISubscriptContext): TSubscriptKind {
    const { typeInfo, subscriptCount, isRegisterAccess } = ctx;
 
    // Registers always use bit manipulation
    if (isRegisterAccess) {
      return subscriptCount === 2 ? "bit_range" : "bit_single";
    }
 
    // Check if this should be array access
    const isArrayAccess = SubscriptClassifier.isArrayAccess(typeInfo);
 
    if (isArrayAccess) {
      // Slice vs element access
      return subscriptCount === 2 ? "array_slice" : "array_element";
    }
 
    // Default: bit manipulation
    return subscriptCount === 2 ? "bit_range" : "bit_single";
  }
 
  /**
   * Determine if a type should use array access semantics.
   *
   * Array access is used when:
   * - Type is explicitly an array (isArray: true) — including array
   *   parameters declared with explicit syntax, e.g. `u8[8] buf` (ADR-006)
   * - Type is a string (strings are char arrays)
   *
   * Parameters are NOT special-cased here (Issue #1100): whether a subscript
   * is array access or bit access depends solely on the declared type, the
   * same rule used for local variables. A scalar parameter without array
   * syntax is bit-indexable exactly like a scalar local variable (ADR-007).
   *
   * @param typeInfo - Type information, or null if unknown
   * @returns true if subscript should be treated as array access
   */
  static isArrayAccess(typeInfo: TTypeInfo | null): boolean {
    if (!typeInfo) {
      // Unknown type - default to array access for safety
      // This matches the expression path's fallback behavior
      return true;
    }
 
    // Explicit array or string -> array access
    if (typeInfo.isArray || typeInfo.isString) {
      return true;
    }
 
    // Otherwise it's a scalar - use bit access
    return false;
  }
}
 
export default SubscriptClassifier;