Skip to main content

wgsl_types/
conv.rs

1//! Type conversion algorithms.
2//!
3//! Implementation of the [conversion_rank] algorithm and utilities to convert
4//! [`Type`]s and [`Instance`]s using [automatic conversions].
5//!
6//! [automatic conversions]: https://www.w3.org/TR/WGSL/#feasible-automatic-conversion
7
8use itertools::Itertools;
9use num_traits::{FromPrimitive, ToPrimitive};
10
11use crate::{
12    Error, Instance, f16,
13    inst::{ArrayInstance, LiteralInstance, MatInstance, StructInstance, VecInstance},
14    syntax::AccessMode,
15    ty::{Ty, Type},
16};
17
18pub trait Convert: Sized + Clone + Ty {
19    /// Convert an instance to another type, if a feasible conversion exists.
20    ///
21    /// E.g. `array<u32>.convert_inner_to(array<f32>)` becomes `array<f32>`.
22    ///
23    /// Reference: <https://www.w3.org/TR/WGSL/#conversion-rank>
24    fn convert_to(&self, ty: &Type) -> Option<Self>;
25
26    /// Convert an instance by changing its inner type to another.
27    ///
28    /// E.g. `array<u32>.convert_inner_to(f32)` becomes `array<f32>`.
29    ///
30    /// Identical to [`Convert::convert_to`] if the type has no inner type.
31    /// Does not check that the resulting type exists, e.g. `i32` is not a valid inner type for
32    /// matrix types.
33    ///
34    ///
35    /// See [`Ty::inner_ty`]
36    /// See [`Convert::convert_to`]
37    fn convert_inner_to(&self, ty: &Type) -> Option<Self> {
38        self.convert_to(ty)
39    }
40
41    /// Convert an abstract instance to a concrete type.
42    ///
43    /// E.g. `array<vec<AbstractInt>>` becomes `array<vec<i32>>`.
44    fn concretize(&self) -> Option<Self> {
45        self.convert_to(&self.ty().concretize())
46    }
47}
48
49impl Convert for Type {
50    fn convert_to(&self, ty: &Type) -> Option<Self> {
51        self.is_convertible_to(ty).then_some(ty.clone())
52    }
53    fn convert_inner_to(&self, ty: &Type) -> Option<Self> {
54        match self {
55            Type::Array(inner, n) => inner
56                .convert_to(ty)
57                .map(|inner| Type::Array(inner.into(), *n)),
58            Type::Vec(n, inner) => inner
59                .convert_to(ty)
60                .map(|inner| Type::Vec(*n, inner.into())),
61            Type::Mat(c, r, inner) => inner
62                .convert_to(ty)
63                .map(|inner| Type::Mat(*c, *r, inner.into())),
64            Type::Atomic(_) => (self == ty).then_some(ty.clone()),
65            Type::Ptr(_, _, _) => (self == ty).then_some(ty.clone()),
66            _ => self.convert_to(ty), // for types that don't have an inner ty
67        }
68    }
69    fn concretize(&self) -> Option<Self> {
70        Some(self.concretize())
71    }
72}
73
74impl Type {
75    pub fn is_convertible_to(&self, ty: &Type) -> bool {
76        conversion_rank(self, ty).is_some()
77    }
78    pub fn concretize(&self) -> Self {
79        match self {
80            Self::AbstractInt => Type::I32,
81            Self::AbstractFloat => Type::F32,
82            Self::Array(ty, n) => Type::Array(ty.concretize().into(), *n),
83            Self::Vec(n, ty) => Type::Vec(*n, ty.concretize().into()),
84            Self::Mat(c, r, ty) if ty.is_abstract() => Type::Mat(*c, *r, Type::F32.into()),
85            _ => self.clone(),
86        }
87    }
88
89    /// Apply the load rule.
90    ///
91    /// Reference: <https://www.w3.org/TR/WGSL/#load-rule>
92    pub fn loaded(self) -> Self {
93        if let Type::Ref(_, ty, _) = self {
94            ty.loaded()
95        } else {
96            self
97        }
98    }
99}
100
101impl Instance {
102    pub fn is_convertible_to(&self, ty: &Type) -> bool {
103        self.ty().is_convertible_to(ty)
104    }
105
106    /// Apply the load rule.
107    ///
108    /// Reference: <https://www.w3.org/TR/WGSL/#load-rule>
109    pub fn loaded(mut self) -> Result<Self, Error> {
110        while let Instance::Ref(r) = self {
111            self = r.read()?.to_owned();
112        }
113        Ok(self)
114    }
115}
116
117impl LiteralInstance {
118    pub fn is_infinite(&self) -> bool {
119        match self {
120            LiteralInstance::Bool(_) => false,
121            LiteralInstance::AbstractInt(_) => false,
122            LiteralInstance::AbstractFloat(n) => n.is_infinite(),
123            LiteralInstance::I32(_) => false,
124            LiteralInstance::U32(_) => false,
125            LiteralInstance::F32(n) => n.is_infinite(),
126            LiteralInstance::F16(n) => n.is_infinite(),
127            #[cfg(feature = "naga-ext")]
128            LiteralInstance::I64(_) => false,
129            #[cfg(feature = "naga-ext")]
130            LiteralInstance::U64(_) => false,
131            #[cfg(feature = "naga-ext")]
132            LiteralInstance::F64(n) => n.is_infinite(),
133        }
134    }
135    pub fn is_finite(&self) -> bool {
136        !self.is_infinite()
137    }
138}
139
140impl Convert for LiteralInstance {
141    fn convert_to(&self, ty: &Type) -> Option<Self> {
142        if ty == &self.ty() {
143            return Some(*self);
144        }
145
146        // TODO: check that these conversions are correctly implemented.
147        // I think they are incorrect. the to_xyz() functions do not perform rounding.
148        // reference: <https://www.w3.org/TR/WGSL/#floating-point-conversion>
149        // ... except that hex literals must be *exactly* representable in the target type.
150        match (self, ty) {
151            (Self::AbstractInt(n), Type::AbstractFloat) => n.to_f64().map(Self::AbstractFloat),
152            (Self::AbstractInt(n), Type::I32) => n.to_i32().map(Self::I32),
153            (Self::AbstractInt(n), Type::U32) => n.to_u32().map(Self::U32),
154            (Self::AbstractInt(n), Type::F32) => n.to_f32().map(Self::F32),
155            (Self::AbstractInt(n), Type::F16) => f16::from_i64(*n).map(Self::F16),
156            (Self::AbstractFloat(n), Type::F32) => n.to_f32().map(Self::F32),
157            (Self::AbstractFloat(n), Type::F16) => Some(Self::F16(f16::from_f64(*n))),
158            _ => None,
159        }
160        .and_then(|n| n.is_finite().then_some(n))
161    }
162}
163
164impl Convert for ArrayInstance {
165    fn convert_to(&self, ty: &Type) -> Option<Self> {
166        if let Type::Array(c_ty, Some(n)) = ty {
167            if *n == self.n() {
168                self.convert_inner_to(c_ty)
169            } else {
170                None
171            }
172        } else if let Type::Array(c_ty, None) = ty {
173            self.convert_inner_to(c_ty)
174        } else {
175            None
176        }
177    }
178    fn convert_inner_to(&self, ty: &Type) -> Option<Self> {
179        let components = self
180            .iter()
181            .map(|c| c.convert_to(ty))
182            .collect::<Option<Vec<_>>>()?;
183        Some(ArrayInstance::new(components, self.runtime_sized))
184    }
185}
186
187impl Convert for VecInstance {
188    fn convert_to(&self, ty: &Type) -> Option<Self> {
189        if let Type::Vec(n, c_ty) = ty {
190            if *n as usize == self.n() {
191                self.convert_inner_to(c_ty)
192            } else {
193                None
194            }
195        } else {
196            None
197        }
198    }
199    fn convert_inner_to(&self, ty: &Type) -> Option<Self> {
200        let components = self
201            .iter()
202            .map(|c| c.convert_to(ty))
203            .collect::<Option<Vec<_>>>()?;
204        Some(VecInstance::new(components))
205    }
206}
207
208impl Convert for MatInstance {
209    fn convert_to(&self, ty: &Type) -> Option<Self> {
210        if let Type::Mat(c, r, c_ty) = ty {
211            if *c as usize == self.c() && *r as usize == self.r() {
212                self.convert_inner_to(c_ty)
213            } else {
214                None
215            }
216        } else {
217            None
218        }
219    }
220    fn convert_inner_to(&self, ty: &Type) -> Option<Self> {
221        let components = self
222            .iter_cols()
223            .map(|c| c.convert_inner_to(ty))
224            .collect::<Option<Vec<_>>>()?;
225        Some(MatInstance::from_cols(components))
226    }
227}
228
229impl Convert for StructInstance {
230    fn convert_to(&self, ty: &Type) -> Option<Self> {
231        if &self.ty() == ty {
232            Some(self.clone())
233        } else if let Type::Struct(s2) = ty {
234            let s1 = &self.ty;
235            if s1.name.starts_with("__") && s2.name.starts_with("__") {
236                // this is a struct type conversion of built-in types.
237                // __frexp_result_* or __modf_result_*
238                // TODO: here we just assume that s2 is a variant of s1. We should
239                // check.
240                if s2.name.ends_with("f32") {
241                    let members = self
242                        .members
243                        .iter()
244                        .map(|inst| inst.convert_inner_to(&Type::F32))
245                        .collect::<Option<Vec<_>>>()?;
246                    Some(StructInstance::new((**s2).clone(), members))
247                } else if s2.name.ends_with("f16") {
248                    let members = self
249                        .members
250                        .iter()
251                        .map(|inst| inst.convert_inner_to(&Type::F16))
252                        .collect::<Option<Vec<_>>>()?;
253                    Some(StructInstance::new((**s2).clone(), members))
254                } else {
255                    None
256                }
257            } else {
258                None
259            }
260        } else {
261            None
262        }
263    }
264}
265
266impl Convert for Instance {
267    fn convert_to(&self, ty: &Type) -> Option<Self> {
268        if &self.ty() == ty {
269            return Some(self.clone());
270        }
271        match self {
272            Self::Literal(l) => l.convert_to(ty).map(Self::Literal),
273            Self::Struct(s) => s.convert_to(ty).map(Self::Struct),
274            Self::Array(a) => a.convert_to(ty).map(Self::Array),
275            Self::Vec(v) => v.convert_to(ty).map(Self::Vec),
276            Self::Mat(m) => m.convert_to(ty).map(Self::Mat),
277            Self::Ptr(_) => None,
278            Self::Ref(r) => r.read().ok().and_then(|r| r.convert_to(ty)), // this is the "load rule". Also performed by `eval_value`.
279            Self::Atomic(_) => None,
280            Self::Opaque(_) => None,
281        }
282    }
283
284    fn convert_inner_to(&self, ty: &Type) -> Option<Self> {
285        match self {
286            Self::Literal(l) => l.convert_inner_to(ty).map(Self::Literal),
287            Self::Struct(_) => None,
288            Self::Array(a) => a.convert_inner_to(ty).map(Self::Array),
289            Self::Vec(v) => v.convert_inner_to(ty).map(Self::Vec),
290            Self::Mat(m) => m.convert_inner_to(ty).map(Self::Mat),
291            Self::Ptr(_) => None,
292            Self::Ref(r) => r.read().ok().and_then(|r| r.convert_inner_to(ty)), // this is the "load rule". Also performed by `eval_value`.
293            Self::Atomic(_) => None,
294            Self::Opaque(_) => None,
295        }
296    }
297}
298
299/// Implements the [conversion rank algorithm](https://www.w3.org/TR/WGSL/#conversion-rank)
300pub fn conversion_rank(ty1: &Type, ty2: &Type) -> Option<u32> {
301    // reference: <https://www.w3.org/TR/WGSL/#conversion-rank>
302    match (ty1, ty2) {
303        (_, _) if ty1 == ty2 => Some(0),
304        (Type::Ref(_, ty1, AccessMode::Read | AccessMode::ReadWrite), ty2) if &**ty1 == ty2 => {
305            Some(0)
306        }
307        (Type::AbstractInt, Type::AbstractFloat) => Some(5),
308        (Type::AbstractInt, Type::I32) => Some(3),
309        (Type::AbstractInt, Type::U32) => Some(4),
310        (Type::AbstractInt, Type::F32) => Some(6),
311        (Type::AbstractInt, Type::F16) => Some(7),
312        (Type::AbstractFloat, Type::F32) => Some(1),
313        (Type::AbstractFloat, Type::F16) => Some(2),
314        // frexp and modf
315        (Type::Struct(s1), Type::Struct(s2)) => {
316            if s1.name.starts_with("__") && s1.name.ends_with("abstract") {
317                if s2.name.ends_with("f32") {
318                    Some(1)
319                } else if s2.name.ends_with("f16") {
320                    Some(2)
321                } else {
322                    None
323                }
324            } else {
325                None
326            }
327        }
328        (Type::Array(ty1, n1), Type::Array(ty2, n2)) if n1 == n2 => conversion_rank(ty1, ty2),
329        (Type::Vec(n1, ty1), Type::Vec(n2, ty2)) if n1 == n2 => conversion_rank(ty1, ty2),
330        (Type::Mat(c1, r1, ty1), Type::Mat(c2, r2, ty2)) if c1 == c2 && r1 == r2 => {
331            conversion_rank(ty1, ty2)
332        }
333        _ => None,
334    }
335}
336
337/// Performs overload resolution when two instances of T are involved (which is the most common).
338/// it just makes sure that the two instance types are the same. This is sufficient in most cases.
339pub fn convert<T: Convert + Ty + Clone>(i1: &T, i2: &T) -> Option<(T, T)> {
340    let (ty1, ty2) = (i1.ty(), i2.ty());
341    let ty = convert_ty(&ty1, &ty2)?;
342    let i1 = i1.convert_to(ty)?;
343    let i2 = i2.convert_to(ty)?;
344    Some((i1, i2))
345}
346
347/// See [`convert`]
348pub fn convert_inner<T1: Convert + Ty + Clone, T2: Convert + Ty + Clone>(
349    i1: &T1,
350    i2: &T2,
351) -> Option<(T1, T2)> {
352    let (ty1, ty2) = (i1.inner_ty(), i2.inner_ty());
353    let ty = convert_ty(&ty1, &ty2)?;
354    let i1 = i1.convert_inner_to(ty)?;
355    let i2 = i2.convert_inner_to(ty)?;
356    Some((i1, i2))
357}
358
359/// See [`convert`]
360pub fn convert_all<'a, T: Convert + Ty + Clone + 'a>(insts: &[T]) -> Option<Vec<T>> {
361    let tys = insts.iter().map(|i| i.ty()).collect_vec();
362    let ty = convert_all_ty(&tys)?;
363    convert_all_to(insts, ty)
364}
365
366/// See [`convert`]
367pub fn convert_all_to<'a, T: Convert + Ty + Clone + 'a>(insts: &[T], ty: &Type) -> Option<Vec<T>> {
368    insts
369        .iter()
370        .map(|inst| inst.convert_to(ty))
371        .collect::<Option<Vec<_>>>()
372}
373
374/// See [`convert`]
375pub fn convert_all_inner_to<'a, T: Convert + Ty + Clone + 'a>(
376    insts: &[T],
377    ty: &Type,
378) -> Option<Vec<T>> {
379    insts
380        .iter()
381        .map(|inst| inst.convert_inner_to(ty))
382        .collect::<Option<Vec<_>>>()
383}
384
385/// Performs overload resolution when two instances of T are involved (which is the most common).
386/// it just makes sure that the two types are the same. This is sufficient in most cases.
387pub fn convert_ty<'a>(ty1: &'a Type, ty2: &'a Type) -> Option<&'a Type> {
388    conversion_rank(ty1, ty2)
389        .map(|_rank| ty2)
390        .or_else(|| conversion_rank(ty2, ty1).map(|_rank| ty1))
391}
392
393/// Performs overload resolution (find the type that all others can be automatically converted to)
394pub fn convert_all_ty<'a>(tys: impl IntoIterator<Item = &'a Type> + 'a) -> Option<&'a Type> {
395    tys.into_iter()
396        .map(Option::Some)
397        .reduce(|ty1, ty2| convert_ty(ty1?, ty2?))
398        .flatten()
399}