You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .github/SPEC.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -680,7 +680,7 @@ GNU statement expressions `({…})` are supported. They get their own scope in t
680
680
681
681
**Multi-declarator split trigger:**`should_split_multi_decl` forces a statement split at a comma when there are pending `typeof` memsets (`typeof_var_count > 0`) and the next declarator either has an explicit initializer (`has_init`) or is itself a VLA (`is_vla`) — the latter prevents VLA dimension evaluation before a preceding declarator's memset has run (e.g., `int arr[n], matrix[arr[0]][n];` where `arr[0]` must not be evaluated before `memset(&arr, ...)`). Bracket orelse on the next declarator also triggers a split.
682
682
683
-
**Multi-declarator VM-type split restriction:** When `process_declarators` must split a multi-declarator statement (due to the triggers above), it re-emits the type specifier for the new declaration. If the type specifier contains a variably-modified type — `typeof(expr)` with VLA dimensions (`has_typeof && is_vla`) or `_Atomic(type)` with VLA dimensions (`has_atomic && is_vla`) — the VLA dimension expression would be evaluated a second time at runtime by the backend compiler (ISO C11 §6.7.2.5 — VM type specifiers are evaluated when the declaration is reached). This causes double evaluation of side effects (function calls, `++`, etc.) in the VLA dimension. Prism rejects such splits with a hard error: the user must declare each variable on a separate line. The guard condition is `(type->has_typeof || type->has_atomic) && type->is_vla`. The `_Atomic(...)` VLA detection was added because `parse_type_specifier` now scans `_Atomic(...)` contents for VLA array dimensions (same pattern as the `typeof(...)` scan), including the `)` predecessor for parenthesized pointer types like `_Atomic(int(*)[n])`. This also covers the orelse `stop_comma` continuation paths (const orelse fallback, orelse action). Anonymous struct/union splits are separately rejected because re-emitting the body produces two incompatible anonymous types.
683
+
**Multi-declarator VM-type split restriction:** When `process_declarators` must split a multi-declarator statement (due to the triggers above), it re-emits the type specifier for the new declaration. If the type specifier contains a variably-modified type — `typeof(expr)` with VLA dimensions (`has_typeof && is_vla`) or `_Atomic(type)` with VLA dimensions (`has_atomic && is_vla`) — the VLA dimension expression would be evaluated a second time at runtime by the backend compiler (ISO C11 §6.7.2.5 — VM type specifiers are evaluated when the declaration is reached). This causes double evaluation of side effects (function calls, `++`, etc.) in the VLA dimension. Prism rejects such splits with a hard error: the user must declare each variable on a separate line. The guard condition is `(type->has_typeof || type->has_atomic) && type->is_vla`. The `_Atomic(...)` VLA detection was added because `parse_type_specifier` now scans `_Atomic(...)` contents for VLA array dimensions (same pattern as the `typeof(...)` scan), including the `)` predecessor for parenthesized pointer types like `_Atomic(int(*)[n])`. This also covers the orelse `stop_comma` continuation paths (const orelse fallback, orelse action). Anonymous struct/union splits are separately rejected because re-emitting the body produces two incompatible anonymous types. **Two-Pass Invariant for split detection:** Phase 1D's `p1d_check_multi_decl_constraints` must perfectly simulate Pass 2's split logic. The `split` predicate includes three triggers: (1) `current_decl_has_orelse` — the current declarator's initializer contains an `orelse` keyword, which unconditionally forces a split via `process_init_orelse_hit` in Pass 2; (2) `any_would_memset` — a preceding declarator requires a typeof/VLA memset and the next declarator has an initializer; (3) bracket orelse on the next declarator. Without the first trigger, Phase 1D would approve VM-type and anonymous struct multi-declarators where the current declarator's orelse forces a split invisible to the static analyzer, causing Pass 2 to crash mid-emission (violating the Two-Pass Invariant). The `any_would_memset` arm also checks `nd.is_vla` (next declarator has VLA dimensions), matching Pass 2's `should_split_multi_decl` which splits when `typeof_var_count > 0 && (next_decl.has_init || next_decl.is_vla)` — without the VLA check, a `typeof(int[n]) arr, buf[get_n()]` declaration would bypass Phase 1D and crash in Pass 2.
684
684
685
685
**Const orelse VM-type restriction:**`handle_const_orelse_fallback` emits the type specifier twice — once for the mutable temporary and once for the final `const` declaration. When the type is variably-modified (`type->is_vla || decl.is_vla`), this forces the C compiler to evaluate VLA size expressions twice at runtime (ISO C11 §6.7.2.5). Prism rejects const-qualified VM-type orelse with a hard error: the user must hoist the value to a non-const variable first. This covers both VLA dimensions in the declarator suffix (e.g. `const int (*p)[get_size()]`) and in the type specifier via typeof (e.g. `const typeof(int[n]) *p`).
0 commit comments