Strict Typing
In many high-level CPU languages like JavaScript or Python, the runtime or compiler automatically converts variables from one type to another (known as implicit coercion). For example, you can add an integer to a floating-point number without thinking twice:
In WGSL, this operation is a compile-time error. WGSL enforces strict typing with absolutely zero implicit coercion.
This section details why WGSL enforces strict typing and how to write clean, idiomatic code using explicit constructors.
Why Enforce Strict Typing?
Strict typing on the GPU is not a limitation—it is a critical performance and safety feature.
- Zero Runtime Overhead: GPUs are designed for massive parallel math, not for deciding how to cast registers at runtime. Enforcing explicit types ensures that the hardware can execute mathematical instructions directly without wasting clock cycles on hidden, silent type conversions.
- Cross-Vendor Consistency: Shader code must compile and run on thousands of different GPU models from diverse vendors (Apple, AMD, Nvidia, Intel, Qualcomm, ARM). Silent implicit casts behave differently on different driver compilers, often leading to subtle, extremely hard-to-debug rendering bugs or compiler crashes. Strict typing guarantees that if your shader compiles, it will compile and run identically on every GPU.
Common Coercion Pitfalls
Because WGSL does not perform implicit casts, mixing different types results in compilation errors. The most common scenarios include:
1. Mixing Float and Integer Literals
You cannot mix floating-point types (f32) and concrete integer types (i32, u32) in any binary operation.
// ❌ COMPILE ERROR: cannot add f32 and concrete integer
let sum = 1.5 + 2i;
// ✔️ CORRECT: both sides must be of the same type
let sum_correct = 1.5 + f32(2i);
Abstract Numerics are an Exception
You can mix unsuffixed literal numbers (called abstract numerics):
For details on how unsuffixed literals resolve automatically, see the Numeric Literals reference.
2. Initializing Variables with Mismatched Literals
An explicit variable type must exactly match the initializer's type.
// ❌ COMPILE ERROR: cannot initialize f32 with an integer literal
let x: f32 = 5;
// ✔️ CORRECT: use a floating-point literal or explicit constructor
let x_correct_1: f32 = 5.0;
let x_correct_2: f32 = f32(5);
3. Mixing Signed and Unsigned Integers
You cannot compare or perform arithmetic between signed (i32) and unsigned (u32) integers. This is crucial for index computations and loop bounds.
let signed_index: i32 = -1;
let array_length: u32 = 5u;
// ❌ COMPILE ERROR: cannot compare i32 and u32
if signed_index < array_length { ... }
// ✔️ CORRECT: explicitly cast the signed index (after checking bounds)
if signed_index >= 0 && u32(signed_index) < array_length { ... }
Explicit Casting with Type Constructors
To convert a value from one type to another in WGSL, you use type constructors. Think of these as functions named after the target type that take the value to be converted as an argument.