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}