naga/proc/
typifier.rs

1use crate::arena::{Arena, Handle, UniqueArena};
2
3use thiserror::Error;
4
5/// The result of computing an expression's type.
6///
7/// This is the (Rust) type returned by [`ResolveContext::resolve`] to represent
8/// the (Naga) type it ascribes to some expression.
9///
10/// You might expect such a function to simply return a `Handle<Type>`. However,
11/// we want type resolution to be a read-only process, and that would limit the
12/// possible results to types already present in the expression's associated
13/// `UniqueArena<Type>`. Naga IR does have certain expressions whose types are
14/// not certain to be present.
15///
16/// So instead, type resolution returns a `TypeResolution` enum: either a
17/// [`Handle`], referencing some type in the arena, or a [`Value`], holding a
18/// free-floating [`TypeInner`]. This extends the range to cover anything that
19/// can be represented with a `TypeInner` referring to the existing arena.
20///
21/// What sorts of expressions can have types not available in the arena?
22///
23/// -   An [`Access`] or [`AccessIndex`] expression applied to a [`Vector`] or
24///     [`Matrix`] must have a [`Scalar`] or [`Vector`] type. But since `Vector`
25///     and `Matrix` represent their element and column types implicitly, not
26///     via a handle, there may not be a suitable type in the expression's
27///     associated arena. Instead, resolving such an expression returns a
28///     `TypeResolution::Value(TypeInner::X { ... })`, where `X` is `Scalar` or
29///     `Vector`.
30///
31/// -   Similarly, the type of an [`Access`] or [`AccessIndex`] expression
32///     applied to a *pointer to* a vector or matrix must produce a *pointer to*
33///     a scalar or vector type. These cannot be represented with a
34///     [`TypeInner::Pointer`], since the `Pointer`'s `base` must point into the
35///     arena, and as before, we cannot assume that a suitable scalar or vector
36///     type is there. So we take things one step further and provide
37///     [`TypeInner::ValuePointer`], specifically for the case of pointers to
38///     scalars or vectors. This type fits in a `TypeInner` and is exactly
39///     equivalent to a `Pointer` to a `Vector` or `Scalar`.
40///
41/// So, for example, the type of an `Access` expression applied to a value of type:
42///
43/// ```ignore
44/// TypeInner::Matrix { columns, rows, width }
45/// ```
46///
47/// might be:
48///
49/// ```ignore
50/// TypeResolution::Value(TypeInner::Vector {
51///     size: rows,
52///     kind: ScalarKind::Float,
53///     width,
54/// })
55/// ```
56///
57/// and the type of an access to a pointer of address space `space` to such a
58/// matrix might be:
59///
60/// ```ignore
61/// TypeResolution::Value(TypeInner::ValuePointer {
62///     size: Some(rows),
63///     kind: ScalarKind::Float,
64///     width,
65///     space,
66/// })
67/// ```
68///
69/// [`Handle`]: TypeResolution::Handle
70/// [`Value`]: TypeResolution::Value
71///
72/// [`Access`]: crate::Expression::Access
73/// [`AccessIndex`]: crate::Expression::AccessIndex
74///
75/// [`TypeInner`]: crate::TypeInner
76/// [`Matrix`]: crate::TypeInner::Matrix
77/// [`Pointer`]: crate::TypeInner::Pointer
78/// [`Scalar`]: crate::TypeInner::Scalar
79/// [`ValuePointer`]: crate::TypeInner::ValuePointer
80/// [`Vector`]: crate::TypeInner::Vector
81///
82/// [`TypeInner::Pointer`]: crate::TypeInner::Pointer
83/// [`TypeInner::ValuePointer`]: crate::TypeInner::ValuePointer
84#[derive(Debug, PartialEq)]
85#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
86#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
87pub enum TypeResolution {
88    /// A type stored in the associated arena.
89    Handle(Handle<crate::Type>),
90
91    /// A free-floating [`TypeInner`], representing a type that may not be
92    /// available in the associated arena. However, the `TypeInner` itself may
93    /// contain `Handle<Type>` values referring to types from the arena.
94    ///
95    /// [`TypeInner`]: crate::TypeInner
96    Value(crate::TypeInner),
97}
98
99impl TypeResolution {
100    pub const fn handle(&self) -> Option<Handle<crate::Type>> {
101        match *self {
102            Self::Handle(handle) => Some(handle),
103            Self::Value(_) => None,
104        }
105    }
106
107    pub fn inner_with<'a>(&'a self, arena: &'a UniqueArena<crate::Type>) -> &'a crate::TypeInner {
108        match *self {
109            Self::Handle(handle) => &arena[handle].inner,
110            Self::Value(ref inner) => inner,
111        }
112    }
113}
114
115// Clone is only implemented for numeric variants of `TypeInner`.
116impl Clone for TypeResolution {
117    fn clone(&self) -> Self {
118        use crate::TypeInner as Ti;
119        match *self {
120            Self::Handle(handle) => Self::Handle(handle),
121            Self::Value(ref v) => Self::Value(match *v {
122                Ti::Scalar { kind, width } => Ti::Scalar { kind, width },
123                Ti::Vector { size, kind, width } => Ti::Vector { size, kind, width },
124                Ti::Matrix {
125                    rows,
126                    columns,
127                    width,
128                } => Ti::Matrix {
129                    rows,
130                    columns,
131                    width,
132                },
133                Ti::Pointer { base, space } => Ti::Pointer { base, space },
134                Ti::ValuePointer {
135                    size,
136                    kind,
137                    width,
138                    space,
139                } => Ti::ValuePointer {
140                    size,
141                    kind,
142                    width,
143                    space,
144                },
145                _ => unreachable!("Unexpected clone type: {:?}", v),
146            }),
147        }
148    }
149}
150
151#[derive(Clone, Debug, Error, PartialEq)]
152pub enum ResolveError {
153    #[error("Index {index} is out of bounds for expression {expr:?}")]
154    OutOfBoundsIndex {
155        expr: Handle<crate::Expression>,
156        index: u32,
157    },
158    #[error("Invalid access into expression {expr:?}, indexed: {indexed}")]
159    InvalidAccess {
160        expr: Handle<crate::Expression>,
161        indexed: bool,
162    },
163    #[error("Invalid sub-access into type {ty:?}, indexed: {indexed}")]
164    InvalidSubAccess {
165        ty: Handle<crate::Type>,
166        indexed: bool,
167    },
168    #[error("Invalid scalar {0:?}")]
169    InvalidScalar(Handle<crate::Expression>),
170    #[error("Invalid vector {0:?}")]
171    InvalidVector(Handle<crate::Expression>),
172    #[error("Invalid pointer {0:?}")]
173    InvalidPointer(Handle<crate::Expression>),
174    #[error("Invalid image {0:?}")]
175    InvalidImage(Handle<crate::Expression>),
176    #[error("Function {name} not defined")]
177    FunctionNotDefined { name: String },
178    #[error("Function without return type")]
179    FunctionReturnsVoid,
180    #[error("Incompatible operands: {0}")]
181    IncompatibleOperands(String),
182    #[error("Function argument {0} doesn't exist")]
183    FunctionArgumentNotFound(u32),
184    #[error("Special type is not registered within the module")]
185    MissingSpecialType,
186}
187
188pub struct ResolveContext<'a> {
189    pub constants: &'a Arena<crate::Constant>,
190    pub types: &'a UniqueArena<crate::Type>,
191    pub special_types: &'a crate::SpecialTypes,
192    pub global_vars: &'a Arena<crate::GlobalVariable>,
193    pub local_vars: &'a Arena<crate::LocalVariable>,
194    pub functions: &'a Arena<crate::Function>,
195    pub arguments: &'a [crate::FunctionArgument],
196}
197
198impl<'a> ResolveContext<'a> {
199    /// Initialize a resolve context from the module.
200    pub const fn with_locals(
201        module: &'a crate::Module,
202        local_vars: &'a Arena<crate::LocalVariable>,
203        arguments: &'a [crate::FunctionArgument],
204    ) -> Self {
205        Self {
206            constants: &module.constants,
207            types: &module.types,
208            special_types: &module.special_types,
209            global_vars: &module.global_variables,
210            local_vars,
211            functions: &module.functions,
212            arguments,
213        }
214    }
215
216    /// Determine the type of `expr`.
217    ///
218    /// The `past` argument must be a closure that can resolve the types of any
219    /// expressions that `expr` refers to. These can be gathered by caching the
220    /// results of prior calls to `resolve`, perhaps as done by the
221    /// [`front::Typifier`] utility type.
222    ///
223    /// Type resolution is a read-only process: this method takes `self` by
224    /// shared reference. However, this means that we cannot add anything to
225    /// `self.types` that we might need to describe `expr`. To work around this,
226    /// this method returns a [`TypeResolution`], rather than simply returning a
227    /// `Handle<Type>`; see the documentation for [`TypeResolution`] for
228    /// details.
229    ///
230    /// [`front::Typifier`]: crate::front::Typifier
231    pub fn resolve(
232        &self,
233        expr: &crate::Expression,
234        past: impl Fn(Handle<crate::Expression>) -> Result<&'a TypeResolution, ResolveError>,
235    ) -> Result<TypeResolution, ResolveError> {
236        use crate::TypeInner as Ti;
237        let types = self.types;
238        Ok(match *expr {
239            crate::Expression::Access { base, .. } => match *past(base)?.inner_with(types) {
240                // Arrays and matrices can only be indexed dynamically behind a
241                // pointer, but that's a validation error, not a type error, so
242                // go ahead provide a type here.
243                Ti::Array { base, .. } => TypeResolution::Handle(base),
244                Ti::Matrix { rows, width, .. } => TypeResolution::Value(Ti::Vector {
245                    size: rows,
246                    kind: crate::ScalarKind::Float,
247                    width,
248                }),
249                Ti::Vector {
250                    size: _,
251                    kind,
252                    width,
253                } => TypeResolution::Value(Ti::Scalar { kind, width }),
254                Ti::ValuePointer {
255                    size: Some(_),
256                    kind,
257                    width,
258                    space,
259                } => TypeResolution::Value(Ti::ValuePointer {
260                    size: None,
261                    kind,
262                    width,
263                    space,
264                }),
265                Ti::Pointer { base, space } => {
266                    TypeResolution::Value(match types[base].inner {
267                        Ti::Array { base, .. } => Ti::Pointer { base, space },
268                        Ti::Vector {
269                            size: _,
270                            kind,
271                            width,
272                        } => Ti::ValuePointer {
273                            size: None,
274                            kind,
275                            width,
276                            space,
277                        },
278                        // Matrices are only dynamically indexed behind a pointer
279                        Ti::Matrix {
280                            columns: _,
281                            rows,
282                            width,
283                        } => Ti::ValuePointer {
284                            kind: crate::ScalarKind::Float,
285                            size: Some(rows),
286                            width,
287                            space,
288                        },
289                        Ti::BindingArray { base, .. } => Ti::Pointer { base, space },
290                        ref other => {
291                            log::error!("Access sub-type {:?}", other);
292                            return Err(ResolveError::InvalidSubAccess {
293                                ty: base,
294                                indexed: false,
295                            });
296                        }
297                    })
298                }
299                Ti::BindingArray { base, .. } => TypeResolution::Handle(base),
300                ref other => {
301                    log::error!("Access type {:?}", other);
302                    return Err(ResolveError::InvalidAccess {
303                        expr: base,
304                        indexed: false,
305                    });
306                }
307            },
308            crate::Expression::AccessIndex { base, index } => {
309                match *past(base)?.inner_with(types) {
310                    Ti::Vector { size, kind, width } => {
311                        if index >= size as u32 {
312                            return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
313                        }
314                        TypeResolution::Value(Ti::Scalar { kind, width })
315                    }
316                    Ti::Matrix {
317                        columns,
318                        rows,
319                        width,
320                    } => {
321                        if index >= columns as u32 {
322                            return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
323                        }
324                        TypeResolution::Value(crate::TypeInner::Vector {
325                            size: rows,
326                            kind: crate::ScalarKind::Float,
327                            width,
328                        })
329                    }
330                    Ti::Array { base, .. } => TypeResolution::Handle(base),
331                    Ti::Struct { ref members, .. } => {
332                        let member = members
333                            .get(index as usize)
334                            .ok_or(ResolveError::OutOfBoundsIndex { expr: base, index })?;
335                        TypeResolution::Handle(member.ty)
336                    }
337                    Ti::ValuePointer {
338                        size: Some(size),
339                        kind,
340                        width,
341                        space,
342                    } => {
343                        if index >= size as u32 {
344                            return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
345                        }
346                        TypeResolution::Value(Ti::ValuePointer {
347                            size: None,
348                            kind,
349                            width,
350                            space,
351                        })
352                    }
353                    Ti::Pointer {
354                        base: ty_base,
355                        space,
356                    } => TypeResolution::Value(match types[ty_base].inner {
357                        Ti::Array { base, .. } => Ti::Pointer { base, space },
358                        Ti::Vector { size, kind, width } => {
359                            if index >= size as u32 {
360                                return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
361                            }
362                            Ti::ValuePointer {
363                                size: None,
364                                kind,
365                                width,
366                                space,
367                            }
368                        }
369                        Ti::Matrix {
370                            rows,
371                            columns,
372                            width,
373                        } => {
374                            if index >= columns as u32 {
375                                return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
376                            }
377                            Ti::ValuePointer {
378                                size: Some(rows),
379                                kind: crate::ScalarKind::Float,
380                                width,
381                                space,
382                            }
383                        }
384                        Ti::Struct { ref members, .. } => {
385                            let member = members
386                                .get(index as usize)
387                                .ok_or(ResolveError::OutOfBoundsIndex { expr: base, index })?;
388                            Ti::Pointer {
389                                base: member.ty,
390                                space,
391                            }
392                        }
393                        Ti::BindingArray { base, .. } => Ti::Pointer { base, space },
394                        ref other => {
395                            log::error!("Access index sub-type {:?}", other);
396                            return Err(ResolveError::InvalidSubAccess {
397                                ty: ty_base,
398                                indexed: true,
399                            });
400                        }
401                    }),
402                    Ti::BindingArray { base, .. } => TypeResolution::Handle(base),
403                    ref other => {
404                        log::error!("Access index type {:?}", other);
405                        return Err(ResolveError::InvalidAccess {
406                            expr: base,
407                            indexed: true,
408                        });
409                    }
410                }
411            }
412            crate::Expression::Splat { size, value } => match *past(value)?.inner_with(types) {
413                Ti::Scalar { kind, width } => {
414                    TypeResolution::Value(Ti::Vector { size, kind, width })
415                }
416                ref other => {
417                    log::error!("Scalar type {:?}", other);
418                    return Err(ResolveError::InvalidScalar(value));
419                }
420            },
421            crate::Expression::Swizzle {
422                size,
423                vector,
424                pattern: _,
425            } => match *past(vector)?.inner_with(types) {
426                Ti::Vector {
427                    size: _,
428                    kind,
429                    width,
430                } => TypeResolution::Value(Ti::Vector { size, kind, width }),
431                ref other => {
432                    log::error!("Vector type {:?}", other);
433                    return Err(ResolveError::InvalidVector(vector));
434                }
435            },
436            crate::Expression::Literal(lit) => TypeResolution::Value(lit.ty_inner()),
437            crate::Expression::Constant(h) => TypeResolution::Handle(self.constants[h].ty),
438            crate::Expression::ZeroValue(ty) => TypeResolution::Handle(ty),
439            crate::Expression::Compose { ty, .. } => TypeResolution::Handle(ty),
440            crate::Expression::FunctionArgument(index) => {
441                let arg = self
442                    .arguments
443                    .get(index as usize)
444                    .ok_or(ResolveError::FunctionArgumentNotFound(index))?;
445                TypeResolution::Handle(arg.ty)
446            }
447            crate::Expression::GlobalVariable(h) => {
448                let var = &self.global_vars[h];
449                if var.space == crate::AddressSpace::Handle {
450                    TypeResolution::Handle(var.ty)
451                } else {
452                    TypeResolution::Value(Ti::Pointer {
453                        base: var.ty,
454                        space: var.space,
455                    })
456                }
457            }
458            crate::Expression::LocalVariable(h) => {
459                let var = &self.local_vars[h];
460                TypeResolution::Value(Ti::Pointer {
461                    base: var.ty,
462                    space: crate::AddressSpace::Function,
463                })
464            }
465            crate::Expression::Load { pointer } => match *past(pointer)?.inner_with(types) {
466                Ti::Pointer { base, space: _ } => {
467                    if let Ti::Atomic { kind, width } = types[base].inner {
468                        TypeResolution::Value(Ti::Scalar { kind, width })
469                    } else {
470                        TypeResolution::Handle(base)
471                    }
472                }
473                Ti::ValuePointer {
474                    size,
475                    kind,
476                    width,
477                    space: _,
478                } => TypeResolution::Value(match size {
479                    Some(size) => Ti::Vector { size, kind, width },
480                    None => Ti::Scalar { kind, width },
481                }),
482                ref other => {
483                    log::error!("Pointer type {:?}", other);
484                    return Err(ResolveError::InvalidPointer(pointer));
485                }
486            },
487            crate::Expression::ImageSample {
488                image,
489                gather: Some(_),
490                ..
491            } => match *past(image)?.inner_with(types) {
492                Ti::Image { class, .. } => TypeResolution::Value(Ti::Vector {
493                    kind: match class {
494                        crate::ImageClass::Sampled { kind, multi: _ } => kind,
495                        _ => crate::ScalarKind::Float,
496                    },
497                    width: 4,
498                    size: crate::VectorSize::Quad,
499                }),
500                ref other => {
501                    log::error!("Image type {:?}", other);
502                    return Err(ResolveError::InvalidImage(image));
503                }
504            },
505            crate::Expression::ImageSample { image, .. }
506            | crate::Expression::ImageLoad { image, .. } => match *past(image)?.inner_with(types) {
507                Ti::Image { class, .. } => TypeResolution::Value(match class {
508                    crate::ImageClass::Depth { multi: _ } => Ti::Scalar {
509                        kind: crate::ScalarKind::Float,
510                        width: 4,
511                    },
512                    crate::ImageClass::Sampled { kind, multi: _ } => Ti::Vector {
513                        kind,
514                        width: 4,
515                        size: crate::VectorSize::Quad,
516                    },
517                    crate::ImageClass::Storage { format, .. } => Ti::Vector {
518                        kind: format.into(),
519                        width: 4,
520                        size: crate::VectorSize::Quad,
521                    },
522                }),
523                ref other => {
524                    log::error!("Image type {:?}", other);
525                    return Err(ResolveError::InvalidImage(image));
526                }
527            },
528            crate::Expression::ImageQuery { image, query } => TypeResolution::Value(match query {
529                crate::ImageQuery::Size { level: _ } => match *past(image)?.inner_with(types) {
530                    Ti::Image { dim, .. } => match dim {
531                        crate::ImageDimension::D1 => Ti::Scalar {
532                            kind: crate::ScalarKind::Uint,
533                            width: 4,
534                        },
535                        crate::ImageDimension::D2 | crate::ImageDimension::Cube => Ti::Vector {
536                            size: crate::VectorSize::Bi,
537                            kind: crate::ScalarKind::Uint,
538                            width: 4,
539                        },
540                        crate::ImageDimension::D3 => Ti::Vector {
541                            size: crate::VectorSize::Tri,
542                            kind: crate::ScalarKind::Uint,
543                            width: 4,
544                        },
545                    },
546                    ref other => {
547                        log::error!("Image type {:?}", other);
548                        return Err(ResolveError::InvalidImage(image));
549                    }
550                },
551                crate::ImageQuery::NumLevels
552                | crate::ImageQuery::NumLayers
553                | crate::ImageQuery::NumSamples => Ti::Scalar {
554                    kind: crate::ScalarKind::Uint,
555                    width: 4,
556                },
557            }),
558            crate::Expression::Unary { expr, .. } => past(expr)?.clone(),
559            crate::Expression::Binary { op, left, right } => match op {
560                crate::BinaryOperator::Add
561                | crate::BinaryOperator::Subtract
562                | crate::BinaryOperator::Divide
563                | crate::BinaryOperator::Modulo => past(left)?.clone(),
564                crate::BinaryOperator::Multiply => {
565                    let (res_left, res_right) = (past(left)?, past(right)?);
566                    match (res_left.inner_with(types), res_right.inner_with(types)) {
567                        (
568                            &Ti::Matrix {
569                                columns: _,
570                                rows,
571                                width,
572                            },
573                            &Ti::Matrix { columns, .. },
574                        ) => TypeResolution::Value(Ti::Matrix {
575                            columns,
576                            rows,
577                            width,
578                        }),
579                        (
580                            &Ti::Matrix {
581                                columns: _,
582                                rows,
583                                width,
584                            },
585                            &Ti::Vector { .. },
586                        ) => TypeResolution::Value(Ti::Vector {
587                            size: rows,
588                            kind: crate::ScalarKind::Float,
589                            width,
590                        }),
591                        (
592                            &Ti::Vector { .. },
593                            &Ti::Matrix {
594                                columns,
595                                rows: _,
596                                width,
597                            },
598                        ) => TypeResolution::Value(Ti::Vector {
599                            size: columns,
600                            kind: crate::ScalarKind::Float,
601                            width,
602                        }),
603                        (&Ti::Scalar { .. }, _) => res_right.clone(),
604                        (_, &Ti::Scalar { .. }) => res_left.clone(),
605                        (&Ti::Vector { .. }, &Ti::Vector { .. }) => res_left.clone(),
606                        (tl, tr) => {
607                            return Err(ResolveError::IncompatibleOperands(format!(
608                                "{tl:?} * {tr:?}"
609                            )))
610                        }
611                    }
612                }
613                crate::BinaryOperator::Equal
614                | crate::BinaryOperator::NotEqual
615                | crate::BinaryOperator::Less
616                | crate::BinaryOperator::LessEqual
617                | crate::BinaryOperator::Greater
618                | crate::BinaryOperator::GreaterEqual
619                | crate::BinaryOperator::LogicalAnd
620                | crate::BinaryOperator::LogicalOr => {
621                    let kind = crate::ScalarKind::Bool;
622                    let width = crate::BOOL_WIDTH;
623                    let inner = match *past(left)?.inner_with(types) {
624                        Ti::Scalar { .. } => Ti::Scalar { kind, width },
625                        Ti::Vector { size, .. } => Ti::Vector { size, kind, width },
626                        ref other => {
627                            return Err(ResolveError::IncompatibleOperands(format!(
628                                "{op:?}({other:?}, _)"
629                            )))
630                        }
631                    };
632                    TypeResolution::Value(inner)
633                }
634                crate::BinaryOperator::And
635                | crate::BinaryOperator::ExclusiveOr
636                | crate::BinaryOperator::InclusiveOr
637                | crate::BinaryOperator::ShiftLeft
638                | crate::BinaryOperator::ShiftRight => past(left)?.clone(),
639            },
640            crate::Expression::AtomicResult { ty, .. } => TypeResolution::Handle(ty),
641            crate::Expression::WorkGroupUniformLoadResult { ty } => TypeResolution::Handle(ty),
642            crate::Expression::Select { accept, .. } => past(accept)?.clone(),
643            crate::Expression::Derivative { expr, .. } => past(expr)?.clone(),
644            crate::Expression::Relational { fun, argument } => match fun {
645                crate::RelationalFunction::All | crate::RelationalFunction::Any => {
646                    TypeResolution::Value(Ti::Scalar {
647                        kind: crate::ScalarKind::Bool,
648                        width: crate::BOOL_WIDTH,
649                    })
650                }
651                crate::RelationalFunction::IsNan
652                | crate::RelationalFunction::IsInf
653                | crate::RelationalFunction::IsFinite
654                | crate::RelationalFunction::IsNormal => match *past(argument)?.inner_with(types) {
655                    Ti::Scalar { .. } => TypeResolution::Value(Ti::Scalar {
656                        kind: crate::ScalarKind::Bool,
657                        width: crate::BOOL_WIDTH,
658                    }),
659                    Ti::Vector { size, .. } => TypeResolution::Value(Ti::Vector {
660                        kind: crate::ScalarKind::Bool,
661                        width: crate::BOOL_WIDTH,
662                        size,
663                    }),
664                    ref other => {
665                        return Err(ResolveError::IncompatibleOperands(format!(
666                            "{fun:?}({other:?})"
667                        )))
668                    }
669                },
670            },
671            crate::Expression::Math {
672                fun,
673                arg,
674                arg1,
675                arg2: _,
676                arg3: _,
677            } => {
678                use crate::MathFunction as Mf;
679                let res_arg = past(arg)?;
680                match fun {
681                    // comparison
682                    Mf::Abs |
683                    Mf::Min |
684                    Mf::Max |
685                    Mf::Clamp |
686                    Mf::Saturate |
687                    // trigonometry
688                    Mf::Cos |
689                    Mf::Cosh |
690                    Mf::Sin |
691                    Mf::Sinh |
692                    Mf::Tan |
693                    Mf::Tanh |
694                    Mf::Acos |
695                    Mf::Asin |
696                    Mf::Atan |
697                    Mf::Atan2 |
698                    Mf::Asinh |
699                    Mf::Acosh |
700                    Mf::Atanh |
701                    Mf::Radians |
702                    Mf::Degrees |
703                    // decomposition
704                    Mf::Ceil |
705                    Mf::Floor |
706                    Mf::Round |
707                    Mf::Fract |
708                    Mf::Trunc |
709                    Mf::Modf |
710                    Mf::Frexp |
711                    Mf::Ldexp |
712                    // exponent
713                    Mf::Exp |
714                    Mf::Exp2 |
715                    Mf::Log |
716                    Mf::Log2 |
717                    Mf::Pow => res_arg.clone(),
718                    // geometry
719                    Mf::Dot => match *res_arg.inner_with(types) {
720                        Ti::Vector {
721                            kind,
722                            size: _,
723                            width,
724                        } => TypeResolution::Value(Ti::Scalar { kind, width }),
725                        ref other =>
726                            return Err(ResolveError::IncompatibleOperands(
727                                format!("{fun:?}({other:?}, _)")
728                            )),
729                    },
730                    Mf::Outer => {
731                        let arg1 = arg1.ok_or_else(|| ResolveError::IncompatibleOperands(
732                            format!("{fun:?}(_, None)")
733                        ))?;
734                        match (res_arg.inner_with(types), past(arg1)?.inner_with(types)) {
735                            (&Ti::Vector {kind: _, size: columns,width}, &Ti::Vector{ size: rows, .. }) => TypeResolution::Value(Ti::Matrix { columns, rows, width }),
736                            (left, right) =>
737                                return Err(ResolveError::IncompatibleOperands(
738                                    format!("{fun:?}({left:?}, {right:?})")
739                                )),
740                        }
741                    },
742                    Mf::Cross => res_arg.clone(),
743                    Mf::Distance |
744                    Mf::Length => match *res_arg.inner_with(types) {
745                        Ti::Scalar {width,kind} |
746                        Ti::Vector {width,kind,size:_} => TypeResolution::Value(Ti::Scalar { kind, width }),
747                        ref other => return Err(ResolveError::IncompatibleOperands(
748                                format!("{fun:?}({other:?})")
749                            )),
750                    },
751                    Mf::Normalize |
752                    Mf::FaceForward |
753                    Mf::Reflect |
754                    Mf::Refract => res_arg.clone(),
755                    // computational
756                    Mf::Sign |
757                    Mf::Fma |
758                    Mf::Mix |
759                    Mf::Step |
760                    Mf::SmoothStep |
761                    Mf::Sqrt |
762                    Mf::InverseSqrt => res_arg.clone(),
763                    Mf::Transpose => match *res_arg.inner_with(types) {
764                        Ti::Matrix {
765                            columns,
766                            rows,
767                            width,
768                        } => TypeResolution::Value(Ti::Matrix {
769                            columns: rows,
770                            rows: columns,
771                            width,
772                        }),
773                        ref other => return Err(ResolveError::IncompatibleOperands(
774                            format!("{fun:?}({other:?})")
775                        )),
776                    },
777                    Mf::Inverse => match *res_arg.inner_with(types) {
778                        Ti::Matrix {
779                            columns,
780                            rows,
781                            width,
782                        } if columns == rows => TypeResolution::Value(Ti::Matrix {
783                            columns,
784                            rows,
785                            width,
786                        }),
787                        ref other => return Err(ResolveError::IncompatibleOperands(
788                            format!("{fun:?}({other:?})")
789                        )),
790                    },
791                    Mf::Determinant => match *res_arg.inner_with(types) {
792                        Ti::Matrix {
793                            width,
794                            ..
795                        } => TypeResolution::Value(Ti::Scalar { kind: crate::ScalarKind::Float, width }),
796                        ref other => return Err(ResolveError::IncompatibleOperands(
797                            format!("{fun:?}({other:?})")
798                        )),
799                    },
800                    // bits
801                    Mf::CountTrailingZeros |
802                    Mf::CountLeadingZeros |
803                    Mf::CountOneBits |
804                    Mf::ReverseBits |
805                    Mf::ExtractBits |
806                    Mf::InsertBits |
807                    Mf::FindLsb |
808                    Mf::FindMsb => match *res_arg.inner_with(types)  {
809                        Ti::Scalar { kind: kind @ (crate::ScalarKind::Sint | crate::ScalarKind::Uint), width } =>
810                            TypeResolution::Value(Ti::Scalar { kind, width }),
811                        Ti::Vector { size, kind: kind @ (crate::ScalarKind::Sint | crate::ScalarKind::Uint), width } =>
812                            TypeResolution::Value(Ti::Vector { size, kind, width }),
813                        ref other => return Err(ResolveError::IncompatibleOperands(
814                                format!("{fun:?}({other:?})")
815                            )),
816                    },
817                    // data packing
818                    Mf::Pack4x8snorm |
819                    Mf::Pack4x8unorm |
820                    Mf::Pack2x16snorm |
821                    Mf::Pack2x16unorm |
822                    Mf::Pack2x16float => TypeResolution::Value(Ti::Scalar { kind: crate::ScalarKind::Uint, width: 4 }),
823                    // data unpacking
824                    Mf::Unpack4x8snorm |
825                    Mf::Unpack4x8unorm => TypeResolution::Value(Ti::Vector { size: crate::VectorSize::Quad, kind: crate::ScalarKind::Float, width: 4 }),
826                    Mf::Unpack2x16snorm |
827                    Mf::Unpack2x16unorm |
828                    Mf::Unpack2x16float => TypeResolution::Value(Ti::Vector { size: crate::VectorSize::Bi, kind: crate::ScalarKind::Float, width: 4 }),
829                }
830            }
831            crate::Expression::As {
832                expr,
833                kind,
834                convert,
835            } => match *past(expr)?.inner_with(types) {
836                Ti::Scalar { kind: _, width } => TypeResolution::Value(Ti::Scalar {
837                    kind,
838                    width: convert.unwrap_or(width),
839                }),
840                Ti::Vector {
841                    kind: _,
842                    size,
843                    width,
844                } => TypeResolution::Value(Ti::Vector {
845                    kind,
846                    size,
847                    width: convert.unwrap_or(width),
848                }),
849                Ti::Matrix {
850                    columns,
851                    rows,
852                    width,
853                } => TypeResolution::Value(Ti::Matrix {
854                    columns,
855                    rows,
856                    width: convert.unwrap_or(width),
857                }),
858                ref other => {
859                    return Err(ResolveError::IncompatibleOperands(format!(
860                        "{other:?} as {kind:?}"
861                    )))
862                }
863            },
864            crate::Expression::CallResult(function) => {
865                let result = self.functions[function]
866                    .result
867                    .as_ref()
868                    .ok_or(ResolveError::FunctionReturnsVoid)?;
869                TypeResolution::Handle(result.ty)
870            }
871            crate::Expression::ArrayLength(_) => TypeResolution::Value(Ti::Scalar {
872                kind: crate::ScalarKind::Uint,
873                width: 4,
874            }),
875            crate::Expression::RayQueryProceedResult => TypeResolution::Value(Ti::Scalar {
876                kind: crate::ScalarKind::Bool,
877                width: crate::BOOL_WIDTH,
878            }),
879            crate::Expression::RayQueryGetIntersection { .. } => {
880                let result = self
881                    .special_types
882                    .ray_intersection
883                    .ok_or(ResolveError::MissingSpecialType)?;
884                TypeResolution::Handle(result)
885            }
886        })
887    }
888}
889
890#[test]
891fn test_error_size() {
892    use std::mem::size_of;
893    assert_eq!(size_of::<ResolveError>(), 32);
894}