naga/front/wgsl/lower/
construction.rs

1use alloc::{boxed::Box, vec, vec::Vec};
2use core::num::NonZeroU32;
3
4use crate::common::wgsl::{TryToWgsl, TypeContext};
5use crate::front::wgsl::lower::{ExpressionContext, Lowerer};
6use crate::front::wgsl::parse::ast;
7use crate::front::wgsl::{Error, Result};
8use crate::{Handle, Span};
9
10/// A [`constructor built-in function`].
11///
12/// WGSL has two types of such functions:
13///
14/// - Those that fully specify the type being constructed, like
15///   `vec3<f32>(x,y,z)`, which obviously constructs a `vec3<f32>`.
16///
17/// - Those that leave the component type of the composite being constructed
18///   implicit, to be inferred from the argument types, like `vec3(x,y,z)`,
19///   which constructs a `vec3<T>` where `T` is the type of `x`, `y`, and `z`.
20///
21/// This enum represents both cases. The `PartialFoo` variants
22/// represent the second case, where the component type is implicit.
23///
24/// [`constructor built-in function`]: https://gpuweb.github.io/gpuweb/wgsl/#constructor-builtin-function
25pub enum Constructor<T> {
26    /// A vector construction whose component type is inferred from the
27    /// argument: `vec3(1.0)`.
28    PartialVector { size: crate::VectorSize },
29
30    /// A matrix construction whose component type is inferred from the
31    /// argument: `mat2x2(1,2,3,4)`.
32    PartialMatrix {
33        columns: crate::VectorSize,
34        rows: crate::VectorSize,
35    },
36
37    /// An array whose component type and size are inferred from the arguments:
38    /// `array(3,4,5)`.
39    PartialArray,
40
41    /// A known Naga type.
42    ///
43    /// When we match on this type, we need to see the `TypeInner` here, but at
44    /// the point that we build this value we'll still need mutable access to
45    /// the module later. To avoid borrowing from the module, the type parameter
46    /// `T` is `Handle<Type>` initially. Then we use `borrow_inner` to produce a
47    /// version holding a tuple `(Handle<Type>, &TypeInner)`.
48    Type(T),
49}
50
51impl Constructor<Handle<crate::Type>> {
52    /// Return an equivalent `Constructor` value that includes borrowed
53    /// `TypeInner` values alongside any type handles.
54    ///
55    /// The returned form is more convenient to match on, since the patterns
56    /// can actually see what the handle refers to.
57    fn borrow_inner(
58        self,
59        module: &crate::Module,
60    ) -> Constructor<(Handle<crate::Type>, &crate::TypeInner)> {
61        match self {
62            Constructor::PartialVector { size } => Constructor::PartialVector { size },
63            Constructor::PartialMatrix { columns, rows } => {
64                Constructor::PartialMatrix { columns, rows }
65            }
66            Constructor::PartialArray => Constructor::PartialArray,
67            Constructor::Type(handle) => Constructor::Type((handle, &module.types[handle].inner)),
68        }
69    }
70}
71
72enum Components<'a> {
73    None,
74    One {
75        component: Handle<crate::Expression>,
76        span: Span,
77        ty_inner: &'a crate::TypeInner,
78    },
79    Many {
80        components: Vec<Handle<crate::Expression>>,
81        spans: Vec<Span>,
82    },
83}
84
85impl Components<'_> {
86    fn into_components_vec(self) -> Vec<Handle<crate::Expression>> {
87        match self {
88            Self::None => vec![],
89            Self::One { component, .. } => vec![component],
90            Self::Many { components, .. } => components,
91        }
92    }
93}
94
95impl<'source> Lowerer<'source, '_> {
96    /// Generate Naga IR for a type constructor expression.
97    ///
98    /// The `constructor` value represents the head of the constructor
99    /// expression, which is at least a hint of which type is being built; if
100    /// it's one of the `Partial` variants, we need to consider the argument
101    /// types as well.
102    ///
103    /// This is used for [`Call`] expressions, once we've determined that
104    /// the "callable" (in WGSL spec terms) is actually a type.
105    ///
106    /// [`Call`]: ast::Expression::Call
107    pub fn construct(
108        &mut self,
109        span: Span,
110        constructor: Constructor<Handle<crate::Type>>,
111        ty_span: Span,
112        components: &[Handle<ast::Expression<'source>>],
113        ctx: &mut ExpressionContext<'source, '_, '_>,
114    ) -> Result<'source, Handle<crate::Expression>> {
115        use crate::proc::TypeResolution as Tr;
116
117        let components = match *components {
118            [] => Components::None,
119            [component] => {
120                let span = ctx.ast_expressions.get_span(component);
121                let component = self.expression_for_abstract(component, ctx)?;
122                let ty_inner = super::resolve_inner!(ctx, component);
123
124                Components::One {
125                    component,
126                    span,
127                    ty_inner,
128                }
129            }
130            ref ast_components @ [_, _, ..] => {
131                let components = ast_components
132                    .iter()
133                    .map(|&expr| self.expression_for_abstract(expr, ctx))
134                    .collect::<Result<_>>()?;
135                let spans = ast_components
136                    .iter()
137                    .map(|&expr| ctx.ast_expressions.get_span(expr))
138                    .collect();
139
140                for &component in &components {
141                    ctx.grow_types(component)?;
142                }
143
144                Components::Many { components, spans }
145            }
146        };
147
148        // Even though we computed `constructor` above, wait until now to borrow
149        // a reference to the `TypeInner`, so that the component-handling code
150        // above can have mutable access to the type arena.
151        let constructor = constructor.borrow_inner(ctx.module);
152
153        let expr;
154        match (components, constructor) {
155            // Zero-value constructor with explicit type.
156            (Components::None, Constructor::Type((result_ty, inner)))
157                if inner.is_constructible(&ctx.module.types) =>
158            {
159                expr = crate::Expression::ZeroValue(result_ty);
160            }
161            // Zero-value constructor, vector with type inference
162            (Components::None, Constructor::PartialVector { size }) => {
163                // vec2(), vec3(), vec4() return vectors of abstractInts; the same
164                // is not true of the similar constructors for matrices or arrays.
165                // See https://www.w3.org/TR/WGSL/#vec2-builtin et seq.
166                let result_ty = ctx.module.types.insert(
167                    crate::Type {
168                        name: None,
169                        inner: crate::TypeInner::Vector {
170                            size,
171                            scalar: crate::Scalar::ABSTRACT_INT,
172                        },
173                    },
174                    span,
175                );
176                expr = crate::Expression::ZeroValue(result_ty);
177            }
178            // Zero-value constructor, matrix or array with type inference
179            (Components::None, Constructor::PartialMatrix { .. } | Constructor::PartialArray) => {
180                // We have no arguments from which to infer the result type, so
181                // partial constructors aren't acceptable here.
182                return Err(Box::new(Error::TypeNotInferable(ty_span)));
183            }
184
185            // Scalar constructor & conversion (scalar -> scalar)
186            (
187                Components::One {
188                    component,
189                    ty_inner: &crate::TypeInner::Scalar { .. },
190                    ..
191                },
192                Constructor::Type((_, &crate::TypeInner::Scalar(scalar))),
193            ) => {
194                expr = crate::Expression::As {
195                    expr: component,
196                    kind: scalar.kind,
197                    convert: Some(scalar.width),
198                };
199            }
200
201            // Vector conversion (vector -> vector)
202            (
203                Components::One {
204                    component,
205                    ty_inner: &crate::TypeInner::Vector { size: src_size, .. },
206                    ..
207                },
208                Constructor::Type((
209                    _,
210                    &crate::TypeInner::Vector {
211                        size: dst_size,
212                        scalar: dst_scalar,
213                    },
214                )),
215            ) if dst_size == src_size => {
216                expr = crate::Expression::As {
217                    expr: component,
218                    kind: dst_scalar.kind,
219                    convert: Some(dst_scalar.width),
220                };
221            }
222
223            // Vector conversion (vector -> vector) - partial
224            (
225                Components::One {
226                    component,
227                    ty_inner: &crate::TypeInner::Vector { size: src_size, .. },
228                    ..
229                },
230                Constructor::PartialVector { size: dst_size },
231            ) if dst_size == src_size => {
232                // This is a trivial conversion: the sizes match, and a Partial
233                // constructor doesn't specify a scalar type, so nothing can
234                // possibly happen.
235                return Ok(component);
236            }
237
238            // Matrix conversion (matrix -> matrix)
239            (
240                Components::One {
241                    component,
242                    ty_inner:
243                        &crate::TypeInner::Matrix {
244                            columns: src_columns,
245                            rows: src_rows,
246                            ..
247                        },
248                    ..
249                },
250                Constructor::Type((
251                    _,
252                    &crate::TypeInner::Matrix {
253                        columns: dst_columns,
254                        rows: dst_rows,
255                        scalar: dst_scalar,
256                    },
257                )),
258            ) if dst_columns == src_columns && dst_rows == src_rows => {
259                expr = crate::Expression::As {
260                    expr: component,
261                    kind: dst_scalar.kind,
262                    convert: Some(dst_scalar.width),
263                };
264            }
265
266            // Matrix conversion (matrix -> matrix) - partial
267            (
268                Components::One {
269                    component,
270                    ty_inner:
271                        &crate::TypeInner::Matrix {
272                            columns: src_columns,
273                            rows: src_rows,
274                            ..
275                        },
276                    ..
277                },
278                Constructor::PartialMatrix {
279                    columns: dst_columns,
280                    rows: dst_rows,
281                },
282            ) if dst_columns == src_columns && dst_rows == src_rows => {
283                // This is a trivial conversion: the sizes match, and a Partial
284                // constructor doesn't specify a scalar type, so nothing can
285                // possibly happen.
286                return Ok(component);
287            }
288
289            // Vector constructor (splat) - infer type
290            (
291                Components::One {
292                    component,
293                    ty_inner: &crate::TypeInner::Scalar { .. },
294                    ..
295                },
296                Constructor::PartialVector { size },
297            ) => {
298                expr = crate::Expression::Splat {
299                    size,
300                    value: component,
301                };
302            }
303
304            // Vector constructor (splat)
305            (
306                Components::One {
307                    mut component,
308                    ty_inner: &crate::TypeInner::Scalar(component_scalar),
309                    span,
310                },
311                Constructor::Type((
312                    type_handle,
313                    &crate::TypeInner::Vector {
314                        size,
315                        scalar: vec_scalar,
316                    },
317                )),
318            ) => {
319                // Splat only allows automatic conversions of the component's scalar.
320                if !component_scalar.automatically_converts_to(vec_scalar) {
321                    let component_ty = &ctx.typifier()[component];
322                    let arg_ty = ctx.type_resolution_to_string(component_ty);
323                    return Err(Box::new(Error::WrongArgumentType {
324                        function: ctx.type_to_string(type_handle),
325                        call_span: ty_span,
326                        arg_span: span,
327                        arg_index: 0,
328                        arg_ty,
329                        allowed: vec![vec_scalar.to_wgsl_for_diagnostics()],
330                    }));
331                }
332                ctx.convert_slice_to_common_leaf_scalar(
333                    core::slice::from_mut(&mut component),
334                    vec_scalar,
335                )?;
336                expr = crate::Expression::Splat {
337                    size,
338                    value: component,
339                };
340            }
341
342            // Vector constructor (by elements), partial
343            (
344                Components::Many {
345                    mut components,
346                    spans,
347                },
348                Constructor::PartialVector { size },
349            ) => {
350                let consensus_scalar = ctx
351                    .automatic_conversion_consensus(None, &components)
352                    .map_err(|index| {
353                        Error::InvalidConstructorComponentType(spans[index], index as i32)
354                    })?;
355                ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;
356                let inner = consensus_scalar.to_inner_vector(size);
357                let ty = ctx.ensure_type_exists(inner);
358                expr = crate::Expression::Compose { ty, components };
359            }
360
361            // Vector constructor (by elements), full type given
362            (
363                Components::Many { mut components, .. },
364                Constructor::Type((ty, &crate::TypeInner::Vector { scalar, .. })),
365            ) => {
366                ctx.try_automatic_conversions_for_vector(&mut components, scalar, ty_span)?;
367                expr = crate::Expression::Compose { ty, components };
368            }
369
370            // Matrix constructor (by elements), partial
371            (
372                Components::Many {
373                    mut components,
374                    spans,
375                },
376                Constructor::PartialMatrix { columns, rows },
377            ) if components.len() == columns as usize * rows as usize => {
378                let consensus_scalar = ctx
379                    .automatic_conversion_consensus(
380                        Some(crate::Scalar::ABSTRACT_FLOAT),
381                        &components,
382                    )
383                    .map_err(|index| {
384                        Error::InvalidConstructorComponentType(spans[index], index as i32)
385                    })?;
386                ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;
387                let vec_ty = ctx.ensure_type_exists(consensus_scalar.to_inner_vector(rows));
388
389                let components = components
390                    .chunks(rows as usize)
391                    .map(|vec_components| {
392                        ctx.append_expression(
393                            crate::Expression::Compose {
394                                ty: vec_ty,
395                                components: Vec::from(vec_components),
396                            },
397                            Default::default(),
398                        )
399                    })
400                    .collect::<Result<Vec<_>>>()?;
401
402                let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
403                    columns,
404                    rows,
405                    scalar: consensus_scalar,
406                });
407                expr = crate::Expression::Compose { ty, components };
408            }
409
410            // Matrix constructor (by elements), type given
411            (
412                Components::Many { mut components, .. },
413                Constructor::Type((
414                    _,
415                    &crate::TypeInner::Matrix {
416                        columns,
417                        rows,
418                        scalar,
419                    },
420                )),
421            ) if components.len() == columns as usize * rows as usize => {
422                let element = Tr::Value(crate::TypeInner::Scalar(scalar));
423                ctx.try_automatic_conversions_slice(&mut components, &element, ty_span)?;
424                let vec_ty = ctx.ensure_type_exists(scalar.to_inner_vector(rows));
425
426                let components = components
427                    .chunks(rows as usize)
428                    .map(|vec_components| {
429                        ctx.append_expression(
430                            crate::Expression::Compose {
431                                ty: vec_ty,
432                                components: Vec::from(vec_components),
433                            },
434                            Default::default(),
435                        )
436                    })
437                    .collect::<Result<Vec<_>>>()?;
438
439                let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
440                    columns,
441                    rows,
442                    scalar,
443                });
444                expr = crate::Expression::Compose { ty, components };
445            }
446
447            // Matrix constructor (by columns), partial
448            (
449                Components::Many {
450                    mut components,
451                    spans,
452                },
453                Constructor::PartialMatrix { columns, rows },
454            ) => {
455                let consensus_scalar = ctx
456                    .automatic_conversion_consensus(
457                        Some(crate::Scalar::ABSTRACT_FLOAT),
458                        &components,
459                    )
460                    .map_err(|index| {
461                        Error::InvalidConstructorComponentType(spans[index], index as i32)
462                    })?;
463                ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;
464                let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
465                    columns,
466                    rows,
467                    scalar: consensus_scalar,
468                });
469                expr = crate::Expression::Compose { ty, components };
470            }
471
472            // Matrix constructor (by columns), type given
473            (
474                Components::Many { mut components, .. },
475                Constructor::Type((
476                    ty,
477                    &crate::TypeInner::Matrix {
478                        columns: _,
479                        rows,
480                        scalar,
481                    },
482                )),
483            ) => {
484                let component_ty = crate::TypeInner::Vector { size: rows, scalar };
485                ctx.try_automatic_conversions_slice(
486                    &mut components,
487                    &Tr::Value(component_ty),
488                    ty_span,
489                )?;
490                expr = crate::Expression::Compose { ty, components };
491            }
492
493            // Array constructor - infer type
494            (components, Constructor::PartialArray) => {
495                let mut components = components.into_components_vec();
496                if let Ok(consensus_scalar) = ctx.automatic_conversion_consensus(None, &components)
497                {
498                    // Note that this will *not* necessarily convert all the
499                    // components to the same type! The `automatic_conversion_consensus`
500                    // method only considers the parameters' leaf scalar
501                    // types; the parameters themselves could be any mix of
502                    // vectors, matrices, and scalars.
503                    //
504                    // But *if* it is possible for this array construction
505                    // expression to be well-typed at all, then all the
506                    // parameters must have the same type constructors (vec,
507                    // matrix, scalar) applied to their leaf scalars, so
508                    // reconciling their scalars is always the right thing to
509                    // do. And if this array construction is not well-typed,
510                    // these conversions will not make it so, and we can let
511                    // validation catch the error.
512                    ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;
513                } else {
514                    // There's no consensus scalar. Emit the `Compose`
515                    // expression anyway, and let validation catch the problem.
516                }
517
518                let base = ctx.register_type(components[0])?;
519
520                let inner = crate::TypeInner::Array {
521                    base,
522                    size: crate::ArraySize::Constant(
523                        NonZeroU32::new(u32::try_from(components.len()).unwrap()).unwrap(),
524                    ),
525                    stride: {
526                        ctx.layouter.update(ctx.module.to_ctx()).unwrap();
527                        ctx.layouter[base].to_stride()
528                    },
529                };
530                let ty = ctx.ensure_type_exists(inner);
531
532                expr = crate::Expression::Compose { ty, components };
533            }
534
535            // Array constructor, explicit type.
536            (
537                components,
538                Constructor::Type((ty, inner @ &crate::TypeInner::Array { base, .. })),
539            ) if inner.is_constructible(&ctx.module.types) => {
540                let mut components = components.into_components_vec();
541                ctx.try_automatic_conversions_slice(&mut components, &Tr::Handle(base), ty_span)?;
542                expr = crate::Expression::Compose { ty, components };
543            }
544
545            // Struct constructor
546            (
547                components,
548                Constructor::Type((ty, inner @ &crate::TypeInner::Struct { ref members, .. })),
549            ) if inner.is_constructible(&ctx.module.types) => {
550                let mut components = components.into_components_vec();
551                let struct_ty_span = ctx.module.types.get_span(ty);
552
553                // Make a vector of the members' type handles in advance, to
554                // avoid borrowing `members` from `ctx` while we generate
555                // new code.
556                let members: Vec<Handle<crate::Type>> = members.iter().map(|m| m.ty).collect();
557
558                for (component, &ty) in components.iter_mut().zip(&members) {
559                    *component =
560                        ctx.try_automatic_conversions(*component, &Tr::Handle(ty), struct_ty_span)?;
561                }
562                expr = crate::Expression::Compose { ty, components };
563            }
564
565            // ERRORS
566
567            // Bad conversion (type cast)
568            (
569                Components::One {
570                    span, component, ..
571                },
572                Constructor::Type((
573                    ty,
574                    &(crate::TypeInner::Scalar { .. }
575                    | crate::TypeInner::Vector { .. }
576                    | crate::TypeInner::Matrix { .. }),
577                )),
578            ) => {
579                let component_ty = &ctx.typifier()[component];
580                let from_type = ctx.type_resolution_to_string(component_ty);
581                return Err(Box::new(Error::BadTypeCast {
582                    span,
583                    from_type,
584                    to_type: ctx.type_to_string(ty),
585                }));
586            }
587
588            // Too many parameters for scalar constructor
589            (
590                Components::Many { spans, .. },
591                Constructor::Type((_, &crate::TypeInner::Scalar { .. })),
592            ) => {
593                let span = spans[1].until(spans.last().unwrap());
594                return Err(Box::new(Error::UnexpectedComponents(span)));
595            }
596
597            // Other types can't be constructed
598            _ => return Err(Box::new(Error::TypeNotConstructible(ty_span))),
599        }
600
601        let expr = ctx.append_expression(expr, span)?;
602        Ok(expr)
603    }
604}