1 use crate::arena::{Arena, Handle, UniqueArena};
2 
3 use 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 storage class `class` 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 ///     class
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))]
87 pub 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 
99 impl TypeResolution {
handle(&self) -> Option<Handle<crate::Type>>100     pub fn handle(&self) -> Option<Handle<crate::Type>> {
101         match *self {
102             Self::Handle(handle) => Some(handle),
103             Self::Value(_) => None,
104         }
105     }
106 
inner_with<'a>(&'a self, arena: &'a UniqueArena<crate::Type>) -> &'a crate::TypeInner107     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`.
116 impl Clone for TypeResolution {
clone(&self) -> Self117     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, class } => Ti::Pointer { base, class },
134                 Ti::ValuePointer {
135                     size,
136                     kind,
137                     width,
138                     class,
139                 } => Ti::ValuePointer {
140                     size,
141                     kind,
142                     width,
143                     class,
144                 },
145                 _ => unreachable!("Unexpected clone type: {:?}", v),
146             }),
147         }
148     }
149 }
150 
151 impl crate::ConstantInner {
resolve_type(&self) -> TypeResolution152     pub fn resolve_type(&self) -> TypeResolution {
153         match *self {
154             Self::Scalar { width, ref value } => TypeResolution::Value(crate::TypeInner::Scalar {
155                 kind: value.scalar_kind(),
156                 width,
157             }),
158             Self::Composite { ty, components: _ } => TypeResolution::Handle(ty),
159         }
160     }
161 }
162 
163 #[derive(Clone, Debug, Error, PartialEq)]
164 pub 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("Type is not found in the given immutable arena")]
193     TypeNotFound,
194     #[error("Incompatible operands: {0}")]
195     IncompatibleOperands(String),
196 }
197 
198 pub struct ResolveContext<'a> {
199     pub constants: &'a Arena<crate::Constant>,
200     pub types: &'a UniqueArena<crate::Type>,
201     pub global_vars: &'a Arena<crate::GlobalVariable>,
202     pub local_vars: &'a Arena<crate::LocalVariable>,
203     pub functions: &'a Arena<crate::Function>,
204     pub arguments: &'a [crate::FunctionArgument],
205 }
206 
207 impl<'a> ResolveContext<'a> {
208     /// Determine the type of `expr`.
209     ///
210     /// The `past` argument must be a closure that can resolve the types of any
211     /// expressions that `expr` refers to. These can be gathered by caching the
212     /// results of prior calls to `resolve`, perhaps as done by the
213     /// [`front::Typifier`] utility type.
214     ///
215     /// Type resolution is a read-only process: this method takes `self` by
216     /// shared reference. However, this means that we cannot add anything to
217     /// `self.types` that we might need to describe `expr`. To work around this,
218     /// this method returns a [`TypeResolution`], rather than simply returning a
219     /// `Handle<Type>`; see the documentation for [`TypeResolution`] for
220     /// details.
221     ///
222     /// [`front::Typifier`]: crate::front::Typifier
resolve( &self, expr: &crate::Expression, past: impl Fn(Handle<crate::Expression>) -> &'a TypeResolution, ) -> Result<TypeResolution, ResolveError>223     pub fn resolve(
224         &self,
225         expr: &crate::Expression,
226         past: impl Fn(Handle<crate::Expression>) -> &'a TypeResolution,
227     ) -> Result<TypeResolution, ResolveError> {
228         use crate::TypeInner as Ti;
229         let types = self.types;
230         Ok(match *expr {
231             crate::Expression::Access { base, .. } => match *past(base).inner_with(types) {
232                 // Arrays and matrices can only be indexed dynamically behind a
233                 // pointer, but that's a validation error, not a type error, so
234                 // go ahead provide a type here.
235                 Ti::Array { base, .. } => TypeResolution::Handle(base),
236                 Ti::Matrix { rows, width, .. } => TypeResolution::Value(Ti::Vector {
237                     size: rows,
238                     kind: crate::ScalarKind::Float,
239                     width,
240                 }),
241                 Ti::Vector {
242                     size: _,
243                     kind,
244                     width,
245                 } => TypeResolution::Value(Ti::Scalar { kind, width }),
246                 Ti::ValuePointer {
247                     size: Some(_),
248                     kind,
249                     width,
250                     class,
251                 } => TypeResolution::Value(Ti::ValuePointer {
252                     size: None,
253                     kind,
254                     width,
255                     class,
256                 }),
257                 Ti::Pointer { base, class } => {
258                     TypeResolution::Value(match types[base].inner {
259                         Ti::Array { base, .. } => Ti::Pointer { base, class },
260                         Ti::Vector {
261                             size: _,
262                             kind,
263                             width,
264                         } => Ti::ValuePointer {
265                             size: None,
266                             kind,
267                             width,
268                             class,
269                         },
270                         // Matrices are only dynamically indexed behind a pointer
271                         Ti::Matrix {
272                             columns: _,
273                             rows,
274                             width,
275                         } => Ti::ValuePointer {
276                             kind: crate::ScalarKind::Float,
277                             size: Some(rows),
278                             width,
279                             class,
280                         },
281                         ref other => {
282                             log::error!("Access sub-type {:?}", other);
283                             return Err(ResolveError::InvalidSubAccess {
284                                 ty: base,
285                                 indexed: false,
286                             });
287                         }
288                     })
289                 }
290                 ref other => {
291                     log::error!("Access type {:?}", other);
292                     return Err(ResolveError::InvalidAccess {
293                         expr: base,
294                         indexed: false,
295                     });
296                 }
297             },
298             crate::Expression::AccessIndex { base, index } => match *past(base).inner_with(types) {
299                 Ti::Vector { size, kind, width } => {
300                     if index >= size as u32 {
301                         return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
302                     }
303                     TypeResolution::Value(Ti::Scalar { kind, width })
304                 }
305                 Ti::Matrix {
306                     columns,
307                     rows,
308                     width,
309                 } => {
310                     if index >= columns as u32 {
311                         return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
312                     }
313                     TypeResolution::Value(crate::TypeInner::Vector {
314                         size: rows,
315                         kind: crate::ScalarKind::Float,
316                         width,
317                     })
318                 }
319                 Ti::Array { base, .. } => TypeResolution::Handle(base),
320                 Ti::Struct { ref members, .. } => {
321                     let member = members
322                         .get(index as usize)
323                         .ok_or(ResolveError::OutOfBoundsIndex { expr: base, index })?;
324                     TypeResolution::Handle(member.ty)
325                 }
326                 Ti::ValuePointer {
327                     size: Some(size),
328                     kind,
329                     width,
330                     class,
331                 } => {
332                     if index >= size as u32 {
333                         return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
334                     }
335                     TypeResolution::Value(Ti::ValuePointer {
336                         size: None,
337                         kind,
338                         width,
339                         class,
340                     })
341                 }
342                 Ti::Pointer {
343                     base: ty_base,
344                     class,
345                 } => TypeResolution::Value(match types[ty_base].inner {
346                     Ti::Array { base, .. } => Ti::Pointer { base, class },
347                     Ti::Vector { size, kind, width } => {
348                         if index >= size as u32 {
349                             return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
350                         }
351                         Ti::ValuePointer {
352                             size: None,
353                             kind,
354                             width,
355                             class,
356                         }
357                     }
358                     Ti::Matrix {
359                         rows,
360                         columns,
361                         width,
362                     } => {
363                         if index >= columns as u32 {
364                             return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
365                         }
366                         Ti::ValuePointer {
367                             size: Some(rows),
368                             kind: crate::ScalarKind::Float,
369                             width,
370                             class,
371                         }
372                     }
373                     Ti::Struct { ref members, .. } => {
374                         let member = members
375                             .get(index as usize)
376                             .ok_or(ResolveError::OutOfBoundsIndex { expr: base, index })?;
377                         Ti::Pointer {
378                             base: member.ty,
379                             class,
380                         }
381                     }
382                     ref other => {
383                         log::error!("Access index sub-type {:?}", other);
384                         return Err(ResolveError::InvalidSubAccess {
385                             ty: ty_base,
386                             indexed: true,
387                         });
388                     }
389                 }),
390                 ref other => {
391                     log::error!("Access index type {:?}", other);
392                     return Err(ResolveError::InvalidAccess {
393                         expr: base,
394                         indexed: true,
395                     });
396                 }
397             },
398             crate::Expression::Constant(h) => match self.constants[h].inner {
399                 crate::ConstantInner::Scalar { width, ref value } => {
400                     TypeResolution::Value(Ti::Scalar {
401                         kind: value.scalar_kind(),
402                         width,
403                     })
404                 }
405                 crate::ConstantInner::Composite { ty, components: _ } => TypeResolution::Handle(ty),
406             },
407             crate::Expression::Splat { size, value } => match *past(value).inner_with(types) {
408                 Ti::Scalar { kind, width } => {
409                     TypeResolution::Value(Ti::Vector { size, kind, width })
410                 }
411                 ref other => {
412                     log::error!("Scalar type {:?}", other);
413                     return Err(ResolveError::InvalidScalar(value));
414                 }
415             },
416             crate::Expression::Swizzle {
417                 size,
418                 vector,
419                 pattern: _,
420             } => match *past(vector).inner_with(types) {
421                 Ti::Vector {
422                     size: _,
423                     kind,
424                     width,
425                 } => TypeResolution::Value(Ti::Vector { size, kind, width }),
426                 ref other => {
427                     log::error!("Vector type {:?}", other);
428                     return Err(ResolveError::InvalidVector(vector));
429                 }
430             },
431             crate::Expression::Compose { ty, .. } => TypeResolution::Handle(ty),
432             crate::Expression::FunctionArgument(index) => {
433                 TypeResolution::Handle(self.arguments[index as usize].ty)
434             }
435             crate::Expression::GlobalVariable(h) => {
436                 let var = &self.global_vars[h];
437                 if var.class == crate::StorageClass::Handle {
438                     TypeResolution::Handle(var.ty)
439                 } else {
440                     TypeResolution::Value(Ti::Pointer {
441                         base: var.ty,
442                         class: var.class,
443                     })
444                 }
445             }
446             crate::Expression::LocalVariable(h) => {
447                 let var = &self.local_vars[h];
448                 TypeResolution::Value(Ti::Pointer {
449                     base: var.ty,
450                     class: crate::StorageClass::Function,
451                 })
452             }
453             crate::Expression::Load { pointer } => match *past(pointer).inner_with(types) {
454                 Ti::Pointer { base, class: _ } => {
455                     if let Ti::Atomic { kind, width } = types[base].inner {
456                         TypeResolution::Value(Ti::Scalar { kind, width })
457                     } else {
458                         TypeResolution::Handle(base)
459                     }
460                 }
461                 Ti::ValuePointer {
462                     size,
463                     kind,
464                     width,
465                     class: _,
466                 } => TypeResolution::Value(match size {
467                     Some(size) => Ti::Vector { size, kind, width },
468                     None => Ti::Scalar { kind, width },
469                 }),
470                 ref other => {
471                     log::error!("Pointer type {:?}", other);
472                     return Err(ResolveError::InvalidPointer(pointer));
473                 }
474             },
475             crate::Expression::ImageSample {
476                 image,
477                 gather: Some(_),
478                 ..
479             } => match *past(image).inner_with(types) {
480                 Ti::Image { class, .. } => TypeResolution::Value(Ti::Vector {
481                     kind: match class {
482                         crate::ImageClass::Sampled { kind, multi: _ } => kind,
483                         _ => crate::ScalarKind::Float,
484                     },
485                     width: 4,
486                     size: crate::VectorSize::Quad,
487                 }),
488                 ref other => {
489                     log::error!("Image type {:?}", other);
490                     return Err(ResolveError::InvalidImage(image));
491                 }
492             },
493             crate::Expression::ImageSample { image, .. }
494             | crate::Expression::ImageLoad { image, .. } => match *past(image).inner_with(types) {
495                 Ti::Image { class, .. } => TypeResolution::Value(match class {
496                     crate::ImageClass::Depth { multi: _ } => Ti::Scalar {
497                         kind: crate::ScalarKind::Float,
498                         width: 4,
499                     },
500                     crate::ImageClass::Sampled { kind, multi: _ } => Ti::Vector {
501                         kind,
502                         width: 4,
503                         size: crate::VectorSize::Quad,
504                     },
505                     crate::ImageClass::Storage { format, .. } => Ti::Vector {
506                         kind: format.into(),
507                         width: 4,
508                         size: crate::VectorSize::Quad,
509                     },
510                 }),
511                 ref other => {
512                     log::error!("Image type {:?}", other);
513                     return Err(ResolveError::InvalidImage(image));
514                 }
515             },
516             crate::Expression::ImageQuery { image, query } => TypeResolution::Value(match query {
517                 crate::ImageQuery::Size { level: _ } => match *past(image).inner_with(types) {
518                     Ti::Image { dim, .. } => match dim {
519                         crate::ImageDimension::D1 => Ti::Scalar {
520                             kind: crate::ScalarKind::Sint,
521                             width: 4,
522                         },
523                         crate::ImageDimension::D2 | crate::ImageDimension::Cube => Ti::Vector {
524                             size: crate::VectorSize::Bi,
525                             kind: crate::ScalarKind::Sint,
526                             width: 4,
527                         },
528                         crate::ImageDimension::D3 => Ti::Vector {
529                             size: crate::VectorSize::Tri,
530                             kind: crate::ScalarKind::Sint,
531                             width: 4,
532                         },
533                     },
534                     ref other => {
535                         log::error!("Image type {:?}", other);
536                         return Err(ResolveError::InvalidImage(image));
537                     }
538                 },
539                 crate::ImageQuery::NumLevels
540                 | crate::ImageQuery::NumLayers
541                 | crate::ImageQuery::NumSamples => Ti::Scalar {
542                     kind: crate::ScalarKind::Sint,
543                     width: 4,
544                 },
545             }),
546             crate::Expression::Unary { expr, .. } => past(expr).clone(),
547             crate::Expression::Binary { op, left, right } => match op {
548                 crate::BinaryOperator::Add
549                 | crate::BinaryOperator::Subtract
550                 | crate::BinaryOperator::Divide
551                 | crate::BinaryOperator::Modulo => past(left).clone(),
552                 crate::BinaryOperator::Multiply => {
553                     let (res_left, res_right) = (past(left), past(right));
554                     match (res_left.inner_with(types), res_right.inner_with(types)) {
555                         (
556                             &Ti::Matrix {
557                                 columns: _,
558                                 rows,
559                                 width,
560                             },
561                             &Ti::Matrix { columns, .. },
562                         ) => TypeResolution::Value(Ti::Matrix {
563                             columns,
564                             rows,
565                             width,
566                         }),
567                         (
568                             &Ti::Matrix {
569                                 columns: _,
570                                 rows,
571                                 width,
572                             },
573                             &Ti::Vector { .. },
574                         ) => TypeResolution::Value(Ti::Vector {
575                             size: rows,
576                             kind: crate::ScalarKind::Float,
577                             width,
578                         }),
579                         (
580                             &Ti::Vector { .. },
581                             &Ti::Matrix {
582                                 columns,
583                                 rows: _,
584                                 width,
585                             },
586                         ) => TypeResolution::Value(Ti::Vector {
587                             size: columns,
588                             kind: crate::ScalarKind::Float,
589                             width,
590                         }),
591                         (&Ti::Scalar { .. }, _) => res_right.clone(),
592                         (_, &Ti::Scalar { .. }) => res_left.clone(),
593                         (&Ti::Vector { .. }, &Ti::Vector { .. }) => res_left.clone(),
594                         (tl, tr) => {
595                             return Err(ResolveError::IncompatibleOperands(format!(
596                                 "{:?} * {:?}",
597                                 tl, tr
598                             )))
599                         }
600                     }
601                 }
602                 crate::BinaryOperator::Equal
603                 | crate::BinaryOperator::NotEqual
604                 | crate::BinaryOperator::Less
605                 | crate::BinaryOperator::LessEqual
606                 | crate::BinaryOperator::Greater
607                 | crate::BinaryOperator::GreaterEqual
608                 | crate::BinaryOperator::LogicalAnd
609                 | crate::BinaryOperator::LogicalOr => {
610                     let kind = crate::ScalarKind::Bool;
611                     let width = crate::BOOL_WIDTH;
612                     let inner = match *past(left).inner_with(types) {
613                         Ti::Scalar { .. } => Ti::Scalar { kind, width },
614                         Ti::Vector { size, .. } => Ti::Vector { size, kind, width },
615                         ref other => {
616                             return Err(ResolveError::IncompatibleOperands(format!(
617                                 "{:?}({:?}, _)",
618                                 op, other
619                             )))
620                         }
621                     };
622                     TypeResolution::Value(inner)
623                 }
624                 crate::BinaryOperator::And
625                 | crate::BinaryOperator::ExclusiveOr
626                 | crate::BinaryOperator::InclusiveOr
627                 | crate::BinaryOperator::ShiftLeft
628                 | crate::BinaryOperator::ShiftRight => past(left).clone(),
629             },
630             crate::Expression::AtomicResult {
631                 kind,
632                 width,
633                 comparison,
634             } => {
635                 if comparison {
636                     TypeResolution::Value(Ti::Vector {
637                         size: crate::VectorSize::Bi,
638                         kind,
639                         width,
640                     })
641                 } else {
642                     TypeResolution::Value(Ti::Scalar { kind, width })
643                 }
644             }
645             crate::Expression::Select { accept, .. } => past(accept).clone(),
646             crate::Expression::Derivative { axis: _, expr } => past(expr).clone(),
647             crate::Expression::Relational { fun, argument } => match fun {
648                 crate::RelationalFunction::All | crate::RelationalFunction::Any => {
649                     TypeResolution::Value(Ti::Scalar {
650                         kind: crate::ScalarKind::Bool,
651                         width: crate::BOOL_WIDTH,
652                     })
653                 }
654                 crate::RelationalFunction::IsNan
655                 | crate::RelationalFunction::IsInf
656                 | crate::RelationalFunction::IsFinite
657                 | crate::RelationalFunction::IsNormal => match *past(argument).inner_with(types) {
658                     Ti::Scalar { .. } => TypeResolution::Value(Ti::Scalar {
659                         kind: crate::ScalarKind::Bool,
660                         width: crate::BOOL_WIDTH,
661                     }),
662                     Ti::Vector { size, .. } => TypeResolution::Value(Ti::Vector {
663                         kind: crate::ScalarKind::Bool,
664                         width: crate::BOOL_WIDTH,
665                         size,
666                     }),
667                     ref other => {
668                         return Err(ResolveError::IncompatibleOperands(format!(
669                             "{:?}({:?})",
670                             fun, other
671                         )))
672                     }
673                 },
674             },
675             crate::Expression::Math {
676                 fun,
677                 arg,
678                 arg1,
679                 arg2: _,
680                 arg3: _,
681             } => {
682                 use crate::MathFunction as Mf;
683                 let res_arg = past(arg);
684                 match fun {
685                     // comparison
686                     Mf::Abs |
687                     Mf::Min |
688                     Mf::Max |
689                     Mf::Clamp |
690                     // trigonometry
691                     Mf::Cos |
692                     Mf::Cosh |
693                     Mf::Sin |
694                     Mf::Sinh |
695                     Mf::Tan |
696                     Mf::Tanh |
697                     Mf::Acos |
698                     Mf::Asin |
699                     Mf::Atan |
700                     Mf::Atan2 |
701                     Mf::Asinh |
702                     Mf::Acosh |
703                     Mf::Atanh |
704                     Mf::Radians |
705                     Mf::Degrees |
706                     // decomposition
707                     Mf::Ceil |
708                     Mf::Floor |
709                     Mf::Round |
710                     Mf::Fract |
711                     Mf::Trunc |
712                     Mf::Modf |
713                     Mf::Frexp |
714                     Mf::Ldexp |
715                     // exponent
716                     Mf::Exp |
717                     Mf::Exp2 |
718                     Mf::Log |
719                     Mf::Log2 |
720                     Mf::Pow => res_arg.clone(),
721                     // geometry
722                     Mf::Dot => match *res_arg.inner_with(types) {
723                         Ti::Vector {
724                             kind,
725                             size: _,
726                             width,
727                         } => TypeResolution::Value(Ti::Scalar { kind, width }),
728                         ref other =>
729                             return Err(ResolveError::IncompatibleOperands(
730                                 format!("{:?}({:?}, _)", fun, other)
731                             )),
732                     },
733                     Mf::Outer => {
734                         let arg1 = arg1.ok_or_else(|| ResolveError::IncompatibleOperands(
735                             format!("{:?}(_, None)", fun)
736                         ))?;
737                         match (res_arg.inner_with(types), past(arg1).inner_with(types)) {
738                             (&Ti::Vector {kind: _, size: columns,width}, &Ti::Vector{ size: rows, .. }) => TypeResolution::Value(Ti::Matrix { columns, rows, width }),
739                             (left, right) =>
740                                 return Err(ResolveError::IncompatibleOperands(
741                                     format!("{:?}({:?}, {:?})", fun, left, right)
742                                 )),
743                         }
744                     },
745                     Mf::Cross => res_arg.clone(),
746                     Mf::Distance |
747                     Mf::Length => match *res_arg.inner_with(types) {
748                         Ti::Scalar {width,kind} |
749                         Ti::Vector {width,kind,size:_} => TypeResolution::Value(Ti::Scalar { kind, width }),
750                         ref other => return Err(ResolveError::IncompatibleOperands(
751                                 format!("{:?}({:?})", fun, other)
752                             )),
753                     },
754                     Mf::Normalize |
755                     Mf::FaceForward |
756                     Mf::Reflect |
757                     Mf::Refract => res_arg.clone(),
758                     // computational
759                     Mf::Sign |
760                     Mf::Fma |
761                     Mf::Mix |
762                     Mf::Step |
763                     Mf::SmoothStep |
764                     Mf::Sqrt |
765                     Mf::InverseSqrt => res_arg.clone(),
766                     Mf::Transpose => match *res_arg.inner_with(types) {
767                         Ti::Matrix {
768                             columns,
769                             rows,
770                             width,
771                         } => TypeResolution::Value(Ti::Matrix {
772                             columns: rows,
773                             rows: columns,
774                             width,
775                         }),
776                         ref other => return Err(ResolveError::IncompatibleOperands(
777                             format!("{:?}({:?})", fun, other)
778                         )),
779                     },
780                     Mf::Inverse => match *res_arg.inner_with(types) {
781                         Ti::Matrix {
782                             columns,
783                             rows,
784                             width,
785                         } if columns == rows => TypeResolution::Value(Ti::Matrix {
786                             columns,
787                             rows,
788                             width,
789                         }),
790                         ref other => return Err(ResolveError::IncompatibleOperands(
791                             format!("{:?}({:?})", fun, other)
792                         )),
793                     },
794                     Mf::Determinant => match *res_arg.inner_with(types) {
795                         Ti::Matrix {
796                             width,
797                             ..
798                         } => TypeResolution::Value(Ti::Scalar { kind: crate::ScalarKind::Float, width }),
799                         ref other => return Err(ResolveError::IncompatibleOperands(
800                             format!("{:?}({:?})", fun, other)
801                         )),
802                     },
803                     // bits
804                     Mf::CountOneBits |
805                     Mf::ReverseBits |
806                     Mf::ExtractBits |
807                     Mf::InsertBits => res_arg.clone(),
808                     Mf::FindLsb |
809                     Mf::FindMsb => match *res_arg.inner_with(types)  {
810                         Ti::Scalar { kind: _, width } =>
811                             TypeResolution::Value(Ti::Scalar { kind: crate::ScalarKind::Sint, width }),
812                         Ti::Vector { size, kind: _, width } =>
813                             TypeResolution::Value(Ti::Vector { size, kind: crate::ScalarKind::Sint, width }),
814                         ref other => return Err(ResolveError::IncompatibleOperands(
815                                 format!("{:?}({:?})", fun, other)
816                             )),
817                     },
818                     // data packing
819                     Mf::Pack4x8snorm |
820                     Mf::Pack4x8unorm |
821                     Mf::Pack2x16snorm |
822                     Mf::Pack2x16unorm |
823                     Mf::Pack2x16float => TypeResolution::Value(Ti::Scalar { kind: crate::ScalarKind::Uint, width: 4 }),
824                     // data unpacking
825                     Mf::Unpack4x8snorm |
826                     Mf::Unpack4x8unorm => TypeResolution::Value(Ti::Vector { size: crate::VectorSize::Quad, kind: crate::ScalarKind::Float, width: 4 }),
827                     Mf::Unpack2x16snorm |
828                     Mf::Unpack2x16unorm |
829                     Mf::Unpack2x16float => TypeResolution::Value(Ti::Vector { size: crate::VectorSize::Bi, kind: crate::ScalarKind::Float, width: 4 }),
830                 }
831             }
832             crate::Expression::As {
833                 expr,
834                 kind,
835                 convert,
836             } => match *past(expr).inner_with(types) {
837                 Ti::Scalar { kind: _, width } => TypeResolution::Value(Ti::Scalar {
838                     kind,
839                     width: convert.unwrap_or(width),
840                 }),
841                 Ti::Vector {
842                     kind: _,
843                     size,
844                     width,
845                 } => TypeResolution::Value(Ti::Vector {
846                     kind,
847                     size,
848                     width: convert.unwrap_or(width),
849                 }),
850                 ref other => {
851                     return Err(ResolveError::IncompatibleOperands(format!(
852                         "{:?} as {:?}",
853                         other, kind
854                     )))
855                 }
856             },
857             crate::Expression::CallResult(function) => {
858                 let result = self.functions[function]
859                     .result
860                     .as_ref()
861                     .ok_or(ResolveError::FunctionReturnsVoid)?;
862                 TypeResolution::Handle(result.ty)
863             }
864             crate::Expression::ArrayLength(_) => TypeResolution::Value(Ti::Scalar {
865                 kind: crate::ScalarKind::Uint,
866                 width: 4,
867             }),
868         })
869     }
870 }
871 
872 #[test]
test_error_size()873 fn test_error_size() {
874     use std::mem::size_of;
875     assert_eq!(size_of::<ResolveError>(), 32);
876 }
877