parse_file, parse_file_with_imports_defined, and parse_source_text return a program on success and NULL on a source-read or syntax error. Diagnostics use the existing text or JSON format. A bad source file does not exit the host process. Parser temporaries, open input streams, and partial trees are released before the call returns; a subsequent parse can use the same thread normally.

Free successful results with ProgramFree. Source parsing and ProgramRead automatically create a compilation context when no context is selected. The result owns that context. Loading a module graph with ProgramsLoad instead gives the graph one context, released by ProgramsFree.

To construct raw IR or keep multiple programs in one compilation region, select an explicit context:

CompilerContext *context = CompilerContextOpen();
if(context == NULL) return 0;
CompilerContext *previous = CompilerContextSelect(context);
ZirProgram *program = parse_source_text("example.zi",
    "Answer :: () -> s32 { return 42 }\n");
CompilerContextSelect(previous);
int valid = program != NULL && CheckPrograms(&program, 1);
ProgramFree(program);
if(!CompilerContextClose(context)) return 0;
return valid;

CompilerContextClose refuses a selected context or one retained by live programs. Restore the previous selection and free the programs first. Strings, parameter views, field cursors, and source spans borrowed from a context must not be used after that context is destroyed. Copies created by the linker retain their dependencies automatically. Internal code that copies an IR graph must use ProgramInheritContexts and assign copied modules the new graph's lookup context.

Public checking, lowering, and VM entry points select their program's context for the operation and restore the caller's selection afterward. Raw IR construction and internal compiler helpers use the explicitly selected context. Unscoped low-level construction uses the shared process context; long-running hosts should select an explicit region instead.

Each mutable compilation graph has one compiling owner. Separate contexts can parse and check on separate threads. Native backend configuration still has process state, so embedding hosts must serialize native backend operations. Independent VM instances may execute an immutable program concurrently while that program and its contexts remain alive. Closing a context or mutating or freeing its graph during those operations is unsupported. Shared immutable snapshots and scratch-pool publication are synchronized; scratch buffers have one borrower at a time.

ZirFunction.checked records semantic validity. Native symbol planning and body eligibility are separate operations. Native-only functions can be valid even when the portable verifier rejects them. The source/saved-IR checks and the portable verifier must still run before executable use.