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 target: scalar,
197 };
198 }
199
200 // Vector conversion (vector -> vector)
201 (
202 Components::One {
203 component,
204 ty_inner: &crate::TypeInner::Vector { size: src_size, .. },
205 ..
206 },
207 Constructor::Type((
208 _,
209 &crate::TypeInner::Vector {
210 size: dst_size,
211 scalar: dst_scalar,
212 },
213 )),
214 ) if dst_size == src_size => {
215 expr = crate::Expression::As {
216 expr: component,
217 target: dst_scalar,
218 };
219 }
220
221 // Vector conversion (vector -> vector) - partial
222 (
223 Components::One {
224 component,
225 ty_inner: &crate::TypeInner::Vector { size: src_size, .. },
226 ..
227 },
228 Constructor::PartialVector { size: dst_size },
229 ) if dst_size == src_size => {
230 // This is a trivial conversion: the sizes match, and a Partial
231 // constructor doesn't specify a scalar type, so nothing can
232 // possibly happen.
233 return Ok(component);
234 }
235
236 // Matrix conversion (matrix -> matrix)
237 (
238 Components::One {
239 component,
240 ty_inner:
241 &crate::TypeInner::Matrix {
242 columns: src_columns,
243 rows: src_rows,
244 ..
245 },
246 ..
247 },
248 Constructor::Type((
249 _,
250 &crate::TypeInner::Matrix {
251 columns: dst_columns,
252 rows: dst_rows,
253 scalar: dst_scalar,
254 },
255 )),
256 ) if dst_columns == src_columns && dst_rows == src_rows => {
257 expr = crate::Expression::As {
258 expr: component,
259 target: dst_scalar,
260 };
261 }
262
263 // Matrix conversion (matrix -> matrix) - partial
264 (
265 Components::One {
266 component,
267 ty_inner:
268 &crate::TypeInner::Matrix {
269 columns: src_columns,
270 rows: src_rows,
271 ..
272 },
273 ..
274 },
275 Constructor::PartialMatrix {
276 columns: dst_columns,
277 rows: dst_rows,
278 },
279 ) if dst_columns == src_columns && dst_rows == src_rows => {
280 // This is a trivial conversion: the sizes match, and a Partial
281 // constructor doesn't specify a scalar type, so nothing can
282 // possibly happen.
283 return Ok(component);
284 }
285
286 // Vector constructor (splat) - infer type
287 (
288 Components::One {
289 component,
290 ty_inner: &crate::TypeInner::Scalar { .. },
291 ..
292 },
293 Constructor::PartialVector { size },
294 ) => {
295 expr = crate::Expression::Splat {
296 size,
297 value: component,
298 };
299 }
300
301 // Vector constructor (splat)
302 (
303 Components::One {
304 mut component,
305 ty_inner: &crate::TypeInner::Scalar(component_scalar),
306 span,
307 },
308 Constructor::Type((
309 type_handle,
310 &crate::TypeInner::Vector {
311 size,
312 scalar: vec_scalar,
313 },
314 )),
315 ) => {
316 // Splat only allows automatic conversions of the component's scalar.
317 if !component_scalar.automatically_converts_to(vec_scalar) {
318 let component_ty = &ctx.typifier()[component];
319 let arg_ty = ctx.type_resolution_to_string(component_ty);
320 return Err(Box::new(Error::WrongArgumentType {
321 function: ctx.type_to_string(type_handle),
322 call_span: ty_span,
323 arg_span: span,
324 arg_index: 0,
325 arg_ty,
326 allowed: vec![vec_scalar.to_wgsl_for_diagnostics()],
327 }));
328 }
329 ctx.convert_slice_to_common_leaf_scalar(
330 core::slice::from_mut(&mut component),
331 vec_scalar,
332 )?;
333 expr = crate::Expression::Splat {
334 size,
335 value: component,
336 };
337 }
338
339 // Vector constructor (by elements), partial
340 (
341 Components::Many {
342 mut components,
343 spans,
344 },
345 Constructor::PartialVector { size },
346 ) => {
347 let consensus_scalar = ctx
348 .automatic_conversion_consensus(None, &components)
349 .map_err(|index| {
350 Error::InvalidConstructorComponentType(spans[index], index as i32)
351 })?;
352 ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;
353 let inner = consensus_scalar.to_inner_vector(size);
354 let ty = ctx.ensure_type_exists(inner);
355 expr = crate::Expression::Compose { ty, components };
356 }
357
358 // Vector constructor (by elements), full type given
359 (
360 Components::Many { mut components, .. },
361 Constructor::Type((ty, &crate::TypeInner::Vector { scalar, .. })),
362 ) => {
363 ctx.try_automatic_conversions_for_vector(&mut components, scalar, ty_span)?;
364 expr = crate::Expression::Compose { ty, components };
365 }
366
367 // Matrix constructor (by elements), partial
368 (
369 Components::Many {
370 mut components,
371 spans,
372 },
373 Constructor::PartialMatrix { columns, rows },
374 ) if components.len() == columns as usize * rows as usize => {
375 let consensus_scalar = ctx
376 .automatic_conversion_consensus(
377 Some(crate::Scalar::ABSTRACT_FLOAT),
378 &components,
379 )
380 .map_err(|index| {
381 Error::InvalidConstructorComponentType(spans[index], index as i32)
382 })?;
383 ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;
384 let vec_ty = ctx.ensure_type_exists(consensus_scalar.to_inner_vector(rows));
385
386 let components = components
387 .chunks(rows as usize)
388 .map(|vec_components| {
389 ctx.append_expression(
390 crate::Expression::Compose {
391 ty: vec_ty,
392 components: Vec::from(vec_components),
393 },
394 Default::default(),
395 )
396 })
397 .collect::<Result<Vec<_>>>()?;
398
399 let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
400 columns,
401 rows,
402 scalar: consensus_scalar,
403 });
404 expr = crate::Expression::Compose { ty, components };
405 }
406
407 // Matrix constructor (by elements), type given
408 (
409 Components::Many { mut components, .. },
410 Constructor::Type((
411 _,
412 &crate::TypeInner::Matrix {
413 columns,
414 rows,
415 scalar,
416 },
417 )),
418 ) if components.len() == columns as usize * rows as usize => {
419 let element = Tr::Value(crate::TypeInner::Scalar(scalar));
420 ctx.try_automatic_conversions_slice(&mut components, &element, ty_span)?;
421 let vec_ty = ctx.ensure_type_exists(scalar.to_inner_vector(rows));
422
423 let components = components
424 .chunks(rows as usize)
425 .map(|vec_components| {
426 ctx.append_expression(
427 crate::Expression::Compose {
428 ty: vec_ty,
429 components: Vec::from(vec_components),
430 },
431 Default::default(),
432 )
433 })
434 .collect::<Result<Vec<_>>>()?;
435
436 let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
437 columns,
438 rows,
439 scalar,
440 });
441 expr = crate::Expression::Compose { ty, components };
442 }
443
444 // Matrix constructor (by columns), partial
445 (
446 Components::Many {
447 mut components,
448 spans,
449 },
450 Constructor::PartialMatrix { columns, rows },
451 ) if components.len() == columns as usize => {
452 let consensus_scalar = ctx
453 .automatic_conversion_consensus(
454 Some(crate::Scalar::ABSTRACT_FLOAT),
455 &components,
456 )
457 .map_err(|index| {
458 Error::InvalidConstructorComponentType(spans[index], index as i32)
459 })?;
460
461 let component_ty = crate::TypeInner::Vector {
462 size: rows,
463 scalar: consensus_scalar,
464 };
465 ctx.try_automatic_conversions_slice(
466 &mut components,
467 &Tr::Value(component_ty),
468 ty_span,
469 )?;
470
471 let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
472 columns,
473 rows,
474 scalar: consensus_scalar,
475 });
476 expr = crate::Expression::Compose { ty, components };
477 }
478
479 // Matrix constructor (by columns), type given
480 (
481 Components::Many { mut components, .. },
482 Constructor::Type((
483 ty,
484 &crate::TypeInner::Matrix {
485 columns,
486 rows,
487 scalar,
488 },
489 )),
490 ) if components.len() == columns as usize => {
491 let component_ty = crate::TypeInner::Vector { size: rows, scalar };
492 ctx.try_automatic_conversions_slice(
493 &mut components,
494 &Tr::Value(component_ty),
495 ty_span,
496 )?;
497 expr = crate::Expression::Compose { ty, components };
498 }
499
500 // Array constructor - infer type
501 (components, Constructor::PartialArray) => {
502 let mut components = components.into_components_vec();
503 if let Ok(consensus_scalar) = ctx.automatic_conversion_consensus(None, &components)
504 {
505 // Note that this will *not* necessarily convert all the
506 // components to the same type! The `automatic_conversion_consensus`
507 // method only considers the parameters' leaf scalar
508 // types; the parameters themselves could be any mix of
509 // vectors, matrices, and scalars.
510 //
511 // But *if* it is possible for this array construction
512 // expression to be well-typed at all, then all the
513 // parameters must have the same type constructors (vec,
514 // matrix, scalar) applied to their leaf scalars, so
515 // reconciling their scalars is always the right thing to
516 // do. And if this array construction is not well-typed,
517 // these conversions will not make it so, and we can let
518 // validation catch the error.
519 ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;
520 } else {
521 // There's no consensus scalar. Emit the `Compose`
522 // expression anyway, and let validation catch the problem.
523 }
524
525 let base = ctx.register_type(components[0])?;
526
527 ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {
528 let crate::proc::LayoutErrorInner::TooLarge = err.inner else {
529 unreachable!("unexpected layout error: {err:?}");
530 };
531 // This error could be for any type that was pending layout. Lots of
532 // type definitions don't get spans, so the error message may not be
533 // very useful.
534 Box::new(Error::TypeTooLarge {
535 span: ctx.module.types.get_span(err.ty),
536 })
537 })?;
538 let stride = ctx.layouter[base].to_stride();
539
540 let inner = crate::TypeInner::Array {
541 base,
542 size: crate::ArraySize::Constant(
543 NonZeroU32::new(u32::try_from(components.len()).unwrap()).unwrap(),
544 ),
545 stride,
546 };
547 let ty = ctx.ensure_type_exists(inner);
548
549 expr = crate::Expression::Compose { ty, components };
550 }
551
552 // Array constructor, explicit type.
553 (
554 components,
555 Constructor::Type((ty, inner @ &crate::TypeInner::Array { base, .. })),
556 ) if inner.is_constructible(&ctx.module.types) => {
557 let mut components = components.into_components_vec();
558 ctx.try_automatic_conversions_slice(&mut components, &Tr::Handle(base), ty_span)?;
559 expr = crate::Expression::Compose { ty, components };
560 }
561
562 // Struct constructor
563 (
564 components,
565 Constructor::Type((ty, inner @ &crate::TypeInner::Struct { ref members, .. })),
566 ) if inner.is_constructible(&ctx.module.types) => {
567 let mut components = components.into_components_vec();
568 let struct_ty_span = ctx.module.types.get_span(ty);
569
570 // Make a vector of the members' type handles in advance, to
571 // avoid borrowing `members` from `ctx` while we generate
572 // new code.
573 let members: Vec<Handle<crate::Type>> = members.iter().map(|m| m.ty).collect();
574
575 for (component, &ty) in components.iter_mut().zip(&members) {
576 *component =
577 ctx.try_automatic_conversions(*component, &Tr::Handle(ty), struct_ty_span)?;
578 }
579 expr = crate::Expression::Compose { ty, components };
580 }
581
582 // ERRORS
583
584 // Bad conversion (type cast)
585 (
586 Components::One {
587 span, component, ..
588 },
589 Constructor::Type((
590 ty,
591 &(crate::TypeInner::Scalar { .. }
592 | crate::TypeInner::Vector { .. }
593 | crate::TypeInner::Matrix { .. }),
594 )),
595 ) => {
596 let component_ty = &ctx.typifier()[component];
597 let from_type = ctx.type_resolution_to_string(component_ty);
598 return Err(Box::new(Error::BadTypeCast {
599 span,
600 from_type,
601 to_type: ctx.type_to_string(ty),
602 }));
603 }
604
605 // Too many parameters for scalar constructor
606 (
607 Components::Many { spans, .. },
608 Constructor::Type((_, &crate::TypeInner::Scalar { .. })),
609 ) => {
610 let span = spans[1].until(spans.last().unwrap());
611 return Err(Box::new(Error::UnexpectedComponents(span)));
612 }
613
614 // Other types can't be constructed
615 _ => return Err(Box::new(Error::TypeNotConstructible(ty_span))),
616 }
617
618 let expr = ctx.append_expression(expr, span)?;
619 Ok(expr)
620 }
621}