Rule 11.10
Chapter 11, Targets and the oracle Test
A compiled program’s manifest, what packModule() returns and what the default export of a module’s host import is, must be one JSON object that carries its schema (schema, 1) and the package version that wrote it (compiler); each binding with its group and slot, its resource in reflect()’s vocabulary, the stages that reach it, and a buffer’s byte layout with every offset, size and stride under the rule of its space (Rule 6.8), the _fp64 guard the emit adds among them; each entry with its stage, its workgroup size, its inputs and outputs with their locations, builtins and interpolation, the bindings it reaches and which it writes, a vertex entry’s vertex buffer, and its line; the overrides and the WebGPU features; with console, the recorded variant, its WGSL, its log table and its bindings; with ir, the program as portable IR and the package version that wrote it, which emitted again gives every other field byte for byte; and what the WebGL2 tier uses, each full-screen fragment entry’s GLSL ES 3.00 program, block names and texture-sampler pairs, and each storage array’s data texture.
A sampled texture’s resource must carry its sampleType, the word its bind group layout takes, from its element and the calls that read it: depth for a depth texture, uint or sint for an integer one, float for an f32 texture that a call of an entry, or of a function an entry calls, pairs with a sampler (a textureSample of any form or a textureGather, through a helper’s parameters or a const of the texture as well), and unfilterable-float for every other f32 texture: one the entries only load, measure or count, one no entry reaches, and a multisampled one, which no sampler reads.
A module with no entry is read whole, since a host writes its entries over it, and one with a raw statement in what its entries reach, whose text no analysis reads, has each f32 texture but a multisampled one float; reflect() reports the same word, and the WebGL2 tier’s texture-sampler pairs are read from the same calls.
A manifest packed under emit options (packModule(m, { emit }): an optimization level, parens, fp64Flavor and plugins) must hold the WGSL, the recorded variant’s WGSL, the GLSL (each WebGL2 draw’s program among it) and the bindings those options emit, the _fp64 guard of a module that emulates f64 among the bindings under the float flavor and none under the integer one; the GLSL writer has no level, so level is the WGSL’s alone.
It must record the level and the two named options, emit, so that the IR emitted again is emitted under them and gives every other field byte for byte.
A plugin is a function, which a manifest cannot record: ir with a plugin must be a TypeError that says the load-time emitter could not emit the program again, and a word an option does not take, which a writer reads as another, must be a TypeError that names what the option takes.
It is built from the IR alone, with no TypeScript, so every producer builds the same one; a reader must refuse a schema it does not know, naming both.
The rule text and the parts under it are the compiler's own English, as the design document writes them.
Rationale
a host that runs a program needed reflect() for its offsets, parsed type strings for its resources, and re-derived what the call layer computed privately; a stored program with no version cannot say which runtime reads it, which an engine’s shader cache needs; a texture’s sample type follows from the calls that read it, which only the compiler has, and a wrong one is a pipeline WebGPU refuses: float for an r32float texture the program only loads (#404), unfilterable-float for one a filtering sampler reads; a host that shows or ships a program under other emit options (an application’s WGSL tab) must run that program and not the default one, whose bindings the flavor changes (the guard), and a manifest that did not record how it was emitted could not be emitted again to agree with itself.
Derives from
changes 0025 (section 1) and 0028 (sections 2 and 4) in changes/; issues #335 (principle 4) and #404; Rule 6.8; measured on Dawn (Chromium 153 on SwiftShader): a texture laid out unfilterable-float beside a filtering sampler is refused when a textureSample, a textureSampleLevel, a textureGather, a helper’s parameters or a let pair them, and accepted when the texture is only loaded.
How it is verified
Checked by a test. A test, a gate script or a CI workflow names this rule, and the traceability check fails when a file listed below stops naming it.
Where the rule says the compiler enforces it:
buildManifest in src/core/manifest.ts, which packModule in src/compiler/ts/pack.ts and hostFace in src/compiler/ts/host-face.ts call, and which checks the emit options and hands the writers theirs, the level to emitModule in src/core/emit.ts; the sample type by reflect() in src/core/reflect.ts over the pairs texturePairs in src/core/passes/texture-pairs.ts reads; pinned by src/core/manifest.test.ts, over every example (schema, version, JSON round trip, slots, entries, offsets, vertex layout, the recorded variant, a data texture’s format against the GLSL, the call layer’s layouts, each texture’s sample type against the calls in its WGSL, and for each level and flavor the WGSL, the GLSL, the bindings, the recorded variant and the WebGL2 draws those options emit and repack giving the manifest back byte for byte, with each refusal’s sentence), by src/core/passes/texture-pairs.test.ts (each way a call pairs a texture with a sampler, and each it does not), and by src/vite.test.ts, which imports a module’s default export through the plugin and bundles it alone with no CPU tier.
The files that verify it at commit 26de7be8, each at the first line that names the rule:
Explained in
The sections of the surface document that explain this rule, at commit 26de7be8:
See also
Source
The rule at commit 26de7be8:
docs/language-design.md:1191(the design document)reqs/rules/RULE-1110.md(its traceability item)