Vector Types
In addition to arrays, Wyn provides fixed-width vector types. They are distinct from arrays — they have a fixed component count, different semantics, and are required for certain shader interfaces and built-in variables.
Vector types use the naming convention vecNT where:
Nis the number of components (2, 3, or 4)Tis the element type (i32, f32, etc.)
Common vector types:
| Component Type | 2-component | 3-component | 4-component |
|---|---|---|---|
i32 (signed) | vec2i32 | vec3i32 | vec4i32 |
f32 (32-bit float) | vec2f32 | vec3f32 | vec4f32 |
Vector types are distinct from array types and have different semantics:
- Vectors are fixed-size, optimized for SIMD operations
- Arrays are more general containers with runtime length operations
Example usage:
-- Vector types for graphics operations (using @[...] literal syntax)
let position: vec3f32 = @[1.0, 2.0, 3.0]
let color: vec4f32 = @[1.0, 0.0, 0.0, 1.0]
-- Built-in variables often require vector types
#[vertex]
entry vertex_main() #[builtin(position)] vec4f32 =
@[0.0, 0.0, 0.0, 1.0]
Vector Swizzles
A vector’s components are accessed with field syntax (v.x). Wyn
also supports swizzles: one to four letters drawn from a single
“swizzle set”. The two sets and their component indices are:
| Set | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| xyzw | x | y | z | w |
| rgba | r | g | b | a |
rgba is an alias set for xyzw (r == x, g == y, b == z,
a == w) — the two sets address the same underlying components and
produce identical values. Mixing letters from the two sets in one
swizzle (.xg, .rbw) is a type error.
A single-letter swizzle produces the scalar component; a 2-, 3-, or 4-letter swizzle produces a new vector of that length. Letters may repeat and may be in any order.
Each referenced component must lie within the source vector’s length —
.z (or .b) on a vec2 is a type error.
let v: vec4f32 = @[1.0, 2.0, 3.0, 4.0]
let first: f32 = v.x -- 1.0
let alpha: f32 = v.a -- 4.0 (same as v.w)
let rgb: vec3f32 = v.rgb -- (1.0, 2.0, 3.0)
let rev: vec4f32 = v.wzyx -- (4.0, 3.0, 2.0, 1.0)
let splat: vec3f32 = v.xxx -- (1.0, 1.0, 1.0)
Swizzle Update via with
Wyn extends the with operator to vec swizzles, so the GLSL idiom
dir.yz *= rot(angle) translates directly:
let v1 = v0 with .yz = e -- replace v0.y, v0.z with e.x, e.y
let v2 = v1 with .yz *= m -- compound: same as `with .yz = v1.yz * m`
let v3 = v2 with .x = scalar -- single-component LHS takes a scalar RHS
The compound forms *= += -= /= desugar to
target with .swizzle = target.swizzle <op> rhs, with target
evaluated once. The result is always a fresh vec — wyn is
immutable, the original target is unchanged. Components on the
LHS must be distinct (v with .xx = e is rejected); the RHS
arity must match the swizzle length (a vec2 for .yz, a scalar
for .x); and each component must be in range for the target’s
size.
Vector Constructors
Vectors are constructed with the @[...] literal syntax:
let v1: vec3f32 = @[1.0, 2.0, 3.0]
let v2: vec4f32 = @[1.0, 0.0, 0.0, 1.0]
The element type is inferred from the arguments or the context.
Vector Arithmetic and Scalar Broadcasting
The binary arithmetic operators +, -, *, and / apply
component-wise to vectors. When both operands are vectors they must
have the same vector type (same length and element type); the result
is that type and each component is combined independently:
let a: vec3f32 = @[1.0, 2.0, 3.0]
let b: vec3f32 = @[4.0, 5.0, 6.0]
let s: vec3f32 = a + b -- (5.0, 7.0, 9.0)
let p: vec3f32 = a * b -- (4.0, 10.0, 18.0), component-wise
When one operand is a scalar, it is broadcast against the vector:
the scalar is applied to every component, and the result has the
vector’s type. The scalar may appear on either side, so both
v op scalar and scalar op v are accepted:
let v: vec3f32 = @[1.0, 2.0, 3.0]
let scaled: vec3f32 = v * 2.0 -- (2.0, 4.0, 6.0)
let shifted: vec3f32 = 3.0 - 2.0 * v -- 3.0 - (2.0, 4.0, 6.0) = (1.0, -1.0, -3.0)
Broadcasting performs no implicit numeric conversion: the scalar’s
type must equal the vector’s element type. Mixing a vec3f32 with an
i32 scalar is a type error — write the scalar as f32. Likewise,
component-wise vector arithmetic between two vectors of different
element types or lengths is rejected.
For * specifically, matrix products (matrix×matrix, matrix×vector,
vector×matrix, matrix×scalar) take priority over component-wise
arithmetic; see Matrix Types.
Constraints
- Location numbers must be non-negative integers
- Each shader stage has specific allowed built-ins
Type Safety
The attribute system is statically type-checked:
- Built-in attributes must be applied to compatible types
- Location attributes can be used with any serializable type
- Shader stage compatibility is verified at compile time
- Interface matching between vertex and fragment shaders is validated