feat: type-directed integer literal suffix insertion
- Dominant language
- Rust
- Stars
- 62
- Forks
- 5
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 9
Description
## Problem
Rust infers the type of unsuffixed integer literals (e.g. `0`, `1`) from context - if the return type is `u32`, the literal is `u32`. But WGSL defaults unsuffixed integer literals to `i32`. Since `#[wgsl]` preserves the literal as-is (without suffix), code that compiles fine in Rust can produce invalid WGSL.
## Example
```rust
#[wgsl]
mod shader {
fn to_array(data: bool) -> [u32; 1] {
[select(0, 1, data)] // Rust: 0 and 1 are u32. WGSL: 0 and 1 are i32.
}
}
```
Rust type-checks this (infers `0` and `1` as `u32` from the return type). But the generated WGSL is:
```wgsl
fn to_array(data: bool) -> array {
return array(select(0, 1, data)); // naga error: select returns i32, not u32
}
```
naga rejects it: `The return expression does not match the declared return type`.
## Proposed solution
A post-parse IR pass that propagates expected types from anchor points down to leaf literals, inserting the appropriate WGSL suffix (`u`/`i`/`f`):
- **Return expressions**: `return array(select(0, 1, data))` — return type is `array`, so element type is `u32`, so `select` args are `u32`, so `0` → `0u` and `1` → `1u`.
- **Array literals**: `[0, 1, 2]` in a `[u32; 3]` context → all `u32`.
- **Struct constructor fields**: `Foo { count: 0 }` where `count: u32` → `0` is `u32`.
- **Variable initializers**: `let x: u32 = 0;` → `0` is `u32`.
- **Function call args**: `select(0, 1, data)` — requires knowing `select`'s signature (builtins are known).
- **Assignment RHS**: `x = 0;` where `x: u32` → `0` is `u32`.
This is a limited form of type inference scoped to literal disambiguation — not a full type checker. The IR already has the type information at many anchor points (`ReturnType`, `Local.ty`, `Type::Array.elem`, `Field.ty`, `Expr::Struct` field types).
## Alternatives considered
1. **Warn on unsuffixed literals** — noisy, many false positives.
2. **Require explicit suffixes** — poor DX for Rust users.
3. **Document the gotcha** — users still hit the bug.
## Scope
Estimated ~300-500 lines. The pass runs after parsing and before rendering, similar to the `resolve_assoc_types` pass. It walks expressions with an expected type context and suffixes bare `Lit::Int { suffix: "" }` nodes.
## Context
Discovered during crabslab `SlabItem` development, where `bool::to_array` uses `select(0, 1, data)` which compiles in Rust (infers `u32`) but fails naga validation in WGSL (defaults to `i32`).
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the post-parse IR pass analogous to resolve_assoc_types, then inspect ReturnType, Local.ty, Type::Array.elem, Field.ty, and Expr::Struct for available type anchors. Implement the scoped walk described in the issue and verify that the select example and the listed return, array, struct, initializer, call, and assignment contexts receive the appropriate literal suffixes before rendering.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100