schell / schell/wgsl-rs

feat: type-directed integer literal suffix insertion

Open
#145 1 comment 0 reactions 0 assignees View on GitHub
bug enhancement P2
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.