Skip to content

WebGPU: reservedNames is missing 64 WGSL reserved words, so addFunction(function filter/get/set/type/...) fails to compile #861

Description

@fuzzie360

Verified on develop (branch HEAD as of 2026-08-02), Chrome headless, Apple Metal-3 adapter.

Summary

mangleFunctionName() in src/backend/web-gpu/function-node.js rewrites a user function name to fn_<name> when it appears in the module-level reservedNames list. That list has 98 entries and is missing 64 WGSL reserved words that are legal JavaScript function names, so gpu.addFunction(function filter(...)) — and 63 others — emits the bare name into the generated WGSL and the shader fails to compile.

This is a papercut, not a correctness hazard: it fails loudly, because WebGPUKernel already checks getCompilationInfo() and throws with the line number and the generated WGSL attached. Nothing silently returns a wrong answer. The fix is to extend one array.

Reproduction

const gpu = new GPU({ mode: 'webgpu' });
gpu.addFunction(function self(x) { return x * 2; });          // any of the 64 below
const k = gpu.createKernel(function (a) {
  return self(a[this.thread.x]);
}).setOutput([4]);
await k(new Float32Array([1, 2, 3, 4]));
Error compiling WGSL compute shader:
  15:4 'self' is a reserved keyword
--- generated WGSL ---

A function named double in the same harness works, and a kernel local named self also works — locals are emitted as user_<name> (function-node.js:218, :667), so only function names reach mangleFunctionName unprefixed.

The 64, measured not guessed

Each was compiled individually as an addFunction name; these are the ones that failed specifically with a reserved-keyword diagnostic:

common compile consteval constexpr crate enum explicit extern filter final
friend from get goto impl implements inline interface layout macro match mod
module move mut namespace nil of operator package partition pass premerge priv
protected pub public readonly ref regardless resource restrict self set shared
sizeof smooth static subroutine template trait type union unless unsafe unsized
use using virtual volatile wgsl where writeonly yield

Several are names people really do use for helper functions — filter, get, set, type, match, from, module, interface, static, public, shared, ref.

A further 13 candidates (do, export, finally, import, …) are JavaScript reserved words too, so they cannot be function names in the first place and are correctly out of scope. 10 more compiled fine and are not WGSL-reserved.

Suggested fix

Add the list above to reservedNames in src/backend/web-gpu/function-node.js. The existing fn_ mangling then handles them, and the comment above mangleFunctionName ("mangle rather than reject") already describes the intended behaviour — the list simply hasn't caught up with the full WGSL reserved-word set.

Worth considering: mangling every user function name unconditionally (fn_<name> always) removes the need to track the spec's reserved list at all, the way user_<name> already does for variables. That is how the variable path avoids this entire class.

Context

Found while building a benchmark suite that runs the same workloads through gpu.js on WebGPU/WebGL2/WebGL/CPU and against hand-written baselines, on ~35 kernels. Happy to test a patch against that suite.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions