Skip to main content

wgsl_types/builtin/
call_ty.rs

1//! Type-checking and computation of the return type of built-in functions.
2//!
3//! Functions bear the same name as the WGSL counterpart.
4//! Functions that take template parameters are suffixed with `_t` and take `tplt_*` arguments.
5//!
6//! ### Warning
7//!
8//! Type-checking of some functions is incomplete.
9
10#![allow(non_snake_case)]
11
12use crate::{
13    CallSignature, Error,
14    conv::{Convert, convert_all_ty, convert_ty},
15    syntax::*,
16    tplt::{BitcastTemplate, TpltParam},
17    ty::{StructMemberType, StructType, TextureDimensions, TextureType, Ty, Type},
18};
19
20type E = Error;
21
22/// Compute the return type of calling a built-in function.
23///
24/// The arguments must be [loaded][Type::loaded].
25///
26/// Does not include constructor built-ins, see [`type_ctor`][super::type_ctor].
27///
28/// Some functions are still TODO, see [`call`][super::call] for the list of functions and statuses.
29pub fn type_builtin_fn(
30    name: &str,
31    tplt: Option<&[TpltParam]>,
32    args: &[Type],
33) -> Result<Option<Type>, E> {
34    let err = || {
35        E::Signature(CallSignature {
36            name: name.to_string(),
37            tplt: tplt.map(|t| t.to_vec()),
38            args: args.to_vec(),
39        })
40    };
41
42    match (name, args) {
43        // bitcast
44        ("bitcast", [a]) if let Some(tplt) = tplt => {
45            let tplt = BitcastTemplate::parse(tplt)?;
46            bitcast_t(tplt.ty(), a).map(Some)
47        }
48        _ if tplt.is_some() => Err(err()),
49        // logical
50        ("all", [a]) => all(a).map(Some),
51        ("any", [a]) => any(a).map(Some),
52        ("select", [a1, a2, a3]) => select(a1, a2, a3).map(Some),
53        // array
54        ("arrayLength", [a]) => arrayLength(a).map(Some),
55        // numeric
56        ("abs", [a]) => abs(a).map(Some),
57        ("acos", [a]) => acos(a).map(Some),
58        ("acosh", [a]) => acosh(a).map(Some),
59        ("asin", [a]) => asin(a).map(Some),
60        ("asinh", [a]) => asinh(a).map(Some),
61        ("atan", [a]) => atan(a).map(Some),
62        ("atanh", [a]) => atanh(a).map(Some),
63        ("atan2", [a1, a2]) => atan2(a1, a2).map(Some),
64        ("ceil", [a]) => ceil(a).map(Some),
65        ("clamp", [a1, a2, a3]) => clamp(a1, a2, a3).map(Some),
66        ("cos", [a]) => cos(a).map(Some),
67        ("cosh", [a]) => cosh(a).map(Some),
68        ("countLeadingZeros", [a]) => countLeadingZeros(a).map(Some),
69        ("countOneBits", [a]) => countOneBits(a).map(Some),
70        ("countTrailingZeros", [a]) => countTrailingZeros(a).map(Some),
71        ("cross", [a1, a2]) => cross(a1, a2).map(Some),
72        ("degrees", [a]) => degrees(a).map(Some),
73        ("determinant", [a]) => determinant(a).map(Some),
74        ("distance", [a1, a2]) => distance(a1, a2).map(Some),
75        ("dot", [a1, a2]) => dot(a1, a2).map(Some),
76        ("dot4U8Packed", [a1, a2]) => dot4U8Packed(a1, a2).map(Some),
77        ("dot4I8Packed", [a1, a2]) => dot4I8Packed(a1, a2).map(Some),
78        ("exp", [a]) => exp(a).map(Some),
79        ("exp2", [a]) => exp2(a).map(Some),
80        ("extractBits", [a1, a2, a3]) => extractBits(a1, a2, a3).map(Some),
81        ("faceForward", [a1, a2, a3]) => faceForward(a1, a2, a3).map(Some),
82        ("firstLeadingBit", [a]) => firstLeadingBit(a).map(Some),
83        ("firstTrailingBit", [a]) => firstTrailingBit(a).map(Some),
84        ("floor", [a]) => floor(a).map(Some),
85        ("fma", [a1, a2, a3]) => fma(a1, a2, a3).map(Some),
86        ("fract", [a]) => fract(a).map(Some),
87        ("frexp", [a]) => frexp(a).map(Some),
88        ("insertBits", [a1, a2, a3, a4]) => insertBits(a1, a2, a3, a4).map(Some),
89        ("inverseSqrt", [a]) => inverseSqrt(a).map(Some),
90        ("ldexp", [a1, a2]) => ldexp(a1, a2).map(Some),
91        ("length", [a]) => length(a).map(Some),
92        ("log", [a]) => log(a).map(Some),
93        ("log2", [a]) => log2(a).map(Some),
94        ("max", [a1, a2]) => max(a1, a2).map(Some),
95        ("min", [a1, a2]) => min(a1, a2).map(Some),
96        ("mix", [a1, a2, a3]) => mix(a1, a2, a3).map(Some),
97        ("modf", [a]) => modf(a).map(Some),
98        ("normalize", [a]) => normalize(a).map(Some),
99        ("pow", [a1, a2]) => pow(a1, a2).map(Some),
100        ("quantizeToF16", [a]) => quantizeToF16(a).map(Some),
101        ("radians", [a]) => radians(a).map(Some),
102        ("reflect", [a1, a2]) => reflect(a1, a2).map(Some),
103        ("refract", [a1, a2, a3]) => refract(a1, a2, a3).map(Some),
104        ("reverseBits", [a]) => reverseBits(a).map(Some),
105        ("round", [a]) => round(a).map(Some),
106        ("saturate", [a]) => saturate(a).map(Some),
107        ("sign", [a]) => sign(a).map(Some),
108        ("sin", [a]) => sin(a).map(Some),
109        ("sinh", [a]) => sinh(a).map(Some),
110        ("smoothstep", [a1, a2, a3]) => smoothstep(a1, a2, a3).map(Some),
111        ("sqrt", [a]) => sqrt(a).map(Some),
112        ("step", [a1, a2]) => step(a1, a2).map(Some),
113        ("tan", [a]) => tan(a).map(Some),
114        ("tanh", [a]) => tanh(a).map(Some),
115        ("transpose", [a]) => transpose(a).map(Some),
116        ("trunc", [a]) => trunc(a).map(Some),
117        // derivative
118        ("dpdx", [a]) => dpdx(a).map(Some),
119        ("dpdxCoarse", [a]) => dpdxCoarse(a).map(Some),
120        ("dpdxFine", [a]) => dpdxFine(a).map(Some),
121        ("dpdy", [a]) => dpdy(a).map(Some),
122        ("dpdyCoarse", [a]) => dpdyCoarse(a).map(Some),
123        ("dpdyFine", [a]) => dpdyFine(a).map(Some),
124        ("fwidth", [a]) => fwidth(a).map(Some),
125        ("fwidthCoarse", [a]) => fwidthCoarse(a).map(Some),
126        ("fwidthFine", [a]) => fwidthFine(a).map(Some),
127        // texture
128        ("textureDimensions", [a1]) => textureDimensions(a1, None).map(Some),
129        ("textureDimensions", [a1, a2]) => textureDimensions(a1, Some(a2)).map(Some),
130        ("textureGather", [a1, a2, a3]) => textureGather(a1, a2, a3, None, None, None).map(Some),
131        ("textureGather", [a1, a2, a3, a4]) => {
132            textureGather(a1, a2, a3, Some(a4), None, None).map(Some)
133        }
134        ("textureGather", [a1, a2, a3, a4, a5]) => {
135            textureGather(a1, a2, a3, Some(a4), Some(a5), None).map(Some)
136        }
137        ("textureGather", [a1, a2, a3, a4, a5, a6]) => {
138            textureGather(a1, a2, a3, Some(a4), Some(a5), Some(a6)).map(Some)
139        }
140        ("textureGatherCompare", [a1, a2, a3, a4]) => {
141            textureGatherCompare(a1, a2, a3, a4, None, None).map(Some)
142        }
143        ("textureGatherCompare", [a1, a2, a3, a4, a5]) => {
144            textureGatherCompare(a1, a2, a3, a4, Some(a5), None).map(Some)
145        }
146        ("textureGatherCompare", [a1, a2, a3, a4, a5, a6]) => {
147            textureGatherCompare(a1, a2, a3, a4, Some(a5), Some(a6)).map(Some)
148        }
149        ("textureLoad", [a1, a2]) => textureLoad(a1, a2, None, None).map(Some),
150        ("textureLoad", [a1, a2, a3]) => textureLoad(a1, a2, Some(a3), None).map(Some),
151        ("textureLoad", [a1, a2, a3, a4]) => textureLoad(a1, a2, Some(a3), Some(a4)).map(Some),
152        ("textureNumLayers", [a]) => textureNumLayers(a).map(Some),
153        ("textureNumLevels", [a]) => textureNumLevels(a).map(Some),
154        ("textureNumSamples", [a]) => textureNumSamples(a).map(Some),
155        ("textureSample", [a1, a2, a3]) => textureSample(a1, a2, a3, None, None).map(Some),
156        ("textureSample", [a1, a2, a3, a4]) => textureSample(a1, a2, a3, Some(a4), None).map(Some),
157        ("textureSample", [a1, a2, a3, a4, a5]) => {
158            textureSample(a1, a2, a3, Some(a4), Some(a5)).map(Some)
159        }
160        ("textureSampleBias", [a1, a2, a3, a4]) => {
161            textureSampleBias(a1, a2, a3, a4, None, None).map(Some)
162        }
163        ("textureSampleBias", [a1, a2, a3, a4, a5]) => {
164            textureSampleBias(a1, a2, a3, a4, Some(a5), None).map(Some)
165        }
166        ("textureSampleBias", [a1, a2, a3, a4, a5, a6]) => {
167            textureSampleBias(a1, a2, a3, a4, Some(a5), Some(a6)).map(Some)
168        }
169        ("textureSampleCompare", [a1, a2, a3, a4]) => {
170            textureSampleCompare(a1, a2, a3, a4, None, None).map(Some)
171        }
172        ("textureSampleCompare", [a1, a2, a3, a4, a5]) => {
173            textureSampleCompare(a1, a2, a3, a4, Some(a5), None).map(Some)
174        }
175        ("textureSampleCompare", [a1, a2, a3, a4, a5, a6]) => {
176            textureSampleCompare(a1, a2, a3, a4, Some(a5), Some(a6)).map(Some)
177        }
178        ("textureSampleCompareLevel", [a1, a2, a3, a4]) => {
179            textureSampleCompareLevel(a1, a2, a3, a4, None, None).map(Some)
180        }
181        ("textureSampleCompareLevel", [a1, a2, a3, a4, a5]) => {
182            textureSampleCompareLevel(a1, a2, a3, a4, Some(a5), None).map(Some)
183        }
184        ("textureSampleCompareLevel", [a1, a2, a3, a4, a5, a6]) => {
185            textureSampleCompareLevel(a1, a2, a3, a4, Some(a5), Some(a6)).map(Some)
186        }
187        ("textureSampleGrad", [a1, a2, a3, a4, a5]) => {
188            textureSampleGrad(a1, a2, a3, a4, a5, None, None).map(Some)
189        }
190        ("textureSampleGrad", [a1, a2, a3, a4, a5, a6]) => {
191            textureSampleGrad(a1, a2, a3, a4, a5, Some(a6), None).map(Some)
192        }
193        ("textureSampleGrad", [a1, a2, a3, a4, a5, a6, a7]) => {
194            textureSampleGrad(a1, a2, a3, a4, a5, Some(a6), Some(a7)).map(Some)
195        }
196        ("textureSampleLevel", [a1, a2, a3, a4]) => {
197            textureSampleLevel(a1, a2, a3, a4, None, None).map(Some)
198        }
199        ("textureSampleLevel", [a1, a2, a3, a4, a5]) => {
200            textureSampleLevel(a1, a2, a3, a4, Some(a5), None).map(Some)
201        }
202        ("textureSampleLevel", [a1, a2, a3, a4, a5, a6]) => {
203            textureSampleLevel(a1, a2, a3, a4, Some(a5), Some(a6)).map(Some)
204        }
205        ("textureSampleBaseClampToEdge", [a1, a2, a3]) => {
206            textureSampleBaseClampToEdge(a1, a2, a3).map(Some)
207        }
208        ("textureStore", [a1, a2, a3]) => textureStore(a1, a2, a3, None).map(|()| None),
209        ("textureStore", [a1, a2, a3, a4]) => textureStore(a1, a2, a3, Some(a4)).map(|()| None),
210        // atomic
211        ("atomicLoad", [a]) => atomicLoad(a).map(Some),
212        ("atomicStore", [a1, a2]) => atomicStore(a1, a2).map(|()| None),
213        ("atomicAdd", [a1, a2]) => atomicAdd(a1, a2).map(Some),
214        ("atomicSub", [a1, a2]) => atomicSub(a1, a2).map(Some),
215        ("atomicMax", [a1, a2]) => atomicMax(a1, a2).map(Some),
216        ("atomicMin", [a1, a2]) => atomicMin(a1, a2).map(Some),
217        ("atomicAnd", [a1, a2]) => atomicAnd(a1, a2).map(Some),
218        ("atomicOr", [a1, a2]) => atomicOr(a1, a2).map(Some),
219        ("atomicXor", [a1, a2]) => atomicXor(a1, a2).map(Some),
220        ("atomicExchange", [a1, a2]) => atomicExchange(a1, a2).map(Some),
221        ("atomicCompareExchangeWeak", [a1, a2, a3]) => {
222            atomicCompareExchangeWeak(a1, a2, a3).map(Some)
223        }
224        // packing
225        ("pack4x8snorm", [a]) => pack4x8snorm(a).map(Some),
226        ("pack4x8unorm", [a]) => pack4x8unorm(a).map(Some),
227        ("pack4xI8", [a]) => pack4xI8(a).map(Some),
228        ("pack4xU8", [a]) => pack4xU8(a).map(Some),
229        ("pack4xI8Clamp", [a]) => pack4xI8Clamp(a).map(Some),
230        ("pack4xU8Clamp", [a]) => pack4xU8Clamp(a).map(Some),
231        ("pack2x16snorm", [a]) => pack2x16snorm(a).map(Some),
232        ("pack2x16unorm", [a]) => pack2x16unorm(a).map(Some),
233        ("pack2x16float", [a]) => pack2x16float(a).map(Some),
234        ("unpack4x8snorm", [a]) => unpack4x8snorm(a).map(Some),
235        ("unpack4x8unorm", [a]) => unpack4x8unorm(a).map(Some),
236        ("unpack4xI8", [a]) => unpack4xI8(a).map(Some),
237        ("unpack4xU8", [a]) => unpack4xU8(a).map(Some),
238        ("unpack2x16snorm", [a]) => unpack2x16snorm(a).map(Some),
239        ("unpack2x16unorm", [a]) => unpack2x16unorm(a).map(Some),
240        ("unpack2x16float", [a]) => unpack2x16float(a).map(Some),
241        // synchronization
242        ("storageBarrier", []) => Ok(None),
243        ("textureBarrier", []) => Ok(None),
244        ("workgroupBarrier", []) => Ok(None),
245        ("workgroupUniformLoad", [Type::Ptr(AddressSpace::Workgroup, t, _)]) => {
246            Ok(Some(*t.clone()))
247        }
248        // subgroup
249        ("subgroupAdd", [a]) => subgroupAdd(a).map(Some),
250        ("subgroupExclusiveAdd", [a]) => subgroupExclusiveAdd(a).map(Some),
251        ("subgroupInclusiveAdd", [a]) => subgroupInclusiveAdd(a).map(Some),
252        ("subgroupAll", [a]) => subgroupAll(a).map(Some),
253        ("subgroupAnd", [a]) => subgroupAnd(a).map(Some),
254        ("subgroupAny", [a]) => subgroupAny(a).map(Some),
255        ("subgroupBallot", [a]) => subgroupBallot(Some(a)).map(Some),
256        #[cfg(feature = "naga-ext")]
257        ("subgroupBallot", []) => subgroupBallot(None).map(Some),
258        ("subgroupBroadcast", [a1, a2]) => subgroupBroadcast(a1, a2).map(Some),
259        ("subgroupBroadcastFirst", [a]) => subgroupBroadcastFirst(a).map(Some),
260        ("subgroupElect", []) => subgroupElect().map(Some),
261        ("subgroupMax", [a]) => subgroupMax(a).map(Some),
262        ("subgroupMin", [a]) => subgroupMin(a).map(Some),
263        ("subgroupMul", [a]) => subgroupMul(a).map(Some),
264        ("subgroupExclusiveMul", [a]) => subgroupExclusiveMul(a).map(Some),
265        ("subgroupInclusiveMul", [a]) => subgroupInclusiveMul(a).map(Some),
266        ("subgroupOr", [a]) => subgroupOr(a).map(Some),
267        ("subgroupShuffle", [a1, a2]) => subgroupShuffle(a1, a2).map(Some),
268        ("subgroupShuffleDown", [a1, a2]) => subgroupShuffleDown(a1, a2).map(Some),
269        ("subgroupShuffleUp", [a1, a2]) => subgroupShuffleUp(a1, a2).map(Some),
270        ("subgroupShuffleXor", [a1, a2]) => subgroupShuffleXor(a1, a2).map(Some),
271        ("subgroupXor", [a]) => subgroupXor(a).map(Some),
272        // quad
273        ("quadBroadcast", [a1, a2]) => quadBroadcast(a1, a2).map(Some),
274        ("quadSwapDiagonal", [a]) => quadSwapDiagonal(a).map(Some),
275        ("quadSwapX", [a]) => quadSwapX(a).map(Some),
276        ("quadSwapY", [a]) => quadSwapY(a).map(Some),
277        // naga ray queries extension
278        #[cfg(feature = "naga-ext")]
279        ("rayQueryInitialize", [a1, a2, a3]) => rayQueryInitialize(a1, a2, a3).map(|()| None),
280        #[cfg(feature = "naga-ext")]
281        ("rayQueryProceed", [a]) => rayQueryProceed(a).map(Some),
282        #[cfg(feature = "naga-ext")]
283        ("rayQueryGenerateIntersection", [a1, a2]) => {
284            rayQueryGenerateIntersection(a1, a2).map(|()| None)
285        }
286        #[cfg(feature = "naga-ext")]
287        ("rayQueryConfirmIntersection", [a]) => rayQueryConfirmIntersection(a).map(|()| None),
288        #[cfg(feature = "naga-ext")]
289        ("rayQueryTerminate", [a]) => rayQueryTerminate(a).map(|()| None),
290        #[cfg(feature = "naga-ext")]
291        ("rayQueryGetCommittedIntersection", [a]) => rayQueryGetCommittedIntersection(a).map(Some),
292        #[cfg(feature = "naga-ext")]
293        ("rayQueryGetCandidateIntersection", [a]) => rayQueryGetCandidateIntersection(a).map(Some),
294        #[cfg(feature = "naga-ext")]
295        ("getCommittedHitVertexPositions", [a]) => getCommittedHitVertexPositions(a).map(Some),
296        #[cfg(feature = "naga-ext")]
297        ("getCandidateHitVertexPositions", [a]) => getCandidateHitVertexPositions(a).map(Some),
298        // naga ray tracing pipelines extension
299        #[cfg(feature = "naga-ext")]
300        ("traceRay", [a1, a2, a3]) => traceRay(a1, a2, a3).map(|()| None),
301        _ => Err(err()),
302    }
303}
304
305// ---------------------
306// BUILT-IN RETURN TYPES
307// ---------------------
308
309pub(crate) fn frexp_struct_name(ty: &Type) -> Option<&'static str> {
310    match ty {
311        Type::AbstractFloat => Some("__frexp_result_abstract"),
312        Type::F32 => Some("__frexp_result_f32"),
313        Type::F16 => Some("__frexp_result_f16"),
314        #[cfg(feature = "naga-ext")]
315        Type::F64 => Some("__frexp_result_f64"),
316        Type::Vec(n, ty) => match (n, &**ty) {
317            (2, Type::AbstractFloat) => Some("__frexp_result_vec2_abstract"),
318            (2, Type::F32) => Some("__frexp_result_vec2_f32"),
319            (2, Type::F16) => Some("__frexp_result_vec2_f16"),
320            (3, Type::AbstractFloat) => Some("__frexp_result_vec3_abstract"),
321            (3, Type::F32) => Some("__frexp_result_vec3_f32"),
322            (3, Type::F16) => Some("__frexp_result_vec3_f16"),
323            (4, Type::AbstractFloat) => Some("__frexp_result_vec4_abstract"),
324            (4, Type::F32) => Some("__frexp_result_vec4_f32"),
325            (4, Type::F16) => Some("__frexp_result_vec4_f16"),
326            #[cfg(feature = "naga-ext")]
327            (2, Type::F64) => Some("__frexp_result_vec2_f64"),
328            #[cfg(feature = "naga-ext")]
329            (3, Type::F64) => Some("__frexp_result_vec3_f64"),
330            #[cfg(feature = "naga-ext")]
331            (4, Type::F64) => Some("__frexp_result_vec4_f64"),
332            _ => None,
333        },
334        _ => None,
335    }
336}
337
338pub(crate) fn frexp_struct_type(ty: &Type) -> Option<StructType> {
339    frexp_struct_name(ty).map(|name| {
340        let exp_inner_ty = if ty.is_abstract() {
341            Type::AbstractInt
342        } else {
343            Type::I32
344        };
345        let exp_ty = match ty {
346            Type::Vec(n, _) => Type::Vec(*n, Box::new(exp_inner_ty)),
347            _ => exp_inner_ty,
348        };
349        StructType {
350            name: name.to_string(),
351            members: vec![
352                StructMemberType::new("fract".to_string(), ty.clone()),
353                StructMemberType::new("exp".to_string(), exp_ty),
354            ],
355        }
356    })
357}
358
359pub(crate) fn modf_struct_name(ty: &Type) -> Option<&'static str> {
360    match ty {
361        Type::AbstractFloat => Some("__modf_result_abstract"),
362        Type::F32 => Some("__modf_result_f32"),
363        Type::F16 => Some("__modf_result_f16"),
364        #[cfg(feature = "naga-ext")]
365        Type::F64 => Some("__modf_result_f64"),
366        Type::Vec(n, ty) => match (n, &**ty) {
367            (2, Type::AbstractFloat) => Some("__modf_result_vec2_abstract"),
368            (2, Type::F32) => Some("__modf_result_vec2_f32"),
369            (2, Type::F16) => Some("__modf_result_vec2_f16"),
370            (3, Type::AbstractFloat) => Some("__modf_result_vec3_abstract"),
371            (3, Type::F32) => Some("__modf_result_vec3_f32"),
372            (3, Type::F16) => Some("__modf_result_vec3_f16"),
373            (4, Type::AbstractFloat) => Some("__modf_result_vec4_abstract"),
374            (4, Type::F32) => Some("__modf_result_vec4_f32"),
375            (4, Type::F16) => Some("__modf_result_vec4_f16"),
376            #[cfg(feature = "naga-ext")]
377            (2, Type::F64) => Some("__modf_result_vec2_f64"),
378            #[cfg(feature = "naga-ext")]
379            (3, Type::F64) => Some("__modf_result_vec3_f64"),
380            #[cfg(feature = "naga-ext")]
381            (4, Type::F64) => Some("__modf_result_vec4_f64"),
382            _ => None,
383        },
384        _ => None,
385    }
386}
387
388pub(crate) fn atomic_compare_exchange_struct_type(ty: &Type) -> StructType {
389    StructType {
390        name: "__atomic_compare_exchange_result".to_string(),
391        members: vec![
392            StructMemberType::new("old_value".to_string(), ty.clone()),
393            StructMemberType::new("exchanged".to_string(), Type::Bool),
394        ],
395    }
396}
397
398pub(crate) fn modf_struct_type(ty: &Type) -> Option<StructType> {
399    modf_struct_name(ty).map(|name| StructType {
400        name: name.to_string(),
401        members: vec![
402            StructMemberType::new("fract".to_string(), ty.clone()),
403            StructMemberType::new("whole".to_string(), ty.clone()),
404        ],
405    })
406}
407
408#[cfg(feature = "naga-ext")]
409#[allow(unused)]
410pub(crate) fn ray_desc_struct_type() -> StructType {
411    StructType {
412        name: "RayDesc".to_string(),
413        members: vec![
414            StructMemberType::new("flags".to_string(), Type::U32),
415            StructMemberType::new("cull_mask".to_string(), Type::U32),
416            StructMemberType::new("t_min".to_string(), Type::F32),
417            StructMemberType::new("t_max".to_string(), Type::F32),
418            StructMemberType::new("origin".to_string(), Type::Vec(3, Box::new(Type::F32))),
419            StructMemberType::new("dir".to_string(), Type::Vec(3, Box::new(Type::F32))),
420        ],
421    }
422}
423
424#[cfg(feature = "naga-ext")]
425pub(crate) fn ray_intersection_struct_type() -> StructType {
426    StructType {
427        name: "RayIntersection".to_string(),
428        members: vec![
429            StructMemberType::new("kind".to_string(), Type::U32),
430            StructMemberType::new("t".to_string(), Type::F32),
431            StructMemberType::new("instance_custom_data".to_string(), Type::U32),
432            StructMemberType::new("instance_index".to_string(), Type::U32),
433            StructMemberType::new("sbt_record_offset".to_string(), Type::U32),
434            StructMemberType::new("geometry_index".to_string(), Type::U32),
435            StructMemberType::new("primitive_index".to_string(), Type::U32),
436            StructMemberType::new(
437                "barycentrics".to_string(),
438                Type::Vec(2, Box::new(Type::F32)),
439            ),
440            StructMemberType::new("front_face".to_string(), Type::Bool),
441            StructMemberType::new(
442                "object_to_world".to_string(),
443                Type::Mat(4, 3, Box::new(Type::F32)),
444            ),
445            StructMemberType::new(
446                "world_to_object".to_string(),
447                Type::Mat(4, 3, Box::new(Type::F32)),
448            ),
449        ],
450    }
451}
452
453// utility predicates for `T or vecN<T>` constraints.
454fn inner_is_float(ty: &Type) -> bool {
455    ty.is_float() || matches!(ty, Type::Vec(_, t) if t.is_float())
456}
457fn inner_is_numeric(ty: &Type) -> bool {
458    ty.is_numeric() || matches!(ty, Type::Vec(_, t) if t.is_numeric())
459}
460fn inner_is_integer(ty: &Type) -> bool {
461    ty.is_integer() || matches!(ty, Type::Vec(_, t) if t.is_integer())
462}
463fn inner_is_bool(ty: &Type) -> bool {
464    ty.is_bool() || matches!(ty, Type::Vec(_, t) if t.is_bool())
465}
466
467// -------
468// BITCAST
469// -------
470// reference: <https://www.w3.org/TR/WGSL/#bit-reinterp-builtin-functions>
471
472/// `bitcast<T>()` builtin function.
473///
474/// we assume `tplt_ty` is a concrete numeric scalar or concrete numeric vector.
475///
476/// Reference: <https://www.w3.org/TR/WGSL/#bitcast-builtin>
477pub fn bitcast_t(tplt_ty: &Type, e: &Type) -> Result<Type, E> {
478    if tplt_ty.size_of() != e.concretize().size_of() {
479        Err(E::Builtin(
480            "`bitcast` argument must have the same byte length as the template type",
481        ))
482    } else if inner_is_numeric(e) {
483        Ok(tplt_ty.clone())
484    } else {
485        Err(E::Builtin(
486            "`bitcast` expects a numeric scalar or numeric vector argument",
487        ))
488    }
489}
490
491// -------
492// LOGICAL
493// -------
494// reference: <https://www.w3.org/TR/WGSL/#logical-builtin-functions>
495
496/// `all()` builtin function.
497///
498/// Reference: <https://www.w3.org/TR/WGSL/#all-builtin>
499pub fn all(e: &Type) -> Result<Type, E> {
500    if inner_is_bool(e) {
501        Ok(Type::Bool)
502    } else {
503        Err(E::Builtin(
504            "`all` expects a boolean or vector of boolean argument",
505        ))
506    }
507}
508
509/// `any()` builtin function.
510///
511/// Reference: <https://www.w3.org/TR/WGSL/#any-builtin>
512pub fn any(e: &Type) -> Result<Type, E> {
513    if inner_is_bool(e) {
514        Ok(Type::Bool)
515    } else {
516        Err(E::Builtin(
517            "`any` expects a boolean or vector of boolean argument",
518        ))
519    }
520}
521
522/// `select()` builtin function.
523///
524/// Reference: <https://www.w3.org/TR/WGSL/#select-builtin>
525pub fn select(f: &Type, t: &Type, cond: &Type) -> Result<Type, E> {
526    let ty = convert_ty(f, t).ok_or(E::Builtin(
527        "`select` 1st and 2nd arguments are incompatible",
528    ))?;
529
530    match (ty, cond) {
531        (Type::Vec(n_ty, _), Type::Vec(n_cond, t)) => {
532            if !t.is_bool() {
533                Err(E::Builtin(
534                    "`select` 3rd argument must be a boolean or vector of boolean",
535                ))
536            } else if n_ty != n_cond {
537                Err(E::Builtin(
538                    "`select` 3rd vector argument has incorrect dimensions",
539                ))
540            } else {
541                Ok(ty.clone())
542            }
543        }
544        (ty, Type::Bool) if ty.is_scalar() || ty.is_vec() => Ok(ty.clone()),
545        (_, Type::Bool) => Err(E::Builtin(
546            "`select` 1st and 2nd arguments must be a scalar or vector",
547        )),
548        (_, _) => Err(E::Builtin(
549            "`select` 3rd argument must be a boolean or vector of boolean",
550        )),
551    }
552}
553
554// -----
555// ARRAY
556// -----
557// reference: <https://www.w3.org/TR/WGSL/#array-builtin-functions>
558
559/// `arrayLength()` builtin function.
560///
561/// Reference: <https://www.w3.org/TR/WGSL/#arrayLength-builtin>
562pub fn arrayLength(p: &Type) -> Result<Type, E> {
563    match p {
564        Type::Ptr(AddressSpace::Storage, t, a_m)
565            if a_m.is_read() && matches!(**t, Type::Array(_, None)) =>
566        {
567            Ok(Type::U32)
568        }
569        _ => Err(E::Builtin(
570            "`arrayLength` argument must be a pointer to runtime-sized array, with `storage` address space and `read` or `read_write` access mode",
571        )),
572    }
573}
574
575// -------
576// NUMERIC
577// -------
578// reference: <https://www.w3.org/TR/WGSL/#numeric-builtin-function>
579
580/// `abs()` builtin function.
581///
582/// Reference: <https://www.w3.org/TR/WGSL/#abs-float-builtin>
583pub fn abs(e: &Type) -> Result<Type, E> {
584    inner_is_numeric(e).then_some(e.clone()).ok_or(E::Builtin(
585        "`abs` argument must be a numeric scalar or vector",
586    ))
587}
588
589/// `acos()` builtin function.
590///
591/// NOTE: the function returns NaN as an "indeterminate value" if computed out of domain
592///
593/// Reference: <https://www.w3.org/TR/WGSL/#acos-builtin>
594pub fn acos(e: &Type) -> Result<Type, E> {
595    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
596        "`acos` argument must be a float scalar or vector",
597    ))
598}
599
600/// `acosh()` builtin function.
601///
602/// NOTE: the function returns NaN as an "indeterminate value" if computed out of domain
603///
604/// Reference: <https://www.w3.org/TR/WGSL/#acosh-builtin>
605pub fn acosh(e: &Type) -> Result<Type, E> {
606    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
607        "`acosh` argument must be a float scalar or vector",
608    ))
609}
610
611/// `asin()` builtin function.
612///
613/// NOTE: the function returns NaN as an "indeterminate value" if computed out of domain
614///
615/// Reference: <https://www.w3.org/TR/WGSL/#asin-builtin>
616pub fn asin(e: &Type) -> Result<Type, E> {
617    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
618        "`asin` argument must be a float scalar or vector",
619    ))
620}
621
622/// `asinh()` builtin function.
623///
624/// Reference: <https://www.w3.org/TR/WGSL/#asinh-builtin>
625pub fn asinh(e: &Type) -> Result<Type, E> {
626    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
627        "`asinh` argument must be a float scalar or vector",
628    ))
629}
630
631/// `atan()` builtin function.
632///
633/// Reference: <https://www.w3.org/TR/WGSL/#atan-builtin>
634pub fn atan(e: &Type) -> Result<Type, E> {
635    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
636        "`atan` argument must be a float scalar or vector",
637    ))
638}
639
640/// `atanh()` builtin function.
641///
642/// NOTE: the function returns NaN as an "indeterminate value" if computed out of domain
643///
644/// Reference: <https://www.w3.org/TR/WGSL/#atanh-builtin>
645pub fn atanh(e: &Type) -> Result<Type, E> {
646    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
647        "`atanh` argument must be a float scalar or vector",
648    ))
649}
650
651/// `atan2()` builtin function.
652///
653/// Reference: <https://www.w3.org/TR/WGSL/#atan2-builtin>
654pub fn atan2(y: &Type, x: &Type) -> Result<Type, E> {
655    let ty = convert_ty(y, x).ok_or(E::Builtin("`atan2 arguments are incompatible`"))?;
656    inner_is_float(ty).then_some(ty.clone()).ok_or(E::Builtin(
657        "`atan2` expects float scalar or vector arguments",
658    ))
659}
660
661/// `ceil()` builtin function.
662///
663/// Reference: <https://www.w3.org/TR/WGSL/#ceil-builtin>
664pub fn ceil(e: &Type) -> Result<Type, E> {
665    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
666        "`ceil` argument must be a float scalar or vector",
667    ))
668}
669
670/// `clamp()` builtin function.
671///
672/// Reference: <https://www.w3.org/TR/WGSL/#clamp>
673pub fn clamp(e: &Type, low: &Type, high: &Type) -> Result<Type, E> {
674    let ty =
675        convert_all_ty([e, low, high]).ok_or(E::Builtin("`clamp` arguments are incompatible"))?;
676    if inner_is_numeric(ty) {
677        Ok(ty.clone())
678    } else {
679        Err(E::Builtin(
680            "`clamp` expects three numeric scalar or vector arguments",
681        ))
682    }
683}
684
685/// `cos()` builtin function.
686///
687/// NOTE: the function returns NaN as an "indeterminate value" if computed out of domain
688///
689/// Reference: <https://www.w3.org/TR/WGSL/#cos-builtin>
690pub fn cos(e: &Type) -> Result<Type, E> {
691    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
692        "`cos` argument must be a float scalar or vector",
693    ))
694}
695
696/// `cosh()` builtin function.
697///
698/// Reference: <https://www.w3.org/TR/WGSL/#cosh-builtin>
699pub fn cosh(e: &Type) -> Result<Type, E> {
700    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
701        "`cosh` argument must be a float scalar or vector",
702    ))
703}
704
705/// `countLeadingZeros()` builtin function.
706///
707/// Reference: <https://www.w3.org/TR/WGSL/#countLeadingZeros-builtin>
708pub fn countLeadingZeros(e: &Type) -> Result<Type, E> {
709    inner_is_integer(e)
710        .then_some(e.concretize())
711        .ok_or(E::Builtin(
712            "`countLeadingZeros` argument must be a integer scalar or vector",
713        ))
714}
715
716/// `countOneBits()` builtin function.
717///
718/// Reference: <https://www.w3.org/TR/WGSL/#countOneBits-builtin>
719pub fn countOneBits(e: &Type) -> Result<Type, E> {
720    inner_is_integer(e)
721        .then_some(e.concretize())
722        .ok_or(E::Builtin(
723            "`countOneBits` argument must be a integer scalar or vector",
724        ))
725}
726
727/// `countTrailingZeros()` builtin function.
728///
729/// Reference: <https://www.w3.org/TR/WGSL/#countTrailingZeros-builtin>
730pub fn countTrailingZeros(e: &Type) -> Result<Type, E> {
731    inner_is_integer(e)
732        .then_some(e.concretize())
733        .ok_or(E::Builtin(
734            "`countTrailingZeros` argument must be a integer scalar or vector",
735        ))
736}
737
738/// `cross()` builtin function.
739///
740/// Reference: <https://www.w3.org/TR/WGSL/#cross-builtin>
741pub fn cross(a: &Type, b: &Type) -> Result<Type, E> {
742    let ty = convert_ty(a, b).ok_or(E::Builtin("`cross` arguments are incompatible"))?;
743    match ty {
744        Type::Vec(3, t) if t.is_float() => Ok(ty.clone()),
745        _ => Err(E::Builtin(
746            "`cross` expects two 3-component float vector arguments",
747        )),
748    }
749}
750
751/// `degrees()` builtin function.
752///
753/// Reference: <https://www.w3.org/TR/WGSL/#degrees-builtin>
754pub fn degrees(e: &Type) -> Result<Type, E> {
755    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
756        "`degrees` expects a float scalar or vector argument",
757    ))
758}
759
760/// `determinant()` builtin function.
761///
762/// Reference: <https://www.w3.org/TR/WGSL/#determinant-builtin>
763pub fn determinant(e: &Type) -> Result<Type, E> {
764    match e {
765        Type::Mat(c, r, t) if c == r => Ok(*t.clone()),
766        _ => Err(E::Builtin("`determinant` expects a square matrix argument")),
767    }
768}
769
770/// `distance()` builtin function.
771///
772/// Reference: <https://www.w3.org/TR/WGSL/#distance-builtin>
773pub fn distance(e1: &Type, e2: &Type) -> Result<Type, E> {
774    let ty = convert_ty(e1, e2).ok_or(E::Builtin("`distance` arguments are incompatible"))?;
775    inner_is_float(ty)
776        .then_some(ty.inner_ty())
777        .ok_or(E::Builtin(
778            "`distance` expects two float scalar or vector arguments",
779        ))
780}
781
782/// `dot()` builtin function.
783///
784/// Reference: <https://www.w3.org/TR/WGSL/#dot-builtin>
785pub fn dot(e1: &Type, e2: &Type) -> Result<Type, E> {
786    let ty = convert_ty(e1, e2).ok_or(E::Builtin("`dot` arguments are incompatible"))?;
787    match ty {
788        Type::Vec(_, t) if t.is_numeric() => Ok(*t.clone()),
789        _ => Err(E::Builtin(
790            "`dot` expects two numeric scalar or vector arguments",
791        )),
792    }
793}
794
795/// `dot4U8Packed()` builtin function.
796///
797/// Reference: <https://www.w3.org/TR/WGSL/#dot4U8Packed-builtin>
798pub fn dot4U8Packed(e1: &Type, e2: &Type) -> Result<Type, E> {
799    if e1.is_convertible_to(&Type::U32) && e2.is_convertible_to(&Type::U32) {
800        Ok(Type::U32)
801    } else {
802        Err(E::Builtin("`dot4U8Packed` expects two u32 arguments"))
803    }
804}
805
806/// `dot4I8Packed()` builtin function.
807///
808/// Reference: <https://www.w3.org/TR/WGSL/#dot4I8Packed-builtin>
809pub fn dot4I8Packed(e1: &Type, e2: &Type) -> Result<Type, E> {
810    if e1.is_convertible_to(&Type::U32) && e2.is_convertible_to(&Type::U32) {
811        Ok(Type::I32)
812    } else {
813        Err(E::Builtin("`dot4I8Packed` expects two u32 arguments"))
814    }
815}
816
817/// `exp()` builtin function.
818///
819/// Reference: <https://www.w3.org/TR/WGSL/#exp-builtin>
820pub fn exp(e: &Type) -> Result<Type, E> {
821    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
822        "`exp` argument must be a float scalar or vector",
823    ))
824}
825
826/// `exp2()` builtin function.
827///
828/// Reference: <https://www.w3.org/TR/WGSL/#exp2-builtin>
829pub fn exp2(e: &Type) -> Result<Type, E> {
830    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
831        "`exp2` argument must be a float scalar or vector",
832    ))
833}
834
835/// `extractBits()` builtin function.
836///
837/// Reference: <https://www.w3.org/TR/WGSL/#extractBits-builtin>
838pub fn extractBits(e: &Type, offset: &Type, count: &Type) -> Result<Type, E> {
839    if !inner_is_integer(e) {
840        Err(E::Builtin(
841            "`extractBits` 1st argument must be an integer scalar or vector",
842        ))
843    } else if offset.is_convertible_to(&Type::U32) && count.is_convertible_to(&Type::U32) {
844        Ok(e.concretize())
845    } else {
846        Err(E::Builtin(
847            "`extractBits` 2nd and 3rd arguments must be u32",
848        ))
849    }
850}
851
852/// `faceForward()` builtin function.
853///
854/// Reference: <https://www.w3.org/TR/WGSL/#faceForward-builtin>
855pub fn faceForward(e1: &Type, e2: &Type, e3: &Type) -> Result<Type, E> {
856    let ty = convert_all_ty([e1, e2, e3])
857        .ok_or(E::Builtin("`faceForward` arguments are incompatible"))?;
858    if matches!(ty, Type::Vec(_, t) if t.is_float()) {
859        Ok(ty.clone())
860    } else {
861        Err(E::Builtin(
862            "`faceForward` expects three float vector arguments",
863        ))
864    }
865}
866
867/// `firstLeadingBit()` builtin function.
868///
869/// Reference: <https://www.w3.org/TR/WGSL/#firstLeadingBit-builtin>
870pub fn firstLeadingBit(e: &Type) -> Result<Type, E> {
871    if inner_is_integer(e) {
872        Ok(e.concretize())
873    } else {
874        Err(E::Builtin(
875            "`firstLeadingBit` expects an integer scalar or vector argument",
876        ))
877    }
878}
879
880/// `firstTrailingBit()` builtin function.
881///
882/// Reference: <https://www.w3.org/TR/WGSL/#firstTrailingBit-builtin>
883pub fn firstTrailingBit(e: &Type) -> Result<Type, E> {
884    if inner_is_integer(e) {
885        Ok(e.concretize())
886    } else {
887        Err(E::Builtin(
888            "`firstTrailingBit` expects an integer scalar or vector argument",
889        ))
890    }
891}
892
893/// `floor()` builtin function.
894///
895/// Reference: <https://www.w3.org/TR/WGSL/#floor-builtin>
896pub fn floor(e: &Type) -> Result<Type, E> {
897    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
898        "`floor` argument must be a float scalar or vector",
899    ))
900}
901
902/// `fma()` builtin function.
903///
904/// Reference: <https://www.w3.org/TR/WGSL/#fma-builtin>
905pub fn fma(e1: &Type, e2: &Type, e3: &Type) -> Result<Type, E> {
906    let ty = convert_all_ty([e1, e2, e3]).ok_or(E::Builtin("`fma` arguments are incompatible"))?;
907    inner_is_float(ty).then_some(ty.clone()).ok_or(E::Builtin(
908        "`fma` expects three float scalar or vector arguments",
909    ))
910}
911
912/// `fract()` builtin function.
913///
914/// Reference: <https://www.w3.org/TR/WGSL/#fract-builtin>
915pub fn fract(e: &Type) -> Result<Type, E> {
916    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
917        "`fract` argument must be a float scalar or vector",
918    ))
919}
920
921/// `frexp()` builtin function.
922///
923/// TODO: This built-in is only partially implemented.
924///
925/// Reference: <https://www.w3.org/TR/WGSL/#frexp-builtin>
926pub fn frexp(e: &Type) -> Result<Type, E> {
927    if inner_is_float(e) {
928        Ok(frexp_struct_type(e).unwrap().into())
929    } else {
930        Err(E::Builtin(
931            "`frexp` expects a float scalar or vector argument",
932        ))
933    }
934}
935
936/// `insertBits()` builtin function.
937///
938/// Reference: <https://www.w3.org/TR/WGSL/#insertBits-builtin>
939pub fn insertBits(e: &Type, newbits: &Type, offset: &Type, count: &Type) -> Result<Type, E> {
940    let ty = convert_ty(e, newbits).ok_or(E::Builtin("`insertBits` arguments are incompatible"))?;
941
942    if !inner_is_integer(ty) {
943        Err(E::Builtin(
944            "`insertBits` 1st argument must be an integer scalar or vector",
945        ))
946    } else if !offset.is_convertible_to(&Type::U32) || !count.is_convertible_to(&Type::U32) {
947        Err(E::Builtin("`insertBits` 3rd and 4th arguments must be u32"))
948    } else {
949        Ok(ty.concretize())
950    }
951}
952
953/// `inverseSqrt()` builtin function.
954///
955/// Reference: <https://www.w3.org/TR/WGSL/#inverseSqrt-builtin>
956pub fn inverseSqrt(e: &Type) -> Result<Type, E> {
957    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
958        "`inverseSqrt` argument must be a float scalar or vector",
959    ))
960}
961
962/// `ldexp()` builtin function.
963///
964/// Reference: <https://www.w3.org/TR/WGSL/#ldexp-builtin>
965pub fn ldexp(e1: &Type, e2: &Type) -> Result<Type, E> {
966    if !inner_is_float(e1) {
967        Err(E::Builtin(
968            "`ldexp` 1st argument must be a float scalar or vector",
969        ))
970    } else if !e2.inner_ty().is_signed() || !e2.inner_ty().is_integer() {
971        Err(E::Builtin(
972            "`ldexp` 2nd argument must be a signed integer scalar or vector",
973        ))
974    } else if matches!((e1, e2), (Type::Vec(n1, _), Type::Vec(n2, _)) if n1 != n2) {
975        Err(E::Builtin(
976            "`ldexp` vector arguments must have the same number of components",
977        ))
978    } else if e1.is_vec() && !e2.is_vec() || !e1.is_vec() && e2.is_vec() {
979        Err(E::Builtin(
980            "`ldexp` arguments must be both scalar or both vectors",
981        ))
982    } else if e1.is_abstract() && e2.is_concrete() {
983        // "If either parameter is concrete then the other parameter will undergo automatic conversion to a concrete type (if applicable) and the result will be a concrete type."
984        Ok(e1.concretize())
985    } else {
986        Ok(e1.clone())
987    }
988}
989
990/// `length()` builtin function.
991///
992/// Reference: <https://www.w3.org/TR/WGSL/#length-builtin>
993pub fn length(e: &Type) -> Result<Type, E> {
994    inner_is_float(e).then_some(e.inner_ty()).ok_or(E::Builtin(
995        "`length` argument must be a float scalar or vector",
996    ))
997}
998
999/// `log()` builtin function.
1000///
1001/// Reference: <https://www.w3.org/TR/WGSL/#log-builtin>
1002pub fn log(e: &Type) -> Result<Type, E> {
1003    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
1004        "`log` argument must be a float scalar or vector",
1005    ))
1006}
1007
1008/// `log2()` builtin function.
1009///
1010/// Reference: <https://www.w3.org/TR/WGSL/#log2-builtin>
1011pub fn log2(e: &Type) -> Result<Type, E> {
1012    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
1013        "`log2` expects a float scalar or vector argument",
1014    ))
1015}
1016
1017/// `max()` builtin function.
1018///
1019/// Reference: <https://www.w3.org/TR/WGSL/#max-builtin>
1020pub fn max(e1: &Type, e2: &Type) -> Result<Type, E> {
1021    let ty = convert_ty(e1, e2).ok_or(E::Builtin("`max` arguments are incompatible"))?;
1022    inner_is_numeric(ty).then_some(ty.clone()).ok_or(E::Builtin(
1023        "`max` expects two numeric scalar or vector arguments",
1024    ))
1025}
1026
1027/// `min()` builtin function.
1028///
1029/// Reference: <https://www.w3.org/TR/WGSL/#min-builtin>
1030pub fn min(e1: &Type, e2: &Type) -> Result<Type, E> {
1031    let ty = convert_ty(e1, e2).ok_or(E::Builtin("`min` arguments are incompatible"))?;
1032    inner_is_numeric(ty).then_some(ty.clone()).ok_or(E::Builtin(
1033        "`min` expects two numeric scalar or vector arguments",
1034    ))
1035}
1036
1037/// `mix()` builtin function.
1038///
1039/// Reference: <https://www.w3.org/TR/WGSL/#mix-builtin>
1040pub fn mix(e1: &Type, e2: &Type, e3: &Type) -> Result<Type, E> {
1041    // e1 and e2 have to be the same type, but e3 can be the same type or the same inner type.
1042    let ty =
1043        convert_ty(e1, e2).ok_or(E::Builtin("`mix` 1st and 2nd arguments are incompatible"))?;
1044
1045    if !inner_is_float(ty) {
1046        Err(E::Builtin(
1047            "`mix` expects three float scalar or vector arguments",
1048        ))
1049    }
1050    // 2nd overload: scalar blend factor with vector mixing components
1051    else if ty.is_vec() && e3.is_scalar() {
1052        let ty = convert_ty(&ty.inner_ty(), e3)
1053            .and_then(|inner_ty| ty.convert_inner_to(inner_ty))
1054            .ok_or(E::Builtin(
1055                "`mix` 3rd argument is incompatible with 1st and 2nd argument inner type",
1056            ))?;
1057        Ok(ty)
1058    }
1059    // 1st overload: 3 args of the same type
1060    else {
1061        let ty = convert_ty(ty, e3).ok_or(E::Builtin(
1062            "`mix` 3rd argument is incompatible with 1st and 2nd arguments",
1063        ))?;
1064        Ok(ty.clone())
1065    }
1066}
1067
1068/// `modf()` builtin function.
1069///
1070/// Reference: <https://www.w3.org/TR/WGSL/#modf-builtin>
1071pub fn modf(e: &Type) -> Result<Type, E> {
1072    if inner_is_float(e) {
1073        Ok(modf_struct_type(e).unwrap().into())
1074    } else {
1075        Err(E::Builtin(
1076            "`modf` expects a float scalar or vector argument",
1077        ))
1078    }
1079}
1080
1081/// `normalize()` builtin function.
1082///
1083/// Reference: <https://www.w3.org/TR/WGSL/#normalize-builtin>
1084pub fn normalize(e: &Type) -> Result<Type, E> {
1085    match e {
1086        // abstractInt is convertible to float
1087        Type::Vec(n, t) if t.is_abstract_int() => Ok(Type::Vec(*n, Type::AbstractFloat.into())),
1088        Type::Vec(_, t) if t.is_float() => Ok(e.clone()),
1089        _ => Err(E::Builtin("`normalize` expects a float vector argument")),
1090    }
1091}
1092
1093/// `pow()` builtin function.
1094///
1095/// Reference: <https://www.w3.org/TR/WGSL/#pow-builtin>
1096pub fn pow(e1: &Type, e2: &Type) -> Result<Type, E> {
1097    let ty = convert_ty(e1, e2).ok_or(E::Builtin("`pow` arguments are incompatible"))?;
1098    inner_is_float(ty).then_some(ty.clone()).ok_or(E::Builtin(
1099        "`pow` argument must be a float scalar or vector",
1100    ))
1101}
1102
1103/// `quantizeToF16()` builtin function.
1104///
1105/// Reference: <https://www.w3.org/TR/WGSL/#quantizeToF16-builtin>
1106pub fn quantizeToF16(e: &Type) -> Result<Type, E> {
1107    const ERR: E = E::Builtin("`quantizeToF16` expects a f32 scalar or vector argument");
1108    let ty = e.convert_inner_to(&Type::F32).ok_or(ERR)?;
1109    if ty.is_f32() || ty.is_vec() {
1110        Ok(ty)
1111    } else {
1112        Err(ERR)
1113    }
1114}
1115
1116/// `radians()` builtin function.
1117///
1118/// Reference: <https://www.w3.org/TR/WGSL/#radians-builtin>
1119pub fn radians(e: &Type) -> Result<Type, E> {
1120    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
1121        "`radians` argument must be a float scalar or vector",
1122    ))
1123}
1124
1125/// `reflect()` builtin function.
1126///
1127/// Reference: <https://www.w3.org/TR/WGSL/#reflect-builtin>
1128pub fn reflect(e1: &Type, e2: &Type) -> Result<Type, E> {
1129    let ty = convert_ty(e1, e2).ok_or(E::Builtin("`reflect` arguments are incompatible"))?;
1130    if ty.is_vec() && ty.inner_ty().is_float() {
1131        Ok(ty.clone())
1132    } else {
1133        Err(E::Builtin("`reflect` expects two float vector arguments"))
1134    }
1135}
1136
1137/// `refract()` builtin function.
1138///
1139/// Reference: <https://www.w3.org/TR/WGSL/#refract-builtin>
1140pub fn refract(e1: &Type, e2: &Type, e3: &Type) -> Result<Type, E> {
1141    let ty = convert_ty(e1, e2).ok_or(E::Builtin(
1142        "`refract` 1st and 2nd arguments are incompatible",
1143    ))?;
1144
1145    let ty = convert_ty(&ty.inner_ty(), e3)
1146        .and_then(|inner_ty| ty.convert_inner_to(inner_ty))
1147        .ok_or(E::Builtin(
1148            "`refract` 3rd argument is incompatible with 1st and 2nd argument inner type",
1149        ))?;
1150
1151    if ty.is_vec() && ty.inner_ty().is_float() {
1152        Ok(ty)
1153    } else {
1154        Err(E::Builtin(
1155            "`refract` expects two scalar vector arguments and one scalar argument",
1156        ))
1157    }
1158}
1159
1160/// `reverseBits()` builtin function.
1161///
1162/// Reference: <https://www.w3.org/TR/WGSL/#reverseBits-builtin>
1163pub fn reverseBits(e: &Type) -> Result<Type, E> {
1164    if inner_is_integer(e) {
1165        Ok(e.concretize())
1166    } else {
1167        Err(E::Builtin(
1168            "`reverseBits` expects an integer scalar or vector argument",
1169        ))
1170    }
1171}
1172
1173/// `round()` builtin function.
1174///
1175/// Reference: <https://www.w3.org/TR/WGSL/#round-builtin>
1176pub fn round(e: &Type) -> Result<Type, E> {
1177    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
1178        "`round` argument must be a float scalar or vector",
1179    ))
1180}
1181
1182/// `saturate()` builtin function.
1183///
1184/// Reference: <https://www.w3.org/TR/WGSL/#saturate-builtin>
1185pub fn saturate(e: &Type) -> Result<Type, E> {
1186    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
1187        "`saturate` argument must be a float scalar or vector",
1188    ))
1189}
1190
1191/// `sign()` builtin function.
1192///
1193/// Reference: <https://www.w3.org/TR/WGSL/#sign-builtin>
1194pub fn sign(e: &Type) -> Result<Type, E> {
1195    if inner_is_numeric(e) && e.inner_ty().is_signed() {
1196        Ok(e.clone())
1197    } else {
1198        Err(E::Builtin(
1199            "`sign` argument must be a signed numeric scalar or vector",
1200        ))
1201    }
1202}
1203
1204/// `sin()` builtin function.
1205///
1206/// Reference: <https://www.w3.org/TR/WGSL/#sin-builtin>
1207pub fn sin(e: &Type) -> Result<Type, E> {
1208    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
1209        "`sin` argument must be a float scalar or vector",
1210    ))
1211}
1212
1213/// `sinh()` builtin function.
1214///
1215/// Reference: <https://www.w3.org/TR/WGSL/#sinh-builtin>
1216pub fn sinh(e: &Type) -> Result<Type, E> {
1217    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
1218        "`sinh` argument must be a float scalar or vector",
1219    ))
1220}
1221
1222/// `smoothstep()` builtin function.
1223///
1224/// Reference: <https://www.w3.org/TR/WGSL/#smoothstep-builtin>
1225pub fn smoothstep(edge0: &Type, edge1: &Type, x: &Type) -> Result<Type, E> {
1226    let ty = convert_all_ty([edge0, edge1, x])
1227        .ok_or(E::Builtin("`smoothstep` arguments are incompatible"))?;
1228    inner_is_float(ty).then_some(ty.clone()).ok_or(E::Builtin(
1229        "`smoothstep` expects three float scalar or vector arguments",
1230    ))
1231}
1232
1233/// `sqrt()` builtin function.
1234///
1235/// Reference: <https://www.w3.org/TR/WGSL/#sqrt-builtin>
1236pub fn sqrt(e: &Type) -> Result<Type, E> {
1237    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
1238        "`sqrt` argument must be a float scalar or vector",
1239    ))
1240}
1241
1242/// `step()` builtin function.
1243///
1244/// Reference: <https://www.w3.org/TR/WGSL/#step-builtin>
1245pub fn step(edge: &Type, x: &Type) -> Result<Type, E> {
1246    let ty = convert_all_ty([edge, x]).ok_or(E::Builtin("`step` arguments are incompatible"))?;
1247    inner_is_float(ty).then_some(ty.clone()).ok_or(E::Builtin(
1248        "`step` expects two float scalar or vector arguments",
1249    ))
1250}
1251
1252/// `tan()` builtin function.
1253///
1254/// Reference: <https://www.w3.org/TR/WGSL/#tan-builtin>
1255pub fn tan(e: &Type) -> Result<Type, E> {
1256    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
1257        "`tan` argument must be a float scalar or vector",
1258    ))
1259}
1260
1261/// `tanh()` builtin function.
1262///
1263/// Reference: <https://www.w3.org/TR/WGSL/#tanh-builtin>
1264pub fn tanh(e: &Type) -> Result<Type, E> {
1265    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
1266        "`tanh` argument must be a float scalar or vector",
1267    ))
1268}
1269
1270/// `transpose()` builtin function.
1271///
1272/// Reference: <https://www.w3.org/TR/WGSL/#transpose-builtin>
1273pub fn transpose(e: &Type) -> Result<Type, E> {
1274    match e {
1275        Type::Mat(r, c, ty) => Ok(Type::Mat(*c, *r, ty.clone())),
1276        _ => Err(E::Builtin("`transpose` expects a matrix argument")),
1277    }
1278}
1279
1280/// `trunc()` builtin function.
1281///
1282/// Reference: <https://www.w3.org/TR/WGSL/#trunc-builtin>
1283pub fn trunc(e: &Type) -> Result<Type, E> {
1284    inner_is_float(e).then_some(e.clone()).ok_or(E::Builtin(
1285        "`trunc` argument must be a float scalar or vector",
1286    ))
1287}
1288
1289// ----------
1290// DERIVATIVE
1291// ----------
1292// reference: <https://www.w3.org/TR/WGSL/#derivative-builtin-functions>
1293
1294/// `dpdx()` builtin function.
1295///
1296/// Reference: <https://www.w3.org/TR/WGSL/#dpdx-builtin>
1297pub fn dpdx(e: &Type) -> Result<Type, E> {
1298    let ty = e.convert_inner_to(&Type::F32);
1299    if let Some(ty) = ty
1300        && (ty.is_scalar() || ty.is_vec())
1301    {
1302        Ok(ty)
1303    } else {
1304        Err(E::Builtin(
1305            "`dpdx` expects a `f32` scalar or vector argument",
1306        ))
1307    }
1308}
1309
1310/// `dpdxCoarse()` builtin function.
1311///
1312/// Reference: <https://www.w3.org/TR/WGSL/#dpdxCoarse-builtin>
1313pub fn dpdxCoarse(e: &Type) -> Result<Type, E> {
1314    let ty = e.convert_inner_to(&Type::F32);
1315    if let Some(ty) = ty
1316        && (ty.is_scalar() || ty.is_vec())
1317    {
1318        Ok(ty)
1319    } else {
1320        Err(E::Builtin(
1321            "`dpdxCoarse` expects a `f32` scalar or vector argument",
1322        ))
1323    }
1324}
1325
1326/// `dpdxFine()` builtin function.
1327///
1328/// Reference: <https://www.w3.org/TR/WGSL/#dpdxFine-builtin>
1329pub fn dpdxFine(e: &Type) -> Result<Type, E> {
1330    let ty = e.convert_inner_to(&Type::F32);
1331    if let Some(ty) = ty
1332        && (ty.is_scalar() || ty.is_vec())
1333    {
1334        Ok(ty)
1335    } else {
1336        Err(E::Builtin(
1337            "`dpdxFine` expects a `f32` scalar or vector argument",
1338        ))
1339    }
1340}
1341
1342/// `dpdy()` builtin function.
1343///
1344/// Reference: <https://www.w3.org/TR/WGSL/#dpdy-builtin>
1345pub fn dpdy(e: &Type) -> Result<Type, E> {
1346    let ty = e.convert_inner_to(&Type::F32);
1347    if let Some(ty) = ty
1348        && (ty.is_scalar() || ty.is_vec())
1349    {
1350        Ok(ty)
1351    } else {
1352        Err(E::Builtin(
1353            "`dpdy` expects a `f32` scalar or vector argument",
1354        ))
1355    }
1356}
1357
1358/// `dpdyCoarse()` builtin function.
1359///
1360/// Reference: <https://www.w3.org/TR/WGSL/#dpdyCoarse-builtin>
1361pub fn dpdyCoarse(e: &Type) -> Result<Type, E> {
1362    let ty = e.convert_inner_to(&Type::F32);
1363    if let Some(ty) = ty
1364        && (ty.is_scalar() || ty.is_vec())
1365    {
1366        Ok(ty)
1367    } else {
1368        Err(E::Builtin(
1369            "`dpdyCoarse` expects a `f32` scalar or vector argument",
1370        ))
1371    }
1372}
1373
1374/// `dpdyFine()` builtin function.
1375///
1376/// Reference: <https://www.w3.org/TR/WGSL/#dpdyFine-builtin>
1377pub fn dpdyFine(e: &Type) -> Result<Type, E> {
1378    let ty = e.convert_inner_to(&Type::F32);
1379    if let Some(ty) = ty
1380        && (ty.is_scalar() || ty.is_vec())
1381    {
1382        Ok(ty)
1383    } else {
1384        Err(E::Builtin(
1385            "`dpdyFine` expects a `f32` scalar or vector argument",
1386        ))
1387    }
1388}
1389
1390/// `fwidth()` builtin function.
1391///
1392/// Reference: <https://www.w3.org/TR/WGSL/#fwidth-builtin>
1393pub fn fwidth(e: &Type) -> Result<Type, E> {
1394    let ty = e.convert_inner_to(&Type::F32);
1395    if let Some(ty) = ty
1396        && (ty.is_scalar() || ty.is_vec())
1397    {
1398        Ok(ty)
1399    } else {
1400        Err(E::Builtin(
1401            "`fwidth` expects a `f32` scalar or vector argument",
1402        ))
1403    }
1404}
1405
1406/// `fwidthCoarse()` builtin function.
1407///
1408/// Reference: <https://www.w3.org/TR/WGSL/#fwidthCoarse-builtin>
1409pub fn fwidthCoarse(e: &Type) -> Result<Type, E> {
1410    let ty = e.convert_inner_to(&Type::F32);
1411    if let Some(ty) = ty
1412        && (ty.is_scalar() || ty.is_vec())
1413    {
1414        Ok(ty)
1415    } else {
1416        Err(E::Builtin(
1417            "`fwidthCoarse` expects a `f32` scalar or vector argument",
1418        ))
1419    }
1420}
1421
1422/// `fwidthFine()` builtin function.
1423///
1424/// Reference: <https://www.w3.org/TR/WGSL/#fwidthFine-builtin>
1425pub fn fwidthFine(e: &Type) -> Result<Type, E> {
1426    let ty = e.convert_inner_to(&Type::F32);
1427    if let Some(ty) = ty
1428        && (ty.is_scalar() || ty.is_vec())
1429    {
1430        Ok(ty)
1431    } else {
1432        Err(E::Builtin(
1433            "`fwidthFine` expects a `f32` scalar or vector argument",
1434        ))
1435    }
1436}
1437
1438// -------
1439// TEXTURE
1440// -------
1441// reference: <https://www.w3.org/TR/WGSL/#texture-builtin-functions>
1442
1443/// `textureDimensions()` builtin function.
1444///
1445/// Reference: <https://www.w3.org/TR/WGSL/#textureDimensions>
1446pub fn textureDimensions(t: &Type, level: Option<&Type>) -> Result<Type, E> {
1447    match t {
1448        Type::Texture(t) => {
1449            let ret = match t.dimensions() {
1450                TextureDimensions::D1 => Type::U32,
1451                TextureDimensions::D2 => Type::Vec(2, Type::U32.into()),
1452                TextureDimensions::D3 => Type::Vec(3, Type::U32.into()),
1453            };
1454
1455            if let Some(l) = level
1456                && !l.is_integer()
1457            {
1458                Err(E::Builtin(
1459                    "`textureDimensions` 2nd argument must be an integer",
1460                ))
1461            } else {
1462                Ok(ret)
1463            }
1464        }
1465        _ => Err(E::Builtin(
1466            "`textureDimensions` 1st argument must be a texture",
1467        )),
1468    }
1469}
1470
1471/// `textureGather()` builtin function.
1472///
1473/// Reference: <https://www.w3.org/TR/WGSL/#textureGather>
1474// TODO: typecheck the other arguments
1475pub fn textureGather(
1476    e1: &Type,
1477    e2: &Type,
1478    _e3: &Type,
1479    _e4: Option<&Type>,
1480    _e5: Option<&Type>,
1481    _e6: Option<&Type>,
1482) -> Result<Type, E> {
1483    if let Type::Texture(t) = e1 {
1484        if t.is_depth() {
1485            Ok(Type::Vec(4, Type::F32.into()))
1486        } else {
1487            Err(E::Builtin(
1488                "the first argument to `textureGather` must be either a depth texture or an integer",
1489            ))
1490        }
1491    } else if let Type::Texture(t) = e2 {
1492        if let Some(sample_type) = t.sampled_type() {
1493            Ok(Type::Vec(4, Box::new(sample_type.into())))
1494        } else {
1495            Err(E::Builtin(
1496                "the 2nd argument to `textureGather` must be a sampled texture when the 1st one is an integer",
1497            ))
1498        }
1499    } else {
1500        Err(E::Builtin(
1501            "`textureGather` expects a texture in the 1st or 2nd argument",
1502        ))
1503    }
1504}
1505
1506/// `textureGatherCompare()` builtin function.
1507///
1508/// Reference: <https://www.w3.org/TR/WGSL/#textureGatherCompare>
1509// TODO: typecheck the other arguments
1510pub fn textureGatherCompare(
1511    e1: &Type,
1512    _e2: &Type,
1513    _e3: &Type,
1514    _e4: &Type,
1515    _e5: Option<&Type>,
1516    _e6: Option<&Type>,
1517) -> Result<Type, E> {
1518    if let Type::Texture(t) = e1
1519        && t.is_depth()
1520    {
1521        Ok(Type::Vec(4, Type::F32.into()))
1522    } else {
1523        Err(E::Builtin(
1524            "`textureGatherCompare` 1st argument must be a depth texture",
1525        ))
1526    }
1527}
1528
1529/// `textureLoad()` builtin function.
1530///
1531/// Reference: <https://www.w3.org/TR/WGSL/#textureLoad>
1532// TODO: typecheck the other arguments
1533pub fn textureLoad(
1534    e1: &Type,
1535    _e2: &Type,
1536    _e3: Option<&Type>,
1537    _e4: Option<&Type>,
1538) -> Result<Type, E> {
1539    if let Type::Texture(t) = e1 {
1540        if t.is_cube() {
1541            Err(E::Builtin("`textureLoad` does not support cube textures"))
1542        } else if t.is_depth() || *t == TextureType::DepthMultisampled2D {
1543            // NOTE: a `texture_depth_multisampled_2d` is *not* considered a depth texture.
1544            Ok(Type::F32)
1545        } else {
1546            Ok(Type::Vec(4, Box::new(t.channel_type().into())))
1547        }
1548    } else {
1549        Err(E::Builtin("`textureLoad` 1st argument must be a texture"))
1550    }
1551}
1552
1553/// `textureNumLayers()` builtin function.
1554///
1555/// Reference: <https://www.w3.org/TR/WGSL/#textureNumLayers>
1556pub fn textureNumLayers(t: &Type) -> Result<Type, E> {
1557    if let Type::Texture(t) = t
1558        && t.is_arrayed()
1559    {
1560        Ok(Type::U32)
1561    } else {
1562        Err(E::Builtin(
1563            "`textureNumLayers` expects an array texture argument",
1564        ))
1565    }
1566}
1567
1568/// `textureNumLevels()` builtin function.
1569///
1570/// Reference: <https://www.w3.org/TR/WGSL/#textureNumLevels>
1571pub fn textureNumLevels(t: &Type) -> Result<Type, E> {
1572    if let Type::Texture(t) = t
1573        && (t.is_sampled() || t.is_depth())
1574    {
1575        Ok(Type::U32)
1576    } else {
1577        Err(E::Builtin(
1578            "`textureNumLevels` expects an sampled or depth texture argument",
1579        ))
1580    }
1581}
1582
1583/// `textureNumSamples()` builtin function.
1584///
1585/// Reference: <https://www.w3.org/TR/WGSL/#textureNumSamples>
1586pub fn textureNumSamples(t: &Type) -> Result<Type, E> {
1587    if let Type::Texture(t) = t
1588        && t.is_multisampled()
1589    {
1590        Ok(Type::U32)
1591    } else {
1592        Err(E::Builtin(
1593            "`textureNumSamples` expects a multisampled texture argument",
1594        ))
1595    }
1596}
1597
1598/// `textureSample()` builtin function.
1599///
1600/// Reference: <https://www.w3.org/TR/WGSL/#textureSample>
1601// TODO: typecheck the other arguments
1602pub fn textureSample(
1603    e1: &Type,
1604    _e2: &Type,
1605    _e3: &Type,
1606    _e4: Option<&Type>,
1607    _e5: Option<&Type>,
1608) -> Result<Type, E> {
1609    if let Type::Texture(t) = e1 {
1610        if t.is_sampled() {
1611            Ok(Type::Vec(4, Type::F32.into()))
1612        } else if t.is_depth() {
1613            Ok(Type::F32)
1614        } else {
1615            Err(E::Builtin(
1616                "`textureSample` first argument must be a sampled or depth texture",
1617            ))
1618        }
1619    } else {
1620        Err(E::Builtin(
1621            "`textureSample` first argument must be a sampled or depth texture",
1622        ))
1623    }
1624}
1625
1626/// `textureSampleBias()` builtin function.
1627///
1628/// Reference: <https://www.w3.org/TR/WGSL/#textureSampleBias>
1629// TODO: typecheck the other arguments
1630pub fn textureSampleBias(
1631    e1: &Type,
1632    _e2: &Type,
1633    _e3: &Type,
1634    _e4: &Type,
1635    _e5: Option<&Type>,
1636    _e6: Option<&Type>,
1637) -> Result<Type, E> {
1638    if let Type::Texture(t) = e1 {
1639        if t.dimensions() == TextureDimensions::D1 {
1640            Err(E::Builtin(
1641                "`textureSampleBias` texture cannot be 1-dimensional",
1642            ))
1643        } else if t.is_sampled() {
1644            Ok(Type::Vec(4, Type::F32.into()))
1645        } else {
1646            Err(E::Builtin(
1647                "`textureSampleBias` first argument must be a sampled texture",
1648            ))
1649        }
1650    } else {
1651        Err(E::Builtin(
1652            "`textureSampleBias` first argument must be a sampled texture",
1653        ))
1654    }
1655}
1656
1657/// `textureSampleCompare()` builtin function.
1658///
1659/// Reference: <https://www.w3.org/TR/WGSL/#textureSampleCompare>
1660// TODO: typecheck the other arguments
1661pub fn textureSampleCompare(
1662    e1: &Type,
1663    _e2: &Type,
1664    _e3: &Type,
1665    _e4: &Type,
1666    _e5: Option<&Type>,
1667    _e6: Option<&Type>,
1668) -> Result<Type, E> {
1669    if let Type::Texture(t) = e1
1670        && t.is_depth()
1671    {
1672        Ok(Type::F32)
1673    } else {
1674        Err(E::Builtin(
1675            "`textureSampleCompare` first argument must be a depth texture",
1676        ))
1677    }
1678}
1679
1680/// `textureSampleCompareLevel()` builtin function.
1681///
1682/// Reference: <https://www.w3.org/TR/WGSL/#textureSampleCompareLevel>
1683// TODO: typecheck the other arguments
1684pub fn textureSampleCompareLevel(
1685    e1: &Type,
1686    _e2: &Type,
1687    _e3: &Type,
1688    _e4: &Type,
1689    _e5: Option<&Type>,
1690    _e6: Option<&Type>,
1691) -> Result<Type, E> {
1692    if let Type::Texture(t) = e1
1693        && t.is_depth()
1694    {
1695        Ok(Type::F32)
1696    } else {
1697        Err(E::Builtin(
1698            "`textureSampleCompareLevel` first argument must be a depth texture",
1699        ))
1700    }
1701}
1702
1703/// `textureSampleGrad()` builtin function.
1704///
1705/// Reference: <https://www.w3.org/TR/WGSL/#textureSampleGrad>
1706// TODO: typecheck the other arguments
1707pub fn textureSampleGrad(
1708    e1: &Type,
1709    _e2: &Type,
1710    _e3: &Type,
1711    _e4: &Type,
1712    _e5: &Type,
1713    _e6: Option<&Type>,
1714    _e7: Option<&Type>,
1715) -> Result<Type, E> {
1716    if let Type::Texture(t) = e1 {
1717        if cfg!(feature = "naga-ext") && t.dimensions() == TextureDimensions::D1 {
1718            // naga allows 1-D sampleGrad, in which case ddx and ddy are floats.
1719            Ok(Type::Vec(4, Type::F32.into()))
1720        } else if t.dimensions() == TextureDimensions::D1 {
1721            Err(E::Builtin(
1722                "`textureSampleGrad` texture cannot be 1-dimensional",
1723            ))
1724        } else if t.is_sampled() {
1725            Ok(Type::Vec(4, Type::F32.into()))
1726        } else {
1727            Err(E::Builtin(
1728                "`textureSampleGrad` first argument must be a sampled texture",
1729            ))
1730        }
1731    } else {
1732        Err(E::Builtin(
1733            "`textureSampleGrad` first argument must be a sampled texture",
1734        ))
1735    }
1736}
1737
1738/// `textureSampleLevel()` builtin function.
1739///
1740/// Reference: <https://www.w3.org/TR/WGSL/#textureSampleLevel>
1741// TODO: typecheck the other arguments
1742pub fn textureSampleLevel(
1743    e1: &Type,
1744    _e2: &Type,
1745    _e3: &Type,
1746    _e4: &Type,
1747    _e5: Option<&Type>,
1748    _e6: Option<&Type>,
1749) -> Result<Type, E> {
1750    if let Type::Texture(t) = e1 {
1751        if t.is_sampled() {
1752            Ok(Type::Vec(4, Type::F32.into()))
1753        } else if t.is_depth() {
1754            Ok(Type::F32)
1755        } else {
1756            Err(E::Builtin(
1757                "`textureSampleLevel` first argument must be a sampled or depth texture",
1758            ))
1759        }
1760    } else {
1761        Err(E::Builtin(
1762            "`textureSampleLevel` first argument must be a sampled or depth texture",
1763        ))
1764    }
1765}
1766
1767/// `textureSampleBaseClampToEdge()` builtin function.
1768///
1769/// Reference: <https://www.w3.org/TR/WGSL/#textureSampleBaseClampToEdge>
1770pub fn textureSampleBaseClampToEdge(e1: &Type, _e2: &Type, _e3: &Type) -> Result<Type, E> {
1771    if matches!(
1772        e1,
1773        Type::Texture(TextureType::Sampled2D(SampledType::F32) | TextureType::External)
1774    ) {
1775        Ok(Type::Vec(4, Type::F32.into()))
1776    } else {
1777        Err(E::Builtin(
1778            "`textureSampleBaseClampToEdge` first argument must be a `texture_2d<f32> ` or `texture_external`",
1779        ))
1780    }
1781}
1782
1783/// `textureStore()` builtin function.
1784///
1785/// Reference: <https://www.w3.org/TR/WGSL/#textureStore>
1786// TODO: typecheck the other arguments
1787pub fn textureStore(e1: &Type, _e2: &Type, _e3: &Type, _e4: Option<&Type>) -> Result<(), E> {
1788    if let Type::Texture(t) = e1
1789        && t.is_storage()
1790    {
1791        Ok(())
1792    } else {
1793        Err(E::Builtin(
1794            "`textureStore` first argument must be a storage texture",
1795        ))
1796    }
1797}
1798
1799// ------
1800// ATOMIC
1801// ------
1802// reference: <https://www.w3.org/TR/WGSL/#atomic-builtin-functions>
1803
1804/// `atomicLoad()` builtin function.
1805///
1806/// Reference: <https://www.w3.org/TR/WGSL/#atomicLoad-builtin>
1807pub fn atomicLoad(e: &Type) -> Result<Type, E> {
1808    if let Type::Ptr(a_s, ptr_ty, a_m) = e
1809        && let Type::Atomic(ty) = &**ptr_ty
1810    {
1811        if *a_s != AddressSpace::Storage && *a_s != AddressSpace::Workgroup {
1812            Err(E::Builtin(
1813                "the address space of the atomic pointer argument must be `storage` or `workgroup`",
1814            ))
1815        } else if *a_m != AccessMode::ReadWrite {
1816            Err(E::Builtin(
1817                "the access mode of the atomic pointer argument must be `read_write`",
1818            ))
1819        } else {
1820            Ok(*ty.clone())
1821        }
1822    } else {
1823        Err(E::Builtin(
1824            "`atomicLoad` expects a pointer to atomic argument",
1825        ))
1826    }
1827}
1828
1829/// `atomicStore()` builtin function.
1830///
1831/// Reference: <https://www.w3.org/TR/WGSL/#atomicStore-builtin>
1832pub fn atomicStore(e1: &Type, e2: &Type) -> Result<(), E> {
1833    if let Type::Ptr(a_s, ptr_ty, a_m) = e1
1834        && let Type::Atomic(ty) = &**ptr_ty
1835    {
1836        if *a_s != AddressSpace::Storage && *a_s != AddressSpace::Workgroup {
1837            Err(E::Builtin(
1838                "the address space of the atomic pointer argument must be `storage` or `workgroup`",
1839            ))
1840        } else if *a_m != AccessMode::ReadWrite {
1841            Err(E::Builtin(
1842                "the access mode of the atomic pointer argument must be `read_write`",
1843            ))
1844        } else if e2.is_convertible_to(ty) {
1845            Ok(())
1846        } else {
1847            Err(E::Builtin(
1848                "`atomicStore` 2nd argument is incompatible with the atomic pointer type",
1849            ))
1850        }
1851    } else {
1852        Err(E::Builtin(
1853            "`atomicStore` expects a pointer to atomic argument",
1854        ))
1855    }
1856}
1857
1858/// `atomicAdd()` builtin function.
1859///
1860/// Reference: <https://www.w3.org/TR/WGSL/#atomicAdd-builtin>
1861pub fn atomicAdd(e1: &Type, e2: &Type) -> Result<Type, E> {
1862    if let Type::Ptr(a_s, ptr_ty, a_m) = e1
1863        && let Type::Atomic(ty) = &**ptr_ty
1864    {
1865        if *a_s != AddressSpace::Storage && *a_s != AddressSpace::Workgroup {
1866            Err(E::Builtin(
1867                "the address space of the atomic pointer argument must be `storage` or `workgroup`",
1868            ))
1869        } else if *a_m != AccessMode::ReadWrite {
1870            Err(E::Builtin(
1871                "the access mode of the atomic pointer argument must be `read_write`",
1872            ))
1873        } else if e2.is_convertible_to(ty) {
1874            Ok(*ty.clone())
1875        } else {
1876            Err(E::Builtin(
1877                "`atomicAdd` 2nd argument is incompatible with the atomic pointer type",
1878            ))
1879        }
1880    } else {
1881        Err(E::Builtin(
1882            "`atomicAdd` expects a pointer to atomic argument",
1883        ))
1884    }
1885}
1886
1887/// `atomicSub()` builtin function.
1888///
1889/// Reference: <https://www.w3.org/TR/WGSL/#atomicSub-builtin>
1890pub fn atomicSub(e1: &Type, e2: &Type) -> Result<Type, E> {
1891    if let Type::Ptr(a_s, ptr_ty, a_m) = e1
1892        && let Type::Atomic(ty) = &**ptr_ty
1893    {
1894        if *a_s != AddressSpace::Storage && *a_s != AddressSpace::Workgroup {
1895            Err(E::Builtin(
1896                "the address space of the atomic pointer argument must be `storage` or `workgroup`",
1897            ))
1898        } else if *a_m != AccessMode::ReadWrite {
1899            Err(E::Builtin(
1900                "the access mode of the atomic pointer argument must be `read_write`",
1901            ))
1902        } else if e2.is_convertible_to(ty) {
1903            Ok(*ty.clone())
1904        } else {
1905            Err(E::Builtin(
1906                "`atomicSub` 2nd argument is incompatible with the atomic pointer type",
1907            ))
1908        }
1909    } else {
1910        Err(E::Builtin(
1911            "`atomicSub` expects a pointer to atomic argument",
1912        ))
1913    }
1914}
1915
1916/// `atomicMax()` builtin function.
1917///
1918/// Reference: <https://www.w3.org/TR/WGSL/#atomicMax-builtin>
1919pub fn atomicMax(e1: &Type, e2: &Type) -> Result<Type, E> {
1920    if let Type::Ptr(a_s, ptr_ty, a_m) = e1
1921        && let Type::Atomic(ty) = &**ptr_ty
1922    {
1923        if *a_s != AddressSpace::Storage && *a_s != AddressSpace::Workgroup {
1924            Err(E::Builtin(
1925                "the address space of the atomic pointer argument must be `storage` or `workgroup`",
1926            ))
1927        } else if *a_m != AccessMode::ReadWrite {
1928            Err(E::Builtin(
1929                "the access mode of the atomic pointer argument must be `read_write`",
1930            ))
1931        } else if e2.is_convertible_to(ty) {
1932            Ok(*ty.clone())
1933        } else {
1934            Err(E::Builtin(
1935                "`atomicMax` 2nd argument is incompatible with the atomic pointer type",
1936            ))
1937        }
1938    } else {
1939        Err(E::Builtin(
1940            "`atomicMax` expects a pointer to atomic argument",
1941        ))
1942    }
1943}
1944
1945/// `atomicMin()` builtin function.
1946///
1947/// Reference: <https://www.w3.org/TR/WGSL/#atomicMin-builtin>
1948pub fn atomicMin(e1: &Type, e2: &Type) -> Result<Type, E> {
1949    if let Type::Ptr(a_s, ptr_ty, a_m) = e1
1950        && let Type::Atomic(ty) = &**ptr_ty
1951    {
1952        if *a_s != AddressSpace::Storage && *a_s != AddressSpace::Workgroup {
1953            Err(E::Builtin(
1954                "the address space of the atomic pointer argument must be `storage` or `workgroup`",
1955            ))
1956        } else if *a_m != AccessMode::ReadWrite {
1957            Err(E::Builtin(
1958                "the access mode of the atomic pointer argument must be `read_write`",
1959            ))
1960        } else if e2.is_convertible_to(ty) {
1961            Ok(*ty.clone())
1962        } else {
1963            Err(E::Builtin(
1964                "`atomicMin` 2nd argument is incompatible with the atomic pointer type",
1965            ))
1966        }
1967    } else {
1968        Err(E::Builtin(
1969            "`atomicMin` expects a pointer to atomic argument",
1970        ))
1971    }
1972}
1973
1974/// `atomicAnd()` builtin function.
1975///
1976/// Reference: <https://www.w3.org/TR/WGSL/#atomicAnd-builtin>
1977pub fn atomicAnd(e1: &Type, e2: &Type) -> Result<Type, E> {
1978    if let Type::Ptr(a_s, ptr_ty, a_m) = e1
1979        && let Type::Atomic(ty) = &**ptr_ty
1980    {
1981        if *a_s != AddressSpace::Storage && *a_s != AddressSpace::Workgroup {
1982            Err(E::Builtin(
1983                "the address space of the atomic pointer argument must be `storage` or `workgroup`",
1984            ))
1985        } else if *a_m != AccessMode::ReadWrite {
1986            Err(E::Builtin(
1987                "the access mode of the atomic pointer argument must be `read_write`",
1988            ))
1989        } else if e2.is_convertible_to(ty) {
1990            Ok(*ty.clone())
1991        } else {
1992            Err(E::Builtin(
1993                "`atomicAnd` 2nd argument is incompatible with the atomic pointer type",
1994            ))
1995        }
1996    } else {
1997        Err(E::Builtin(
1998            "`atomicAnd` expects a pointer to atomic argument",
1999        ))
2000    }
2001}
2002
2003/// `atomicOr()` builtin function.
2004///
2005/// Reference: <https://www.w3.org/TR/WGSL/#atomicOr-builtin>
2006pub fn atomicOr(e1: &Type, e2: &Type) -> Result<Type, E> {
2007    if let Type::Ptr(a_s, ptr_ty, a_m) = e1
2008        && let Type::Atomic(ty) = &**ptr_ty
2009    {
2010        if *a_s != AddressSpace::Storage && *a_s != AddressSpace::Workgroup {
2011            Err(E::Builtin(
2012                "the address space of the atomic pointer argument must be `storage` or `workgroup`",
2013            ))
2014        } else if *a_m != AccessMode::ReadWrite {
2015            Err(E::Builtin(
2016                "the access mode of the atomic pointer argument must be `read_write`",
2017            ))
2018        } else if e2.is_convertible_to(ty) {
2019            Ok(*ty.clone())
2020        } else {
2021            Err(E::Builtin(
2022                "`atomicOr` 2nd argument is incompatible with the atomic pointer type",
2023            ))
2024        }
2025    } else {
2026        Err(E::Builtin(
2027            "`atomicOr` expects a pointer to atomic argument",
2028        ))
2029    }
2030}
2031
2032/// `atomicXor()` builtin function.
2033///
2034/// Reference: <https://www.w3.org/TR/WGSL/#atomicXor-builtin>
2035pub fn atomicXor(e1: &Type, e2: &Type) -> Result<Type, E> {
2036    if let Type::Ptr(a_s, ptr_ty, a_m) = e1
2037        && let Type::Atomic(ty) = &**ptr_ty
2038    {
2039        if *a_s != AddressSpace::Storage && *a_s != AddressSpace::Workgroup {
2040            Err(E::Builtin(
2041                "the address space of the atomic pointer argument must be `storage` or `workgroup`",
2042            ))
2043        } else if *a_m != AccessMode::ReadWrite {
2044            Err(E::Builtin(
2045                "the access mode of the atomic pointer argument must be `read_write`",
2046            ))
2047        } else if e2.is_convertible_to(ty) {
2048            Ok(*ty.clone())
2049        } else {
2050            Err(E::Builtin(
2051                "`atomicXor` 2nd argument is incompatible with the atomic pointer type",
2052            ))
2053        }
2054    } else {
2055        Err(E::Builtin(
2056            "`atomicXor` expects a pointer to atomic argument",
2057        ))
2058    }
2059}
2060
2061/// `atomicExchange()` builtin function.
2062///
2063/// Reference: <https://www.w3.org/TR/WGSL/#atomicExchange-builtin>
2064pub fn atomicExchange(e1: &Type, e2: &Type) -> Result<Type, E> {
2065    if let Type::Ptr(a_s, ptr_ty, a_m) = e1
2066        && let Type::Atomic(ty) = &**ptr_ty
2067    {
2068        if *a_s != AddressSpace::Storage && *a_s != AddressSpace::Workgroup {
2069            Err(E::Builtin(
2070                "the address space of the atomic pointer argument must be `storage` or `workgroup`",
2071            ))
2072        } else if *a_m != AccessMode::ReadWrite {
2073            Err(E::Builtin(
2074                "the access mode of the atomic pointer argument must be `read_write`",
2075            ))
2076        } else if e2.is_convertible_to(ty) {
2077            Ok(*ty.clone())
2078        } else {
2079            Err(E::Builtin(
2080                "`atomicExchange` 2nd argument is incompatible with the atomic pointer type",
2081            ))
2082        }
2083    } else {
2084        Err(E::Builtin(
2085            "`atomicExchange` expects a pointer to atomic argument",
2086        ))
2087    }
2088}
2089
2090/// `atomicCompareExchangeWeak()` builtin function.
2091///
2092/// Reference: <https://www.w3.org/TR/WGSL/#atomicCompareExchangeWeak-builtin>
2093pub fn atomicCompareExchangeWeak(e1: &Type, e2: &Type, e3: &Type) -> Result<Type, E> {
2094    let Type::Ptr(a_s, ptr_ty, a_m) = e1 else {
2095        return Err(E::Builtin(
2096            "`atomicCompareExchangeWeak` expects a pointer to atomic argument",
2097        ));
2098    };
2099    let Type::Atomic(ty) = &**ptr_ty else {
2100        return Err(E::Builtin(
2101            "`atomicCompareExchangeWeak` expects a pointer to atomic argument",
2102        ));
2103    };
2104    if *a_s != AddressSpace::Storage && *a_s != AddressSpace::Workgroup {
2105        Err(E::Builtin(
2106            "the address space of the atomic pointer argument must be `storage` or `workgroup`",
2107        ))
2108    } else if *a_m != AccessMode::ReadWrite {
2109        Err(E::Builtin(
2110            "the access mode of the atomic pointer argument must be `read_write`",
2111        ))
2112    } else if !e2.is_convertible_to(ty) && !e3.is_convertible_to(ty) {
2113        Err(E::Builtin(
2114            "`atomicCompareExchangeWeak` 2nd and 3rd arguments are incompatible with the atomic pointer type",
2115        ))
2116    } else if !e2.is_convertible_to(ty) {
2117        Err(E::Builtin(
2118            "`atomicCompareExchangeWeak` 2nd argument is incompatible with the atomic pointer type",
2119        ))
2120    } else if !e3.is_convertible_to(ty) {
2121        Err(E::Builtin(
2122            "`atomicCompareExchangeWeak` 3rd argument is incompatible with the atomic pointer type",
2123        ))
2124    } else {
2125        Ok(atomic_compare_exchange_struct_type(ty).into())
2126    }
2127}
2128
2129// ------------
2130// DATA PACKING
2131// ------------
2132// reference: <https://www.w3.org/TR/WGSL/#pack-builtin-functions>
2133
2134/// `pack4x8snorm()` builtin function.
2135///
2136/// Reference: <https://www.w3.org/TR/WGSL/#pack4x8snorm-builtin>
2137pub fn pack4x8snorm(e: &Type) -> Result<Type, E> {
2138    if e.is_convertible_to(&Type::Vec(4, Type::F32.into())) {
2139        Ok(Type::U32)
2140    } else {
2141        Err(E::Builtin("`pack4x8snorm` expects a `vec4<f32>` argument"))
2142    }
2143}
2144
2145/// `pack4x8unorm()` builtin function.
2146///
2147/// Reference: <https://www.w3.org/TR/WGSL/#pack4x8unorm-builtin>
2148pub fn pack4x8unorm(e: &Type) -> Result<Type, E> {
2149    if e.is_convertible_to(&Type::Vec(4, Type::F32.into())) {
2150        Ok(Type::U32)
2151    } else {
2152        Err(E::Builtin("`pack4x8unorm` expects a `vec4<f32>` argument"))
2153    }
2154}
2155
2156/// `pack4xI8()` builtin function.
2157///
2158/// Reference: <https://www.w3.org/TR/WGSL/#pack4xI8-builtin>
2159pub fn pack4xI8(e: &Type) -> Result<Type, E> {
2160    if e.is_convertible_to(&Type::Vec(4, Type::I32.into())) {
2161        Ok(Type::U32)
2162    } else {
2163        Err(E::Builtin("`pack4xI8` expects a `vec4<i32>` argument"))
2164    }
2165}
2166
2167/// `pack4xU8()` builtin function.
2168///
2169/// Reference: <https://www.w3.org/TR/WGSL/#pack4xU8-builtin>
2170pub fn pack4xU8(e: &Type) -> Result<Type, E> {
2171    if e.is_convertible_to(&Type::Vec(4, Type::U32.into())) {
2172        Ok(Type::U32)
2173    } else {
2174        Err(E::Builtin("`pack4xU8` expects a `vec4<u32>` argument"))
2175    }
2176}
2177
2178/// `pack4xI8Clamp()` builtin function.
2179///
2180/// Reference: <https://www.w3.org/TR/WGSL/#pack4xI8Clamp-builtin>
2181pub fn pack4xI8Clamp(e: &Type) -> Result<Type, E> {
2182    if e.is_convertible_to(&Type::Vec(4, Type::I32.into())) {
2183        Ok(Type::U32)
2184    } else {
2185        Err(E::Builtin("`pack4xI8Clamp` expects a `vec4<i32>` argument"))
2186    }
2187}
2188
2189/// `pack4xU8Clamp()` builtin function.
2190///
2191/// Reference: <https://www.w3.org/TR/WGSL/#pack4xU8Clamp-builtin>
2192pub fn pack4xU8Clamp(e: &Type) -> Result<Type, E> {
2193    if e.is_convertible_to(&Type::Vec(4, Type::U32.into())) {
2194        Ok(Type::U32)
2195    } else {
2196        Err(E::Builtin("`pack4xU8Clamp` expects a `vec4<u32>` argument"))
2197    }
2198}
2199
2200/// `pack2x16snorm()` builtin function.
2201///
2202/// Reference: <https://www.w3.org/TR/WGSL/#pack2x16snorm-builtin>
2203pub fn pack2x16snorm(e: &Type) -> Result<Type, E> {
2204    if e.is_convertible_to(&Type::Vec(2, Type::F32.into())) {
2205        Ok(Type::U32)
2206    } else {
2207        Err(E::Builtin("`pack2x16snorm` expects a `vec2<f32>` argument"))
2208    }
2209}
2210
2211/// `pack2x16unorm()` builtin function.
2212///
2213/// Reference: <https://www.w3.org/TR/WGSL/#pack2x16unorm-builtin>
2214pub fn pack2x16unorm(e: &Type) -> Result<Type, E> {
2215    if e.is_convertible_to(&Type::Vec(2, Type::F32.into())) {
2216        Ok(Type::U32)
2217    } else {
2218        Err(E::Builtin("`pack2x16unorm` expects a `vec2<f32>` argument"))
2219    }
2220}
2221
2222/// `pack2x16float()` builtin function.
2223///
2224/// Reference: <https://www.w3.org/TR/WGSL/#pack2x16float-builtin>
2225pub fn pack2x16float(e: &Type) -> Result<Type, E> {
2226    if e.is_convertible_to(&Type::Vec(2, Type::F32.into())) {
2227        Ok(Type::U32)
2228    } else {
2229        Err(E::Builtin("`pack2x16float` expects a `vec2<f32>` argument"))
2230    }
2231}
2232
2233/// `unpack4x8snorm()` builtin function.
2234///
2235/// Reference: <https://www.w3.org/TR/WGSL/#unpack4x8snorm-builtin>
2236pub fn unpack4x8snorm(e: &Type) -> Result<Type, E> {
2237    if e.is_convertible_to(&Type::U32) {
2238        Ok(Type::Vec(4, Type::F32.into()))
2239    } else {
2240        Err(E::Builtin("`unpack4x8snorm` expects a `u32` argument"))
2241    }
2242}
2243
2244/// `unpack4x8unorm()` builtin function.
2245///
2246/// Reference: <https://www.w3.org/TR/WGSL/#unpack4x8unorm-builtin>
2247pub fn unpack4x8unorm(e: &Type) -> Result<Type, E> {
2248    if e.is_convertible_to(&Type::U32) {
2249        Ok(Type::Vec(4, Type::F32.into()))
2250    } else {
2251        Err(E::Builtin("`unpack4x8unorm` expects a `u32` argument"))
2252    }
2253}
2254
2255/// `unpack4xI8()` builtin function.
2256///
2257/// Reference: <https://www.w3.org/TR/WGSL/#unpack4xI8-builtin>
2258pub fn unpack4xI8(e: &Type) -> Result<Type, E> {
2259    if e.is_convertible_to(&Type::U32) {
2260        Ok(Type::Vec(4, Type::I32.into()))
2261    } else {
2262        Err(E::Builtin("`unpack4xI8` expects a `u32` argument"))
2263    }
2264}
2265
2266/// `unpack4xU8()` builtin function.
2267///
2268/// Reference: <https://www.w3.org/TR/WGSL/#unpack4xU8-builtin>
2269pub fn unpack4xU8(e: &Type) -> Result<Type, E> {
2270    if e.is_convertible_to(&Type::U32) {
2271        Ok(Type::Vec(4, Type::U32.into()))
2272    } else {
2273        Err(E::Builtin("`unpack4xU8` expects a `u32` argument"))
2274    }
2275}
2276
2277/// `unpack2x16snorm()` builtin function.
2278///
2279/// Reference: <https://www.w3.org/TR/WGSL/#unpack2x16snorm-builtin>
2280pub fn unpack2x16snorm(e: &Type) -> Result<Type, E> {
2281    if e.is_convertible_to(&Type::U32) {
2282        Ok(Type::Vec(2, Type::F32.into()))
2283    } else {
2284        Err(E::Builtin("`unpack2x16snorm` expects a `u32` argument"))
2285    }
2286}
2287
2288/// `unpack2x16unorm()` builtin function.
2289///
2290/// Reference: <https://www.w3.org/TR/WGSL/#unpack2x16unorm-builtin>
2291pub fn unpack2x16unorm(e: &Type) -> Result<Type, E> {
2292    if e.is_convertible_to(&Type::U32) {
2293        Ok(Type::Vec(2, Type::F32.into()))
2294    } else {
2295        Err(E::Builtin("`unpack2x16unorm` expects a `u32` argument"))
2296    }
2297}
2298
2299/// `unpack2x16float()` builtin function.
2300///
2301/// Reference: <https://www.w3.org/TR/WGSL/#unpack2x16float-builtin>
2302pub fn unpack2x16float(e: &Type) -> Result<Type, E> {
2303    if e.is_convertible_to(&Type::U32) {
2304        Ok(Type::Vec(2, Type::F32.into()))
2305    } else {
2306        Err(E::Builtin("`unpack2x16float` expects a `u32` argument"))
2307    }
2308}
2309
2310// --------
2311// SUBGROUP
2312// --------
2313// reference: <https://www.w3.org/TR/WGSL/#subgroup-builtin-functions>
2314
2315/// `subgroupAdd()` builtin function.
2316///
2317/// Reference: <https://www.w3.org/TR/WGSL/#subgroupAdd-builtin>
2318pub fn subgroupAdd(e: &Type) -> Result<Type, E> {
2319    if inner_is_numeric(e) {
2320        Ok(e.concretize())
2321    } else {
2322        Err(E::Builtin(
2323            "`subgroupAdd` expects a numeric scalar or vector argument",
2324        ))
2325    }
2326}
2327
2328/// `subgroupExclusiveAdd()` builtin function.
2329///
2330/// Reference: <https://www.w3.org/TR/WGSL/#subgroupExclusiveAdd-builtin>
2331pub fn subgroupExclusiveAdd(e: &Type) -> Result<Type, E> {
2332    if inner_is_numeric(e) {
2333        Ok(e.concretize())
2334    } else {
2335        Err(E::Builtin(
2336            "`subgroupExclusiveAdd` expects a numeric scalar or vector argument",
2337        ))
2338    }
2339}
2340
2341/// `subgroupInclusiveAdd()` builtin function.
2342///
2343/// Reference: <https://www.w3.org/TR/WGSL/#subgroupInclusiveAdd-builtin>
2344pub fn subgroupInclusiveAdd(e: &Type) -> Result<Type, E> {
2345    if inner_is_numeric(e) {
2346        Ok(e.concretize())
2347    } else {
2348        Err(E::Builtin(
2349            "`subgroupInclusiveAdd` expects a numeric scalar or vector argument",
2350        ))
2351    }
2352}
2353
2354/// `subgroupAll()` builtin function.
2355///
2356/// Reference: <https://www.w3.org/TR/WGSL/#subgroupAll-builtin>
2357pub fn subgroupAll(e: &Type) -> Result<Type, E> {
2358    if e.is_bool() {
2359        Ok(Type::Bool)
2360    } else {
2361        Err(E::Builtin("`subgroupAll` expects a boolean argument"))
2362    }
2363}
2364
2365/// `subgroupAnd()` builtin function.
2366///
2367/// Reference: <https://www.w3.org/TR/WGSL/#subgroupAnd-builtin>
2368pub fn subgroupAnd(e: &Type) -> Result<Type, E> {
2369    if inner_is_integer(e) {
2370        Ok(e.concretize())
2371    } else {
2372        Err(E::Builtin(
2373            "`subgroupAnd` expects an integer scalar or vector argument",
2374        ))
2375    }
2376}
2377
2378/// `subgroupAny()` builtin function.
2379///
2380/// Reference: <https://www.w3.org/TR/WGSL/#subgroupAny-builtin>
2381pub fn subgroupAny(e: &Type) -> Result<Type, E> {
2382    if e.is_bool() {
2383        Ok(Type::Bool)
2384    } else {
2385        Err(E::Builtin("`subgroupAny` expects a boolean argument"))
2386    }
2387}
2388
2389/// `subgroupBallot()` builtin function.
2390///
2391/// NOTE: The `naga-ext` extension allows omitting the predicate argument.
2392///
2393/// Reference: <https://www.w3.org/TR/WGSL/#subgroupBallot-builtin>
2394pub fn subgroupBallot(pred: Option<&Type>) -> Result<Type, E> {
2395    if let Some(pred) = pred
2396        && pred.is_bool()
2397    {
2398        Ok(Type::Vec(4, Type::U32.into()))
2399    } else if pred.is_none() && cfg!(feature = "naga-ext") {
2400        Ok(Type::Vec(4, Type::U32.into()))
2401    } else {
2402        Err(E::Builtin("`subgroupBallot` expects a boolean argument"))
2403    }
2404}
2405
2406/// `subgroupBroadcast()` builtin function.
2407///
2408/// Reference: <https://www.w3.org/TR/WGSL/#subgroupBroadcast-builtin>
2409pub fn subgroupBroadcast(e: &Type, id: &Type) -> Result<Type, E> {
2410    if inner_is_numeric(e) && id.is_integer() {
2411        Ok(e.concretize())
2412    } else {
2413        Err(E::Builtin(
2414            "`subgroupBroadcast` expects a numeric scalar or vector 1st argument and an integer 2nd argument",
2415        ))
2416    }
2417}
2418
2419/// `subgroupBroadcastFirst()` builtin function.
2420///
2421/// Reference: <https://www.w3.org/TR/WGSL/#subgroupBroadcastFirst-builtin>
2422pub fn subgroupBroadcastFirst(e: &Type) -> Result<Type, E> {
2423    if inner_is_numeric(e) {
2424        Ok(e.concretize())
2425    } else {
2426        Err(E::Builtin(
2427            "`subgroupBroadcastFirst` expects a numeric scalar or vector argument",
2428        ))
2429    }
2430}
2431
2432/// `subgroupElect()` builtin function.
2433///
2434/// Reference: <https://www.w3.org/TR/WGSL/#subgroupElect-builtin>
2435pub fn subgroupElect() -> Result<Type, E> {
2436    Ok(Type::Bool)
2437}
2438
2439/// `subgroupMax()` builtin function.
2440///
2441/// Reference: <https://www.w3.org/TR/WGSL/#subgroupMax-builtin>
2442pub fn subgroupMax(e: &Type) -> Result<Type, E> {
2443    if inner_is_numeric(e) {
2444        Ok(e.concretize())
2445    } else {
2446        Err(E::Builtin(
2447            "`subgroupMax` expects a numeric scalar or vector argument",
2448        ))
2449    }
2450}
2451
2452/// `subgroupMin()` builtin function.
2453///
2454/// Reference: <https://www.w3.org/TR/WGSL/#subgroupMin-builtin>
2455pub fn subgroupMin(e: &Type) -> Result<Type, E> {
2456    if inner_is_numeric(e) {
2457        Ok(e.concretize())
2458    } else {
2459        Err(E::Builtin(
2460            "`subgroupMin` expects a numeric scalar or vector argument",
2461        ))
2462    }
2463}
2464
2465/// `subgroupMul()` builtin function.
2466///
2467/// Reference: <https://www.w3.org/TR/WGSL/#subgroupMul-builtin>
2468pub fn subgroupMul(e: &Type) -> Result<Type, E> {
2469    if inner_is_numeric(e) {
2470        Ok(e.concretize())
2471    } else {
2472        Err(E::Builtin(
2473            "`subgroupMul` expects a numeric scalar or vector argument",
2474        ))
2475    }
2476}
2477
2478/// `subgroupExclusiveMul()` builtin function.
2479///
2480/// Reference: <https://www.w3.org/TR/WGSL/#subgroupExclusiveMul-builtin>
2481pub fn subgroupExclusiveMul(e: &Type) -> Result<Type, E> {
2482    if inner_is_numeric(e) {
2483        Ok(e.concretize())
2484    } else {
2485        Err(E::Builtin(
2486            "`subgroupExclusiveMul` expects a numeric scalar or vector argument",
2487        ))
2488    }
2489}
2490
2491/// `subgroupInclusiveMul()` builtin function.
2492///
2493/// Reference: <https://www.w3.org/TR/WGSL/#subgroupInclusiveMul-builtin>
2494pub fn subgroupInclusiveMul(e: &Type) -> Result<Type, E> {
2495    if inner_is_numeric(e) {
2496        Ok(e.concretize())
2497    } else {
2498        Err(E::Builtin(
2499            "`subgroupInclusiveMul` expects a numeric scalar or vector argument",
2500        ))
2501    }
2502}
2503
2504/// `subgroupOr()` builtin function.
2505///
2506/// Reference: <https://www.w3.org/TR/WGSL/#subgroupOr-builtin>
2507pub fn subgroupOr(e: &Type) -> Result<Type, E> {
2508    if inner_is_integer(e) {
2509        Ok(e.concretize())
2510    } else {
2511        Err(E::Builtin(
2512            "`subgroupOr` expects an integer scalar or vector argument",
2513        ))
2514    }
2515}
2516
2517/// `subgroupShuffle()` builtin function.
2518///
2519/// Reference: <https://www.w3.org/TR/WGSL/#subgroupShuffle-builtin>
2520pub fn subgroupShuffle(e: &Type, id: &Type) -> Result<Type, E> {
2521    if inner_is_numeric(e) && id.is_integer() {
2522        Ok(e.concretize())
2523    } else {
2524        Err(E::Builtin(
2525            "`subgroupShuffle` expects a numeric scalar or vector 1st argument and an integer 2nd argument",
2526        ))
2527    }
2528}
2529
2530/// `subgroupShuffleDown()` builtin function.
2531///
2532/// Reference: <https://www.w3.org/TR/WGSL/#subgroupShuffleDown-builtin>
2533pub fn subgroupShuffleDown(e: &Type, delta: &Type) -> Result<Type, E> {
2534    if inner_is_numeric(e) && delta.is_convertible_to(&Type::U32) {
2535        Ok(e.concretize())
2536    } else {
2537        Err(E::Builtin(
2538            "`subgroupShuffleDown` expects a numeric scalar or vector 1st argument and an integer 2nd argument",
2539        ))
2540    }
2541}
2542
2543/// `subgroupShuffleUp()` builtin function.
2544///
2545/// Reference: <https://www.w3.org/TR/WGSL/#subgroupShuffleUp-builtin>
2546pub fn subgroupShuffleUp(e: &Type, delta: &Type) -> Result<Type, E> {
2547    if inner_is_numeric(e) && delta.is_convertible_to(&Type::U32) {
2548        Ok(e.concretize())
2549    } else {
2550        Err(E::Builtin(
2551            "`subgroupShuffleUp` expects a numeric scalar or vector 1st argument and an integer 2nd argument",
2552        ))
2553    }
2554}
2555
2556/// `subgroupShuffleXor()` builtin function.
2557///
2558/// Reference: <https://www.w3.org/TR/WGSL/#subgroupShuffleXor-builtin>
2559pub fn subgroupShuffleXor(e: &Type, mask: &Type) -> Result<Type, E> {
2560    if inner_is_numeric(e) && mask.is_convertible_to(&Type::U32) {
2561        Ok(e.concretize())
2562    } else {
2563        Err(E::Builtin(
2564            "`subgroupShuffleXor` expects a numeric scalar or vector 1st argument and an integer 2nd argument",
2565        ))
2566    }
2567}
2568
2569/// `subgroupXor()` builtin function.
2570///
2571/// Reference: <https://www.w3.org/TR/WGSL/#subgroupXor-builtin>
2572pub fn subgroupXor(e: &Type) -> Result<Type, E> {
2573    if inner_is_integer(e) {
2574        Ok(e.concretize())
2575    } else {
2576        Err(E::Builtin(
2577            "`subgroupXor` expects an integer scalar or vector argument",
2578        ))
2579    }
2580}
2581
2582// ----
2583// QUAD
2584// ----
2585// reference: <https://www.w3.org/TR/WGSL/#quad-builtin-functions>
2586
2587/// `quadBroadcast()` builtin function.
2588///
2589/// Reference: <https://www.w3.org/TR/WGSL/#quadBroadcast-builtin>
2590pub fn quadBroadcast(e: &Type, id: &Type) -> Result<Type, E> {
2591    if inner_is_numeric(e) && id.is_integer() {
2592        Ok(e.concretize())
2593    } else {
2594        Err(E::Builtin(
2595            "`quadBroadcast` expects a numeric scalar or vector 1st argument and an integer 2nd argument",
2596        ))
2597    }
2598}
2599
2600/// `quadSwapDiagonal()` builtin function.
2601///
2602/// Reference: <https://www.w3.org/TR/WGSL/#quadSwapDiagonal-builtin>
2603pub fn quadSwapDiagonal(e: &Type) -> Result<Type, E> {
2604    if inner_is_numeric(e) {
2605        Ok(e.concretize())
2606    } else {
2607        Err(E::Builtin(
2608            "`quadSwapDiagonal` expects a numeric scalar or vector argument",
2609        ))
2610    }
2611}
2612
2613/// `quadSwapX()` builtin function.
2614///
2615/// Reference: <https://www.w3.org/TR/WGSL/#quadSwapX-builtin>
2616pub fn quadSwapX(e: &Type) -> Result<Type, E> {
2617    if inner_is_numeric(e) {
2618        Ok(e.concretize())
2619    } else {
2620        Err(E::Builtin(
2621            "`quadSwapX` expects a numeric scalar or vector argument",
2622        ))
2623    }
2624}
2625
2626/// `quadSwapY()` builtin function.
2627///
2628/// Reference: <https://www.w3.org/TR/WGSL/#quadSwapY-builtin>
2629pub fn quadSwapY(e: &Type) -> Result<Type, E> {
2630    if inner_is_numeric(e) {
2631        Ok(e.concretize())
2632    } else {
2633        Err(E::Builtin(
2634            "`quadSwapY` expects a numeric scalar or vector argument",
2635        ))
2636    }
2637}
2638
2639// -----------------------
2640// NAGA RAY QUERY EXTENSION
2641// -----------------------
2642// These built-ins are `naga` extensions and are not part of the WGSL specification.
2643//
2644// TODO: validate naga extensions arguments
2645
2646/// `rayQueryInitialize()` `naga` built-in function.
2647#[cfg(feature = "naga-ext")]
2648pub fn rayQueryInitialize(rq: &Type, accel_struct: &Type, ray_desc: &Type) -> Result<(), E> {
2649    if matches!(
2650        rq,
2651        Type::Ptr(AddressSpace::Function, t, AccessMode::ReadWrite) if matches!(**t, Type::RayQuery(_))
2652    ) && matches!(accel_struct, Type::AccelerationStructure(_))
2653        && matches!(ray_desc, Type::Struct(s) if s.name == "RayDesc")
2654    {
2655        Ok(())
2656    } else {
2657        Err(E::Builtin(
2658            "`rayQueryInitialize` expects a pointer to `ray_query`, an acceleration structure and a `RayDesc` argument",
2659        ))
2660    }
2661}
2662
2663/// `rayQueryProceed()` `naga` built-in function.
2664#[cfg(feature = "naga-ext")]
2665pub fn rayQueryProceed(rq: &Type) -> Result<Type, E> {
2666    if matches!(
2667        rq,
2668        Type::Ptr(AddressSpace::Function, t, AccessMode::ReadWrite) if matches!(**t, Type::RayQuery(_))
2669    ) {
2670        Ok(Type::Bool)
2671    } else {
2672        Err(E::Builtin(
2673            "`rayQueryProceed` expects a pointer to `ray_query` argument",
2674        ))
2675    }
2676}
2677
2678/// `rayQueryGenerateIntersection()` `naga` built-in function.
2679#[cfg(feature = "naga-ext")]
2680pub fn rayQueryGenerateIntersection(rq: &Type, hit_t: &Type) -> Result<(), E> {
2681    if !matches!(
2682        rq,
2683        Type::Ptr(AddressSpace::Function, t, AccessMode::ReadWrite) if matches!(**t, Type::RayQuery(_))
2684    ) {
2685        Err(E::Builtin(
2686            "`rayQueryGenerateIntersection` 1st argument must be a pointer to `ray_query`",
2687        ))
2688    } else if hit_t.is_convertible_to(&Type::F32) {
2689        Ok(())
2690    } else {
2691        Err(E::Builtin(
2692            "`rayQueryGenerateIntersection` 2nd argument must be a `f32`",
2693        ))
2694    }
2695}
2696
2697/// `rayQueryConfirmIntersection()` `naga` built-in function.
2698#[cfg(feature = "naga-ext")]
2699pub fn rayQueryConfirmIntersection(rq: &Type) -> Result<(), E> {
2700    if !matches!(
2701        rq,
2702        Type::Ptr(AddressSpace::Function, t, AccessMode::ReadWrite) if matches!(**t, Type::RayQuery(_))
2703    ) {
2704        Err(E::Builtin(
2705            "`rayQueryConfirmIntersection` expects a pointer to `ray_query` argument",
2706        ))
2707    } else {
2708        Ok(())
2709    }
2710}
2711
2712/// `rayQueryTerminate()` `naga` built-in function.
2713#[cfg(feature = "naga-ext")]
2714pub fn rayQueryTerminate(rq: &Type) -> Result<(), E> {
2715    if !matches!(
2716        rq,
2717        Type::Ptr(AddressSpace::Function, t, AccessMode::ReadWrite) if matches!(**t, Type::RayQuery(_))
2718    ) {
2719        Err(E::Builtin(
2720            "`rayQueryTerminate` expects a pointer to `ray_query` argument",
2721        ))
2722    } else {
2723        Ok(())
2724    }
2725}
2726
2727/// `rayQueryGetCommittedIntersection()` `naga` built-in function.
2728#[cfg(feature = "naga-ext")]
2729pub fn rayQueryGetCommittedIntersection(e: &Type) -> Result<Type, E> {
2730    if matches!(
2731        e,
2732        Type::Ptr(AddressSpace::Function, t, AccessMode::ReadWrite) if matches!(**t, Type::RayQuery(_))
2733    ) {
2734        Ok(ray_intersection_struct_type().into())
2735    } else {
2736        Err(E::Builtin(
2737            "`rayQueryGetCommittedIntersection` expects a pointer to `ray_query` argument",
2738        ))
2739    }
2740}
2741
2742/// `rayQueryGetCandidateIntersection()` `naga` built-in function.
2743#[cfg(feature = "naga-ext")]
2744pub fn rayQueryGetCandidateIntersection(e: &Type) -> Result<Type, E> {
2745    if matches!(
2746        e,
2747        Type::Ptr(AddressSpace::Function, t, AccessMode::ReadWrite) if matches!(**t, Type::RayQuery(_))
2748    ) {
2749        Ok(ray_intersection_struct_type().into())
2750    } else {
2751        Err(E::Builtin(
2752            "`rayQueryGetCandidateIntersection` expects a pointer to `ray_query` argument",
2753        ))
2754    }
2755}
2756
2757/// `getCommittedHitVertexPositions()` `naga` built-in function.
2758#[cfg(feature = "naga-ext")]
2759pub fn getCommittedHitVertexPositions(e: &Type) -> Result<Type, E> {
2760    if matches!(
2761        e,
2762        Type::Ptr(AddressSpace::Function, t, AccessMode::ReadWrite) if matches!(**t, Type::RayQuery(_))
2763    ) {
2764        Ok(Type::Array(
2765            Box::new(Type::Vec(3, Box::new(Type::F32))),
2766            Some(3),
2767        ))
2768    } else {
2769        Err(E::Builtin(
2770            "`getCommittedHitVertexPositions` expects a pointer to `ray_query` argument",
2771        ))
2772    }
2773}
2774
2775/// `getCandidateHitVertexPositions()` `naga` built-in function.
2776#[cfg(feature = "naga-ext")]
2777pub fn getCandidateHitVertexPositions(e: &Type) -> Result<Type, E> {
2778    if matches!(
2779        e,
2780        Type::Ptr(AddressSpace::Function, t, AccessMode::ReadWrite) if matches!(**t, Type::RayQuery(_))
2781    ) {
2782        Ok(Type::Array(
2783            Box::new(Type::Vec(3, Box::new(Type::F32))),
2784            Some(3),
2785        ))
2786    } else {
2787        Err(E::Builtin(
2788            "`getCandidateHitVertexPositions` expects a pointer to `ray_query` argument",
2789        ))
2790    }
2791}
2792
2793// ------------------------------------
2794// NAGA RAY TRACING PIPELINES EXTENSION
2795// ------------------------------------
2796// These built-ins are `naga` extensions and are not part of the WGSL specification.
2797
2798/// `traceRay()` `naga` built-in function.
2799#[cfg(feature = "naga-ext")]
2800pub fn traceRay(accel_struct: &Type, ray_desc: &Type, payload: &Type) -> Result<(), E> {
2801    if matches!(accel_struct, Type::AccelerationStructure(_))
2802        && matches!(ray_desc, Type::Struct(s) if s.name == "RayDesc")
2803        && matches!(
2804            payload,
2805            Type::Ptr(
2806                AddressSpace::RayPayload | AddressSpace::IncomingRayPayload,
2807                _,
2808                _
2809            )
2810        )
2811    {
2812        Ok(())
2813    } else {
2814        Err(E::Builtin(
2815            "`traceRay` expects an acceleration structure, a `RayDesc` and a pointer to a `ray_payload` or `incoming_ray_payload` variable",
2816        ))
2817    }
2818}