← Prism
Docs
Overview defer orelse zeroinit raw auto-unreachable
Spec Draft
GitHub ↗

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:

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.

Spec Reference

raw — Prism Spec §6.4