raw
Opt a variable out of zero-initialization when you know it will be overwritten immediately. Surgical — doesn't affect anything else.
Overview
Prism zero-initializes all local variables by default. This is correct and safe for the vast majority of code. But there are real cases where the zeroing is unnecessary overhead:
- A large buffer you're about to fill with
read()orrecv() - A struct that an API function fully initializes (
stat(),getaddrinfo()) - A variable assigned unconditionally in the very next statement
- An inner-loop variable where zeroing is measurable overhead
raw opts one specific variable out without touching anything else. The rest of the file keeps zero-initialization.
Syntax
Place raw before the type, exactly where you'd write const or static:
raw int x;
raw char buf[4096];
raw struct stat st;
raw with an explicit initializer is valid — the initializer takes effect, raw just skips the automatic zero:
raw int result = compute(); // assigned at declaration, no prior zero
Behavior
Prism strips the raw keyword from the output. The declaration emits exactly as plain C — no initializer unless you wrote one yourself.
Prefix raw — all declarators opt out
When raw appears before the type, all declarators in the statement opt out:
raw int x, y, z; // all three: uninitialized
Per-declarator raw — only that declarator opts out
raw after a comma applies only to that specific declarator. Others are still zero-initialized:
int a, raw b; // a = 0, b: uninitialized
int p, raw q, r; // p = 0, q: uninitialized, r = 0
Examples
Read buffer — the primary use case
raw char buf[4096];
ssize_t n = read(fd, buf, sizeof buf);
if (n < 0) return -1;
// buf[0..n-1] is now valid data
API-filled struct
raw struct stat st;
if (stat(path, &st) != 0) return -1;
printf("%lld bytes
", (long long)st.st_size);
Socket receive buffer
raw uint8_t packet[65536];
ssize_t len = recvfrom(sock, packet, sizeof packet, 0, NULL, NULL);
if (len < 0) return -1;
Inner-loop scratch variable
for (int i = 0; i < n; i++) {
raw char tmp[256];
format_item(tmp, sizeof tmp, items[i]); // fully overwrites tmp
output(tmp);
}
getaddrinfo result
raw struct addrinfo hints;
memset(&hints, 0, sizeof hints); // explicit: getaddrinfo requires full zero
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo *res;
if (getaddrinfo(host, port, &hints, &res) != 0) return -1;
defer freeaddrinfo(res);
Performance-critical path
void transform_block(float *dst, const float *src, int n) {
raw float tmp[8]; // immediately filled below, no memset needed
for (int i = 0; i < n; i += 8) {
load8(tmp, src + i);
apply_filter(tmp);
store8(dst + i, tmp);
}
}
Edge Cases
raw on VLAs — goto check still applies
raw opts out of the memset injection, but does not exempt the variable from the goto-skip check. Jumping past a VLA is always a hard error regardless of raw, because it bypasses implicit stack allocation — which is a language-level undefined behavior, not just an initialization concern.
// This is still an error even with raw:
goto skip;
raw int vla[n]; // Error: goto skips VLA declaration
skip:;
raw non-VLA + goto — fine
// This is allowed — no initialization to bypass:
goto skip;
raw int x; // OK: raw opts out of initialization, goto is safe
skip:;
raw is stripped from output
The raw keyword does not appear in the emitted C. It's a Prism-only annotation. The backend compiler never sees it.
Global opt-out: prism -fno-zeroinit src.c disables zero-initialization for the entire file. Prefer per-variable raw for targeted opt-outs — it makes intent explicit at the declaration site.