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 | 1204x 1204x 694x 505x 189x 1204x 1208x 421x 787x 1208x 210x 577x 1208x 1208x 11x | /**
* Issue #1106: shared validation for subscript-chain depth.
*
* Per ADR-036 each subscript peels one array dimension; per ADR-007 a scalar
* integer/float may be bit-indexed once. So a base variable allows at most
* `arrayDimensions + 1` subscript operations. A deeper chain indexes a value
* that is not an array — e.g. `flags[4][3]` on a scalar `u8`, where `flags[4]`
* is already a single bit.
*
* This is the single source of truth for the "how many subscripts may this
* base take?" decision. Both the assignment (write) path
* (AssignmentClassifier) and the expression (read) path
* (PostfixExpressionGenerator) consult it, so the two paths cannot diverge.
* It owns the *counting* as well as the check (`countLeadingSubscripts`):
* sharing only the check while each path re-derived the count would leave the
* two agreeing by coincidence rather than by construction.
*/
import TTypeInfo from "../../../types/TTypeInfo";
import TypeCheckUtils from "../../../../utils/TypeCheckUtils";
import CodeGenErrors from "../helpers/CodeGenErrors";
/**
* Structural shape common to `postfixOp` (read path) and `postfixTargetOp`
* (write path). Both expose their bracket contents via `expression()`: a
* subscript has 1 (`[i]`) or 2 (`[start, width]`), while a member access or a
* call has none.
*/
interface IPostfixOpLike {
expression(): unknown[];
}
class SubscriptDepthValidator {
/**
* Count the leading run of subscript operations applied directly to a base,
* starting at `startIndex` and stopping at the first operation that changes
* the type (member access or call) — after that, subsequent subscripts apply
* to a different value, not to this base.
*
* Counts OPERATIONS, not expressions: the bit range `flags[4, 3]` is one
* operation with two expressions, whereas the chain `flags[4][3]` is two
* operations with one each. Conflating them would reject valid bit ranges.
*
* `startIndex` skips operations already consumed in identifying the base —
* on the read path `this.flags[4][3]` parses as `this` plus the ops
* `.flags`, `[4]`, `[3]`, so the member op is skipped with `startIndex: 1`.
* The write path needs no offset: `assignmentTarget` consumes the `this .
* IDENTIFIER` prefix in the grammar rule itself, so its ops are subscripts
* from index 0.
*/
static countLeadingSubscripts(
ops: readonly IPostfixOpLike[],
startIndex = 0,
): number {
let count = 0;
for (let index = startIndex; index < ops.length; index++) {
if (ops[index].expression().length === 0) {
break;
}
count++;
}
return count;
}
/**
* Validate that a leading run of `subscriptOpCount` subscript operations
* applied directly to `varName` (declared type `typeInfo`) is within range.
*
* Only integer/float bases are checked: those are the bit-indexable scalar
* element types (ADR-007). Strings are char arrays with their own semantics,
* bitmaps reject bracket indexing elsewhere, and struct/other bases are
* handled by the member-access paths — none are validated here.
*
* @throws when the chain is deeper than `arrayDimensions + 1`.
*/
static validate(
typeInfo: TTypeInfo | undefined,
subscriptOpCount: number,
varName: string,
line: number,
): void {
if (!typeInfo || typeInfo.isString || typeInfo.isBitmap) {
return;
}
const isBitIndexable =
TypeCheckUtils.isInteger(typeInfo.baseType) ||
TypeCheckUtils.isFloat(typeInfo.baseType);
if (!isBitIndexable) {
return;
}
const arrayDimensions = typeInfo.arrayDimensions?.length ?? 0;
const maxDepth = arrayDimensions + 1;
if (subscriptOpCount > maxDepth) {
throw CodeGenErrors.tooManySubscripts(
line,
varName,
typeInfo.baseType,
arrayDimensions,
);
}
}
}
export default SubscriptDepthValidator;
|