naga/proc/
typifier.rs

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