use crate::front::wgsl::parse::ast; use crate::{Handle, Span}; use crate::front::wgsl::error::Error; use crate::front::wgsl::lower::{ExpressionContext, Lowerer, OutputContext}; use crate::proc::TypeResolution; enum ConcreteConstructorHandle { PartialVector { size: crate::VectorSize, }, PartialMatrix { columns: crate::VectorSize, rows: crate::VectorSize, }, PartialArray, Type(Handle), } impl ConcreteConstructorHandle { fn borrow<'a>(&self, module: &'a crate::Module) -> ConcreteConstructor<'a> { match *self { Self::PartialVector { size } => ConcreteConstructor::PartialVector { size }, Self::PartialMatrix { columns, rows } => { ConcreteConstructor::PartialMatrix { columns, rows } } Self::PartialArray => ConcreteConstructor::PartialArray, Self::Type(handle) => ConcreteConstructor::Type(handle, &module.types[handle].inner), } } } enum ConcreteConstructor<'a> { PartialVector { size: crate::VectorSize, }, PartialMatrix { columns: crate::VectorSize, rows: crate::VectorSize, }, PartialArray, Type(Handle, &'a crate::TypeInner), } impl ConcreteConstructorHandle { fn to_error_string(&self, ctx: ExpressionContext) -> String { match *self { Self::PartialVector { size } => { format!("vec{}", size as u32,) } Self::PartialMatrix { columns, rows } => { format!("mat{}x{}", columns as u32, rows as u32,) } Self::PartialArray => "array".to_string(), Self::Type(ty) => ctx.format_type(ty), } } } enum ComponentsHandle<'a> { None, One { component: Handle, span: Span, ty: &'a TypeResolution, }, Many { components: Vec>, spans: Vec, first_component_ty: &'a TypeResolution, }, } impl<'a> ComponentsHandle<'a> { fn borrow(self, module: &'a crate::Module) -> Components<'a> { match self { Self::None => Components::None, Self::One { component, span, ty, } => Components::One { component, span, ty_inner: ty.inner_with(&module.types), }, Self::Many { components, spans, first_component_ty, } => Components::Many { components, spans, first_component_ty_inner: first_component_ty.inner_with(&module.types), }, } } } enum Components<'a> { None, One { component: Handle, span: Span, ty_inner: &'a crate::TypeInner, }, Many { components: Vec>, spans: Vec, first_component_ty_inner: &'a crate::TypeInner, }, } impl Components<'_> { fn into_components_vec(self) -> Vec> { match self { Self::None => vec![], Self::One { component, .. } => vec![component], Self::Many { components, .. } => components, } } } impl<'source, 'temp> Lowerer<'source, 'temp> { /// Generate Naga IR for a type constructor expression. /// /// The `constructor` value represents the head of the constructor /// expression, which is at least a hint of which type is being built; if /// it's one of the `Partial` variants, we need to consider the argument /// types as well. /// /// This is used for [`Construct`] expressions, but also for [`Call`] /// expressions, once we've determined that the "callable" (in WGSL spec /// terms) is actually a type. /// /// [`Construct`]: ast::Expression::Construct /// [`Call`]: ast::Expression::Call pub fn construct( &mut self, span: Span, constructor: &ast::ConstructorType<'source>, ty_span: Span, components: &[Handle>], mut ctx: ExpressionContext<'source, '_, '_>, ) -> Result, Error<'source>> { let constructor_h = self.constructor(constructor, ctx.as_output())?; let components_h = match *components { [] => ComponentsHandle::None, [component] => { let span = ctx.ast_expressions.get_span(component); let component = self.expression(component, ctx.reborrow())?; ctx.grow_types(component)?; let ty = &ctx.typifier[component]; ComponentsHandle::One { component, span, ty, } } [component, ref rest @ ..] => { let span = ctx.ast_expressions.get_span(component); let component = self.expression(component, ctx.reborrow())?; let components = std::iter::once(Ok(component)) .chain( rest.iter() .map(|&component| self.expression(component, ctx.reborrow())), ) .collect::>()?; let spans = std::iter::once(span) .chain( rest.iter() .map(|&component| ctx.ast_expressions.get_span(component)), ) .collect(); ctx.grow_types(component)?; let ty = &ctx.typifier[component]; ComponentsHandle::Many { components, spans, first_component_ty: ty, } } }; let (components, constructor) = ( components_h.borrow(ctx.module), constructor_h.borrow(ctx.module), ); let expr = match (components, constructor) { // Empty constructor (Components::None, dst_ty) => { let ty = match dst_ty { ConcreteConstructor::Type(ty, _) => ty, _ => return Err(Error::TypeNotInferrable(ty_span)), }; return match ctx.create_zero_value_constant(ty) { Some(constant) => { Ok(ctx.interrupt_emitter(crate::Expression::Constant(constant), span)) } None => Err(Error::TypeNotConstructible(ty_span)), }; } // Scalar constructor & conversion (scalar -> scalar) ( Components::One { component, ty_inner: &crate::TypeInner::Scalar { .. }, .. }, ConcreteConstructor::Type(_, &crate::TypeInner::Scalar { kind, width }), ) => crate::Expression::As { expr: component, kind, convert: Some(width), }, // Vector conversion (vector -> vector) ( Components::One { component, ty_inner: &crate::TypeInner::Vector { size: src_size, .. }, .. }, ConcreteConstructor::Type( _, &crate::TypeInner::Vector { size: dst_size, kind: dst_kind, width: dst_width, }, ), ) if dst_size == src_size => crate::Expression::As { expr: component, kind: dst_kind, convert: Some(dst_width), }, // Vector conversion (vector -> vector) - partial ( Components::One { component, ty_inner: &crate::TypeInner::Vector { size: src_size, kind: src_kind, .. }, .. }, ConcreteConstructor::PartialVector { size: dst_size }, ) if dst_size == src_size => crate::Expression::As { expr: component, kind: src_kind, convert: None, }, // Matrix conversion (matrix -> matrix) ( Components::One { component, ty_inner: &crate::TypeInner::Matrix { columns: src_columns, rows: src_rows, .. }, .. }, ConcreteConstructor::Type( _, &crate::TypeInner::Matrix { columns: dst_columns, rows: dst_rows, width: dst_width, }, ), ) if dst_columns == src_columns && dst_rows == src_rows => crate::Expression::As { expr: component, kind: crate::ScalarKind::Float, convert: Some(dst_width), }, // Matrix conversion (matrix -> matrix) - partial ( Components::One { component, ty_inner: &crate::TypeInner::Matrix { columns: src_columns, rows: src_rows, .. }, .. }, ConcreteConstructor::PartialMatrix { columns: dst_columns, rows: dst_rows, }, ) if dst_columns == src_columns && dst_rows == src_rows => crate::Expression::As { expr: component, kind: crate::ScalarKind::Float, convert: None, }, // Vector constructor (splat) - infer type ( Components::One { component, ty_inner: &crate::TypeInner::Scalar { .. }, .. }, ConcreteConstructor::PartialVector { size }, ) => crate::Expression::Splat { size, value: component, }, // Vector constructor (splat) ( Components::One { component, ty_inner: &crate::TypeInner::Scalar { kind: src_kind, width: src_width, .. }, .. }, ConcreteConstructor::Type( _, &crate::TypeInner::Vector { size, kind: dst_kind, width: dst_width, }, ), ) if dst_kind == src_kind || dst_width == src_width => crate::Expression::Splat { size, value: component, }, // Vector constructor (by elements) ( Components::Many { components, first_component_ty_inner: &crate::TypeInner::Scalar { kind, width } | &crate::TypeInner::Vector { kind, width, .. }, .. }, ConcreteConstructor::PartialVector { size }, ) | ( Components::Many { components, first_component_ty_inner: &crate::TypeInner::Scalar { .. } | &crate::TypeInner::Vector { .. }, .. }, ConcreteConstructor::Type(_, &crate::TypeInner::Vector { size, width, kind }), ) => { let inner = crate::TypeInner::Vector { size, kind, width }; let ty = ctx.ensure_type_exists(inner); crate::Expression::Compose { ty, components } } // Matrix constructor (by elements) ( Components::Many { components, first_component_ty_inner: &crate::TypeInner::Scalar { width, .. }, .. }, ConcreteConstructor::PartialMatrix { columns, rows }, ) | ( Components::Many { components, first_component_ty_inner: &crate::TypeInner::Scalar { .. }, .. }, ConcreteConstructor::Type( _, &crate::TypeInner::Matrix { columns, rows, width, }, ), ) => { let vec_ty = ctx.ensure_type_exists(crate::TypeInner::Vector { width, kind: crate::ScalarKind::Float, size: rows, }); let components = components .chunks(rows as usize) .map(|vec_components| { ctx.naga_expressions.append( crate::Expression::Compose { ty: vec_ty, components: Vec::from(vec_components), }, Default::default(), ) }) .collect(); let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix { columns, rows, width, }); crate::Expression::Compose { ty, components } } // Matrix constructor (by columns) ( Components::Many { components, first_component_ty_inner: &crate::TypeInner::Vector { width, .. }, .. }, ConcreteConstructor::PartialMatrix { columns, rows }, ) | ( Components::Many { components, first_component_ty_inner: &crate::TypeInner::Vector { .. }, .. }, ConcreteConstructor::Type( _, &crate::TypeInner::Matrix { columns, rows, width, }, ), ) => { let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix { columns, rows, width, }); crate::Expression::Compose { ty, components } } // Array constructor - infer type (components, ConcreteConstructor::PartialArray) => { let components = components.into_components_vec(); let base = ctx.register_type(components[0])?; let size = crate::Constant { name: None, specialization: None, inner: crate::ConstantInner::Scalar { width: 4, value: crate::ScalarValue::Uint(components.len() as _), }, }; let inner = crate::TypeInner::Array { base, size: crate::ArraySize::Constant( ctx.module.constants.fetch_or_append(size, Span::UNDEFINED), ), stride: { self.layouter .update(&ctx.module.types, &ctx.module.constants) .unwrap(); self.layouter[base].to_stride() }, }; let ty = ctx.ensure_type_exists(inner); crate::Expression::Compose { ty, components } } // Array constructor (components, ConcreteConstructor::Type(ty, &crate::TypeInner::Array { .. })) => { let components = components.into_components_vec(); crate::Expression::Compose { ty, components } } // Struct constructor (components, ConcreteConstructor::Type(ty, &crate::TypeInner::Struct { .. })) => { crate::Expression::Compose { ty, components: components.into_components_vec(), } } // ERRORS // Bad conversion (type cast) (Components::One { span, ty_inner, .. }, _) => { let from_type = ctx.format_typeinner(ty_inner); return Err(Error::BadTypeCast { span, from_type, to_type: constructor_h.to_error_string(ctx.reborrow()), }); } // Too many parameters for scalar constructor ( Components::Many { spans, .. }, ConcreteConstructor::Type(_, &crate::TypeInner::Scalar { .. }), ) => { let span = spans[1].until(spans.last().unwrap()); return Err(Error::UnexpectedComponents(span)); } // Parameters are of the wrong type for vector or matrix constructor ( Components::Many { spans, .. }, ConcreteConstructor::Type( _, &crate::TypeInner::Vector { .. } | &crate::TypeInner::Matrix { .. }, ) | ConcreteConstructor::PartialVector { .. } | ConcreteConstructor::PartialMatrix { .. }, ) => { return Err(Error::InvalidConstructorComponentType(spans[0], 0)); } // Other types can't be constructed _ => return Err(Error::TypeNotConstructible(ty_span)), }; let expr = ctx.naga_expressions.append(expr, span); Ok(expr) } /// Build a Naga IR [`ConstantInner`] given a WGSL construction expression. /// /// Given `constructor`, representing the head of a WGSL [`type constructor /// expression`], and a slice of [`ast::Expression`] handles representing /// the constructor's arguments, build a Naga [`ConstantInner`] value /// representing the given value. /// /// If `constructor` is for a composite type, this may entail adding new /// [`Type`]s and [`Constant`]s to [`ctx.module`], if it doesn't already /// have what we need. /// /// If the arguments cannot be evaluated at compile time, return an error. /// /// [`ConstantInner`]: crate::ConstantInner /// [`type constructor expression`]: https://gpuweb.github.io/gpuweb/wgsl/#type-constructor-expr /// [`Function::expressions`]: ast::Function::expressions /// [`TranslationUnit::global_expressions`]: ast::TranslationUnit::global_expressions /// [`Type`]: crate::Type /// [`Constant`]: crate::Constant /// [`ctx.module`]: OutputContext::module pub fn const_construct( &mut self, span: Span, constructor: &ast::ConstructorType<'source>, components: &[Handle>], mut ctx: OutputContext<'source, '_, '_>, ) -> Result> { // TODO: Support zero values, splatting and inference. let constructor = self.constructor(constructor, ctx.reborrow())?; let c = match constructor { ConcreteConstructorHandle::Type(ty) => { let components = components .iter() .map(|&expr| self.constant(expr, ctx.reborrow())) .collect::>()?; crate::ConstantInner::Composite { ty, components } } _ => return Err(Error::ConstExprUnsupported(span)), }; Ok(c) } /// Build a Naga IR [`Type`] for `constructor` if there is enough /// information to do so. /// /// For `Partial` variants of [`ast::ConstructorType`], we don't know the /// component type, so in that case we return the appropriate `Partial` /// variant of [`ConcreteConstructorHandle`]. /// /// But for the other `ConstructorType` variants, we have everything we need /// to know to actually produce a Naga IR type. In this case we add to/find /// in [`ctx.module`] a suitable Naga `Type` and return a /// [`ConcreteConstructorHandle::Type`] value holding its handle. /// /// Note that constructing an [`Array`] type may require inserting /// [`Constant`]s as well as `Type`s into `ctx.module`, to represent the /// array's length. /// /// [`Type`]: crate::Type /// [`ctx.module`]: OutputContext::module /// [`Array`]: crate::TypeInner::Array /// [`Constant`]: crate::Constant fn constructor<'out>( &mut self, constructor: &ast::ConstructorType<'source>, mut ctx: OutputContext<'source, '_, 'out>, ) -> Result> { let c = match *constructor { ast::ConstructorType::Scalar { width, kind } => { let ty = ctx.ensure_type_exists(crate::TypeInner::Scalar { width, kind }); ConcreteConstructorHandle::Type(ty) } ast::ConstructorType::PartialVector { size } => { ConcreteConstructorHandle::PartialVector { size } } ast::ConstructorType::Vector { size, kind, width } => { let ty = ctx.ensure_type_exists(crate::TypeInner::Vector { size, kind, width }); ConcreteConstructorHandle::Type(ty) } ast::ConstructorType::PartialMatrix { rows, columns } => { ConcreteConstructorHandle::PartialMatrix { rows, columns } } ast::ConstructorType::Matrix { rows, columns, width, } => { let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix { columns, rows, width, }); ConcreteConstructorHandle::Type(ty) } ast::ConstructorType::PartialArray => ConcreteConstructorHandle::PartialArray, ast::ConstructorType::Array { base, size } => { let base = self.resolve_ast_type(base, ctx.reborrow())?; let size = match size { ast::ArraySize::Constant(expr) => { crate::ArraySize::Constant(self.constant(expr, ctx.reborrow())?) } ast::ArraySize::Dynamic => crate::ArraySize::Dynamic, }; self.layouter .update(&ctx.module.types, &ctx.module.constants) .unwrap(); let ty = ctx.ensure_type_exists(crate::TypeInner::Array { base, size, stride: self.layouter[base].to_stride(), }); ConcreteConstructorHandle::Type(ty) } ast::ConstructorType::Type(ty) => ConcreteConstructorHandle::Type(ty), }; Ok(c) } }