naga/front/wgsl/lower/
conversion.rs

1//! WGSL's automatic conversions for abstract types.
2
3use crate::{Handle, Span};
4
5impl<'source, 'temp, 'out> super::ExpressionContext<'source, 'temp, 'out> {
6    /// Try to use WGSL's automatic conversions to convert `expr` to `goal_ty`.
7    ///
8    /// If no conversions are necessary, return `expr` unchanged.
9    ///
10    /// If automatic conversions cannot convert `expr` to `goal_ty`, return an
11    /// [`AutoConversion`] error.
12    ///
13    /// Although the Load Rule is one of the automatic conversions, this
14    /// function assumes it has already been applied if appropriate, as
15    /// indicated by the fact that the Rust type of `expr` is not `Typed<_>`.
16    ///
17    /// [`AutoConversion`]: super::Error::AutoConversion
18    pub fn try_automatic_conversions(
19        &mut self,
20        expr: Handle<crate::Expression>,
21        goal_ty: &crate::proc::TypeResolution,
22        goal_span: Span,
23    ) -> Result<Handle<crate::Expression>, super::Error<'source>> {
24        let expr_span = self.get_expression_span(expr);
25        // Keep the TypeResolution so we can get type names for
26        // structs in error messages.
27        let expr_resolution = super::resolve!(self, expr);
28        let types = &self.module.types;
29        let expr_inner = expr_resolution.inner_with(types);
30        let goal_inner = goal_ty.inner_with(types);
31
32        // If `expr` already has the requested type, we're done.
33        if expr_inner.equivalent(goal_inner, types) {
34            return Ok(expr);
35        }
36
37        let (_expr_scalar, goal_scalar) =
38            match expr_inner.automatically_converts_to(goal_inner, types) {
39                Some(scalars) => scalars,
40                None => {
41                    let gctx = &self.module.to_ctx();
42                    let source_type = expr_resolution.to_wgsl(gctx);
43                    let dest_type = goal_ty.to_wgsl(gctx);
44
45                    return Err(super::Error::AutoConversion {
46                        dest_span: goal_span,
47                        dest_type,
48                        source_span: expr_span,
49                        source_type,
50                    });
51                }
52            };
53
54        self.convert_leaf_scalar(expr, expr_span, goal_scalar)
55    }
56
57    /// Try to convert `expr`'s leaf scalar to `goal` using automatic conversions.
58    ///
59    /// If no conversions are necessary, return `expr` unchanged.
60    ///
61    /// If automatic conversions cannot convert `expr` to `goal_scalar`, return
62    /// an [`AutoConversionLeafScalar`] error.
63    ///
64    /// Although the Load Rule is one of the automatic conversions, this
65    /// function assumes it has already been applied if appropriate, as
66    /// indicated by the fact that the Rust type of `expr` is not `Typed<_>`.
67    ///
68    /// [`AutoConversionLeafScalar`]: super::Error::AutoConversionLeafScalar
69    pub fn try_automatic_conversion_for_leaf_scalar(
70        &mut self,
71        expr: Handle<crate::Expression>,
72        goal_scalar: crate::Scalar,
73        goal_span: Span,
74    ) -> Result<Handle<crate::Expression>, super::Error<'source>> {
75        let expr_span = self.get_expression_span(expr);
76        let expr_resolution = super::resolve!(self, expr);
77        let types = &self.module.types;
78        let expr_inner = expr_resolution.inner_with(types);
79
80        let make_error = || {
81            let gctx = &self.module.to_ctx();
82            let source_type = expr_resolution.to_wgsl(gctx);
83            super::Error::AutoConversionLeafScalar {
84                dest_span: goal_span,
85                dest_scalar: goal_scalar.to_wgsl(),
86                source_span: expr_span,
87                source_type,
88            }
89        };
90
91        let expr_scalar = match expr_inner.scalar() {
92            Some(scalar) => scalar,
93            None => return Err(make_error()),
94        };
95
96        if expr_scalar == goal_scalar {
97            return Ok(expr);
98        }
99
100        if !expr_scalar.automatically_converts_to(goal_scalar) {
101            return Err(make_error());
102        }
103
104        assert!(expr_scalar.is_abstract());
105
106        self.convert_leaf_scalar(expr, expr_span, goal_scalar)
107    }
108
109    fn convert_leaf_scalar(
110        &mut self,
111        expr: Handle<crate::Expression>,
112        expr_span: Span,
113        goal_scalar: crate::Scalar,
114    ) -> Result<Handle<crate::Expression>, super::Error<'source>> {
115        let expr_inner = super::resolve_inner!(self, expr);
116        if let crate::TypeInner::Array { .. } = *expr_inner {
117            self.as_const_evaluator()
118                .cast_array(expr, goal_scalar, expr_span)
119                .map_err(|err| super::Error::ConstantEvaluatorError(err, expr_span))
120        } else {
121            let cast = crate::Expression::As {
122                expr,
123                kind: goal_scalar.kind,
124                convert: Some(goal_scalar.width),
125            };
126            self.append_expression(cast, expr_span)
127        }
128    }
129
130    /// Try to convert `exprs` to `goal_ty` using WGSL's automatic conversions.
131    pub fn try_automatic_conversions_slice(
132        &mut self,
133        exprs: &mut [Handle<crate::Expression>],
134        goal_ty: &crate::proc::TypeResolution,
135        goal_span: Span,
136    ) -> Result<(), super::Error<'source>> {
137        for expr in exprs.iter_mut() {
138            *expr = self.try_automatic_conversions(*expr, goal_ty, goal_span)?;
139        }
140
141        Ok(())
142    }
143
144    /// Apply WGSL's automatic conversions to a vector constructor's arguments.
145    ///
146    /// When calling a vector constructor like `vec3<f32>(...)`, the parameters
147    /// can be a mix of scalars and vectors, with the latter being spread out to
148    /// contribute each of their components as a component of the new value.
149    /// When the element type is explicit, as with `<f32>` in the example above,
150    /// WGSL's automatic conversions should convert abstract scalar and vector
151    /// parameters to the constructor's required scalar type.
152    pub fn try_automatic_conversions_for_vector(
153        &mut self,
154        exprs: &mut [Handle<crate::Expression>],
155        goal_scalar: crate::Scalar,
156        goal_span: Span,
157    ) -> Result<(), super::Error<'source>> {
158        use crate::proc::TypeResolution as Tr;
159        use crate::TypeInner as Ti;
160        let goal_scalar_res = Tr::Value(Ti::Scalar(goal_scalar));
161
162        for (i, expr) in exprs.iter_mut().enumerate() {
163            // Keep the TypeResolution so we can get full type names
164            // in error messages.
165            let expr_resolution = super::resolve!(self, *expr);
166            let types = &self.module.types;
167            let expr_inner = expr_resolution.inner_with(types);
168
169            match *expr_inner {
170                Ti::Scalar(_) => {
171                    *expr = self.try_automatic_conversions(*expr, &goal_scalar_res, goal_span)?;
172                }
173                Ti::Vector { size, scalar: _ } => {
174                    let goal_vector_res = Tr::Value(Ti::Vector {
175                        size,
176                        scalar: goal_scalar,
177                    });
178                    *expr = self.try_automatic_conversions(*expr, &goal_vector_res, goal_span)?;
179                }
180                _ => {
181                    let span = self.get_expression_span(*expr);
182                    return Err(super::Error::InvalidConstructorComponentType(
183                        span, i as i32,
184                    ));
185                }
186            }
187        }
188
189        Ok(())
190    }
191
192    /// Convert `expr` to the leaf scalar type `scalar`.
193    pub fn convert_to_leaf_scalar(
194        &mut self,
195        expr: &mut Handle<crate::Expression>,
196        goal: crate::Scalar,
197    ) -> Result<(), super::Error<'source>> {
198        let inner = super::resolve_inner!(self, *expr);
199        // Do nothing if `inner` doesn't even have leaf scalars;
200        // it's a type error that validation will catch.
201        if inner.scalar() != Some(goal) {
202            let cast = crate::Expression::As {
203                expr: *expr,
204                kind: goal.kind,
205                convert: Some(goal.width),
206            };
207            let expr_span = self.get_expression_span(*expr);
208            *expr = self.append_expression(cast, expr_span)?;
209        }
210
211        Ok(())
212    }
213
214    /// Convert all expressions in `exprs` to a common scalar type.
215    ///
216    /// Note that the caller is responsible for making sure these
217    /// conversions are actually justified. This function simply
218    /// generates `As` expressions, regardless of whether they are
219    /// permitted WGSL automatic conversions. Callers intending to
220    /// implement automatic conversions need to determine for
221    /// themselves whether the casts we we generate are justified,
222    /// perhaps by calling `TypeInner::automatically_converts_to` or
223    /// `Scalar::automatic_conversion_combine`.
224    pub fn convert_slice_to_common_leaf_scalar(
225        &mut self,
226        exprs: &mut [Handle<crate::Expression>],
227        goal: crate::Scalar,
228    ) -> Result<(), super::Error<'source>> {
229        for expr in exprs.iter_mut() {
230            self.convert_to_leaf_scalar(expr, goal)?;
231        }
232
233        Ok(())
234    }
235
236    /// Return an expression for the concretized value of `expr`.
237    ///
238    /// If `expr` is already concrete, return it unchanged.
239    pub fn concretize(
240        &mut self,
241        mut expr: Handle<crate::Expression>,
242    ) -> Result<Handle<crate::Expression>, super::Error<'source>> {
243        let inner = super::resolve_inner!(self, expr);
244        if let Some(scalar) = inner.automatically_convertible_scalar(&self.module.types) {
245            let concretized = scalar.concretize();
246            if concretized != scalar {
247                assert!(scalar.is_abstract());
248                let expr_span = self.get_expression_span(expr);
249                expr = self
250                    .as_const_evaluator()
251                    .cast_array(expr, concretized, expr_span)
252                    .map_err(|err| {
253                        // A `TypeResolution` includes the type's full name, if
254                        // it has one. Also, avoid holding the borrow of `inner`
255                        // across the call to `cast_array`.
256                        let expr_type = &self.typifier()[expr];
257                        super::Error::ConcretizationFailed {
258                            expr_span,
259                            expr_type: expr_type.to_wgsl(&self.module.to_ctx()),
260                            scalar: concretized.to_wgsl(),
261                            inner: err,
262                        }
263                    })?;
264            }
265        }
266
267        Ok(expr)
268    }
269
270    /// Find the consensus scalar of `components` under WGSL's automatic
271    /// conversions.
272    ///
273    /// If `components` can all be converted to any common scalar via
274    /// WGSL's automatic conversions, return the best such scalar.
275    ///
276    /// The `components` slice must not be empty. All elements' types must
277    /// have been resolved.
278    ///
279    /// If `components` are definitely not acceptable as arguments to such
280    /// constructors, return `Err(i)`, where `i` is the index in
281    /// `components` of some problematic argument.
282    ///
283    /// This function doesn't fully type-check the arguments - it only
284    /// considers their leaf scalar types. This means it may return `Ok`
285    /// even when the Naga validator will reject the resulting
286    /// construction expression later.
287    pub fn automatic_conversion_consensus<'handle, I>(
288        &self,
289        components: I,
290    ) -> Result<crate::Scalar, usize>
291    where
292        I: IntoIterator<Item = &'handle Handle<crate::Expression>>,
293        I::IntoIter: Clone, // for debugging
294    {
295        let types = &self.module.types;
296        let mut inners = components
297            .into_iter()
298            .map(|&c| self.typifier()[c].inner_with(types));
299        log::debug!(
300            "wgsl automatic_conversion_consensus: {:?}",
301            inners
302                .clone()
303                .map(|inner| inner.to_wgsl(&self.module.to_ctx()))
304                .collect::<Vec<String>>()
305        );
306        let mut best = inners.next().unwrap().scalar().ok_or(0_usize)?;
307        for (inner, i) in inners.zip(1..) {
308            let scalar = inner.scalar().ok_or(i)?;
309            match best.automatic_conversion_combine(scalar) {
310                Some(new_best) => {
311                    best = new_best;
312                }
313                None => return Err(i),
314            }
315        }
316
317        log::debug!("    consensus: {:?}", best.to_wgsl());
318        Ok(best)
319    }
320}
321
322impl crate::TypeInner {
323    /// Determine whether `self` automatically converts to `goal`.
324    ///
325    /// If WGSL's automatic conversions (excluding the Load Rule) will
326    /// convert `self` to `goal`, then return a pair `(from, to)`,
327    /// where `from` and `to` are the scalar types of the leaf values
328    /// of `self` and `goal`.
329    ///
330    /// This function assumes that `self` and `goal` are different
331    /// types. Callers should first check whether any conversion is
332    /// needed at all.
333    ///
334    /// If the automatic conversions cannot convert `self` to `goal`,
335    /// return `None`.
336    fn automatically_converts_to(
337        &self,
338        goal: &Self,
339        types: &crate::UniqueArena<crate::Type>,
340    ) -> Option<(crate::Scalar, crate::Scalar)> {
341        use crate::ScalarKind as Sk;
342        use crate::TypeInner as Ti;
343
344        // Automatic conversions only change the scalar type of a value's leaves
345        // (e.g., `vec4<AbstractFloat>` to `vec4<f32>`), never the type
346        // constructors applied to those scalar types (e.g., never scalar to
347        // `vec4`, or `vec2` to `vec3`). So first we check that the type
348        // constructors match, extracting the leaf scalar types in the process.
349        let expr_scalar;
350        let goal_scalar;
351        match (self, goal) {
352            (&Ti::Scalar(expr), &Ti::Scalar(goal)) => {
353                expr_scalar = expr;
354                goal_scalar = goal;
355            }
356            (
357                &Ti::Vector {
358                    size: expr_size,
359                    scalar: expr,
360                },
361                &Ti::Vector {
362                    size: goal_size,
363                    scalar: goal,
364                },
365            ) if expr_size == goal_size => {
366                expr_scalar = expr;
367                goal_scalar = goal;
368            }
369            (
370                &Ti::Matrix {
371                    rows: expr_rows,
372                    columns: expr_columns,
373                    scalar: expr,
374                },
375                &Ti::Matrix {
376                    rows: goal_rows,
377                    columns: goal_columns,
378                    scalar: goal,
379                },
380            ) if expr_rows == goal_rows && expr_columns == goal_columns => {
381                expr_scalar = expr;
382                goal_scalar = goal;
383            }
384            (
385                &Ti::Array {
386                    base: expr_base,
387                    size: expr_size,
388                    stride: _,
389                },
390                &Ti::Array {
391                    base: goal_base,
392                    size: goal_size,
393                    stride: _,
394                },
395            ) if expr_size == goal_size => {
396                return types[expr_base]
397                    .inner
398                    .automatically_converts_to(&types[goal_base].inner, types);
399            }
400            _ => return None,
401        }
402
403        match (expr_scalar.kind, goal_scalar.kind) {
404            (Sk::AbstractFloat, Sk::Float) => {}
405            (Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}
406            _ => return None,
407        }
408
409        log::trace!("      okay: expr {expr_scalar:?}, goal {goal_scalar:?}");
410        Some((expr_scalar, goal_scalar))
411    }
412
413    fn automatically_convertible_scalar(
414        &self,
415        types: &crate::UniqueArena<crate::Type>,
416    ) -> Option<crate::Scalar> {
417        use crate::TypeInner as Ti;
418        match *self {
419            Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
420                Some(scalar)
421            }
422            Ti::Array { base, .. } => types[base].inner.automatically_convertible_scalar(types),
423            Ti::Atomic(_)
424            | Ti::Pointer { .. }
425            | Ti::ValuePointer { .. }
426            | Ti::Struct { .. }
427            | Ti::Image { .. }
428            | Ti::Sampler { .. }
429            | Ti::AccelerationStructure
430            | Ti::RayQuery
431            | Ti::BindingArray { .. } => None,
432        }
433    }
434}
435
436impl crate::Scalar {
437    /// Find the common type of `self` and `other` under WGSL's
438    /// automatic conversions.
439    ///
440    /// If there are any scalars to which WGSL's automatic conversions
441    /// will convert both `self` and `other`, return the best such
442    /// scalar. Otherwise, return `None`.
443    pub const fn automatic_conversion_combine(self, other: Self) -> Option<crate::Scalar> {
444        use crate::ScalarKind as Sk;
445
446        match (self.kind, other.kind) {
447            // When the kinds match...
448            (Sk::AbstractFloat, Sk::AbstractFloat)
449            | (Sk::AbstractInt, Sk::AbstractInt)
450            | (Sk::Sint, Sk::Sint)
451            | (Sk::Uint, Sk::Uint)
452            | (Sk::Float, Sk::Float)
453            | (Sk::Bool, Sk::Bool) => {
454                if self.width == other.width {
455                    // ... either no conversion is necessary ...
456                    Some(self)
457                } else {
458                    // ... or no conversion is possible.
459                    // We never convert concrete to concrete, and
460                    // abstract types should have only one size.
461                    None
462                }
463            }
464
465            // AbstractInt converts to AbstractFloat.
466            (Sk::AbstractFloat, Sk::AbstractInt) => Some(self),
467            (Sk::AbstractInt, Sk::AbstractFloat) => Some(other),
468
469            // AbstractFloat converts to Float.
470            (Sk::AbstractFloat, Sk::Float) => Some(other),
471            (Sk::Float, Sk::AbstractFloat) => Some(self),
472
473            // AbstractInt converts to concrete integer or float.
474            (Sk::AbstractInt, Sk::Uint | Sk::Sint | Sk::Float) => Some(other),
475            (Sk::Uint | Sk::Sint | Sk::Float, Sk::AbstractInt) => Some(self),
476
477            // AbstractFloat can't be reconciled with concrete integer types.
478            (Sk::AbstractFloat, Sk::Uint | Sk::Sint) | (Sk::Uint | Sk::Sint, Sk::AbstractFloat) => {
479                None
480            }
481
482            // Nothing can be reconciled with `bool`.
483            (Sk::Bool, _) | (_, Sk::Bool) => None,
484
485            // Different concrete types cannot be reconciled.
486            (Sk::Sint | Sk::Uint | Sk::Float, Sk::Sint | Sk::Uint | Sk::Float) => None,
487        }
488    }
489
490    /// Return `true` if automatic conversions will covert `self` to `goal`.
491    pub fn automatically_converts_to(self, goal: Self) -> bool {
492        self.automatic_conversion_combine(goal) == Some(goal)
493    }
494
495    const fn concretize(self) -> Self {
496        use crate::ScalarKind as Sk;
497        match self.kind {
498            Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => self,
499            Sk::AbstractInt => Self::I32,
500            Sk::AbstractFloat => Self::F32,
501        }
502    }
503}