namespace explain; include rt; // writes the records of zy --explain-checks (format version 1, docs/SPEC.md // 4.15.4). the compiler embeds this file and compiles it only in that mode; // its analysis calls note once for every bounds check it keeps. no top-level // unbounded here: it would carry over into the unit being explained. const int NOT_LOOP = 1, NOT_INVARIANT = 2, NOT_LEN = 3, SIZE = 4, PACKED = 5, HOISTED = 6, MAYFREE = 7; word src; // one character of the unit's source int byte(int k) { return (src[k >> 2] >> ((k & 3) * 8)) & 255; } // the column (from 1) of the source offset o int col(int o) { int k = o; while (k > 0 && byte(k - 1) != 10) k--; return o - k + 1; } // a json string int str(char* s) { int i = 0, ch; rt.put_c(34); while (s[i]) { ch = s[i] & 255; if (ch == 34 || ch == 92) rt.put_c(92); if (ch < 32) rt.put_c(32); else rt.put_c(ch); i++; } rt.put_c(34); return 0; } int field(char* name) { rt.put_c(44); str(name); rt.put_c(58); return 0; } // the first line int version() { rt.put_s("{\"version\":1}"); rt.put_c(10); return 0; } // one kept check: kind 0 index, 1 pointer; o1 and o2 the source offsets of its [ and ] int note(word unit, int kind, int why, int line, int o1, int end_line, int o2, int depth, char* fn, char* file) { src = unit; rt.put_s("{\"file\":"); str(file); field("line"); rt.put_n(line); field("col"); rt.put_n(col(o1)); field("end_line"); rt.put_n(end_line); field("end_col"); rt.put_n(col(o2)); field("fn"); str(fn); field("kind"); if (kind) str("pointer"); else str("index"); field("loop_depth"); rt.put_n(depth); field("reason"); if (why == NOT_LOOP) str("index-not-loop-variable"); else if (why == NOT_INVARIANT) str("bound-not-invariant"); else if (why == NOT_LEN) str("index-not-len-derived"); else if (why == SIZE) str("size-unknown"); else if (why == PACKED) str("packed-elements"); else if (why == MAYFREE) str("pointer-may-be-freed"); else str("hoisted-to-loop-entry"); field("fix"); if (why == NOT_LOOP) str("index with the variable of a loop for (i = 0; i < bound; i++), or mark the code unbounded if the index is known to be in range"); else if (why == NOT_INVARIANT) str("the loop body changes the index, the bound or the array, takes an address or makes a closure; keep them fixed inside the loop"); else if (why == NOT_LEN) str("bound the loop with len() of the array, or with a constant no larger than its length"); else if (why == SIZE) str("the compiler does not know the size of the block; allocate it here with a constant size, or index a fixed array"); else if (why == PACKED) str("packed elements are checked by the runtime on every access"); else if (why == MAYFREE) str("the loop body calls something that might free or resize the block; move the call out of the loop"); else str("checked once when the loop starts, and on every access only if that check fails; nothing to do"); rt.put_c(125); rt.put_c(10); return 0; }