///|
fn WgslIrEmitter::global_expression(
  self : WgslIrEmitter,
  handle : Handle,
) -> String raise WgslIrEmitError {
  let expression = match
    self.shader_module.global_expressions.items.get(handle.index()) {
    Some(value) => value
    None => raise MissingHandle("global expression \{handle.index()}")
  }
  self.emit_expression(expression, None)
}

///|
fn WgslIrEmitter::abstract_float_global_expression(
  self : WgslIrEmitter,
  handle : Handle,
) -> String raise WgslIrEmitError {
  self.with_preserved_abstract_float_literals().global_expression(handle)
}

///|
fn WgslIrEmitter::function_expression(
  self : WgslIrEmitter,
  function : Function,
  handle : Handle,
) -> String raise WgslIrEmitError {
  match self.function_named_expression_name(function, handle) {
    Some(name) => return name
    None => ()
  }
  match self.optional_function_expression_temporary_name(handle) {
    Some(name) => return name
    None => ()
  }
  self.function_expression_inline(function, handle)
}

///|
fn WgslIrEmitter::function_expression_inline(
  self : WgslIrEmitter,
  function : Function,
  handle : Handle,
) -> String raise WgslIrEmitError {
  let expression = match function.expressions.items.get(handle.index()) {
    Some(value) => value
    None => raise MissingHandle("function expression \{handle.index()}")
  }
  match expression {
    CallResult(_) | AtomicResult(_, _) =>
      raise Unsupported(
        "raw operation result \{handle.index()} without arena temporary name",
      )
    _ => self.emit_expression(expression, Some(function))
  }
}

///|
fn WgslIrEmitter::abstract_float_function_expression(
  self : WgslIrEmitter,
  function : Function,
  handle : Handle,
) -> String raise WgslIrEmitError {
  self
  .with_preserved_abstract_float_literals()
  .function_expression(function, handle)
}

///|
fn WgslIrEmitter::function_pointer_expression(
  self : WgslIrEmitter,
  function : Function,
  handle : Handle,
) -> String raise WgslIrEmitError {
  let expression = match function.expressions.items.get(handle.index()) {
    Some(value) => value
    None => raise MissingHandle("function pointer expression \{handle.index()}")
  }
  match expression {
    Load(inner) => self.function_pointer_expression(function, inner)
    _ => self.emit_expression(expression, Some(function))
  }
}

///|
fn WgslIrEmitter::load_expression(
  self : WgslIrEmitter,
  function : Function?,
  pointer : Handle,
) -> String raise WgslIrEmitError {
  if self.atomic_pointer_scalar_type(function, pointer) != None {
    return "atomicLoad(\{self.pointer_expression(function, pointer)})"
  }
  let expression = self.required_function_expression(function, pointer)
  if self.load_expression_requires_explicit_deref(function, pointer) {
    self.options.pointer_dereference_text(expression)
  } else {
    expression
  }
}

///|
fn WgslIrEmitter::load_expression_requires_explicit_deref(
  self : WgslIrEmitter,
  function : Function?,
  pointer : Handle,
) -> Bool raise WgslIrEmitError {
  let expression = match function {
    Some(function) =>
      match function.expressions.items.get(pointer.index()) {
        Some(expression) => expression
        None =>
          raise MissingHandle("function load expression \{pointer.index()}")
      }
    None =>
      match self.shader_module.global_expressions.items.get(pointer.index()) {
        Some(expression) => expression
        None => raise MissingHandle("global load expression \{pointer.index()}")
      }
  }
  match expression {
    Access(_, _) | AccessIndex(_, _) | Component(_, _) | Swizzle(_, _, _) =>
      false
    Load(inner) => self.load_expression_requires_explicit_deref(function, inner)
    _ =>
      match self.expression_type(function, pointer) {
        Some(ty) =>
          match self.shader_module.types.items.get(ty.index()) {
            Some(type_) =>
              match type_.inner {
                Pointer(_, _) => true
                _ => false
              }
            None => false
          }
        None => false
      }
  }
}

///|
fn WgslIrEmitter::access_base_expression(
  self : WgslIrEmitter,
  function : Function?,
  base : Handle,
) -> String raise WgslIrEmitError {
  let expression = self.required_function_expression(function, base)
  if self.expression_is_pointer(function, base) {
    return "(*\{expression})"
  }
  let needs_parens = match function {
    Some(function) =>
      match function.expressions.items.get(base.index()) {
        Some(Load(inner)) =>
          self.load_expression_requires_explicit_deref(Some(function), inner)
        _ => false
      }
    None =>
      match self.shader_module.global_expressions.items.get(base.index()) {
        Some(Load(inner)) =>
          self.load_expression_requires_explicit_deref(None, inner)
        _ => false
      }
  }
  if needs_parens {
    "(\{expression})"
  } else {
    expression
  }
}

///|
fn WgslIrEmitter::emit_expression(
  self : WgslIrEmitter,
  expression : Expression,
  function : Function?,
) -> String raise WgslIrEmitError {
  match expression {
    Literal(literal) => self.literal(literal)
    Constant(handle) => self.constant_expression(handle)
    Override(handle) => self.override_name(handle)
    ZeroValue(ty) => "\{self.type_name(ty)}()"
    Compose(ty, components) => self.compose_expression(ty, components, function)
    Splat(size, inner) => self.splat_expression(function, size, inner)
    Access(base, index) =>
      match
        self.static_access_index_expression_by_index_expression(
          function, base, index,
        ) {
        Some(value) => value
        None =>
          "\{self.access_base_expression(function, base)}[\{self.access_index_operand_expression(function, index)}]"
      }
    AccessIndex(base, index) =>
      self.access_index_expression(function, base, index)
    Component(base, component) =>
      self.component_expression(function, base, component)
    Swizzle(_, base, components) =>
      "\{self.access_base_expression(function, base)}.\{self.swizzle_components(components)}"
    FunctionArgument(index) => self.function_argument_name(function, index)
    GlobalVariable(handle) => self.global_variable_name(handle)
    LocalVariable(handle) => self.local_variable_name(function, handle)
    Load(pointer) => self.load_expression(function, pointer)
    AddressOf(_, target) =>
      match function {
        Some(func) =>
          self.options.address_of_text(
            self.function_pointer_expression(func, target),
          )
        None => raise Unsupported("global address-of expression")
      }
    AtomicCall(atomic_function, arguments) =>
      self.atomic_call_expression(function, atomic_function, arguments)
    ImageSample(
      image,
      sampler,
      gather,
      coordinate,
      array_index,
      offset,
      level,
      depth_ref,
      clamp_to_edge
    ) =>
      self.image_sample_expression(
        function, image, sampler, gather, coordinate, array_index, offset, level,
        depth_ref, clamp_to_edge,
      )
    ImageLoad(image, coordinate, array_index, sample, level) =>
      self.image_load_expression(
        function, image, coordinate, array_index, sample, level,
      )
    ImageQuery(image, query) =>
      self.image_query_expression(function, image, query)
    ArrayLength(pointer) =>
      "arrayLength(\{self.pointer_builtin_argument_expression(function, pointer)})"
    RayQueryInitialize(query, acceleration, ray_desc) =>
      "rayQueryInitialize(\{self.required_function_expression(function, query)}, \{self.required_function_expression(function, acceleration)}, \{self.required_function_expression(function, ray_desc)})"
    RayQueryProceed(query) =>
      "rayQueryProceed(\{self.required_function_expression(function, query)})"
    RayQueryGenerateIntersection(query, intersection_t) =>
      "rayQueryGenerateIntersection(\{self.required_function_expression(function, query)}, \{self.required_function_expression(function, intersection_t)})"
    RayQueryConfirmIntersection(query) =>
      "rayQueryConfirmIntersection(\{self.required_function_expression(function, query)})"
    RayQueryTerminate(query) =>
      "rayQueryTerminate(\{self.required_function_expression(function, query)})"
    RayQueryVertexPositions(query, committed) =>
      self.ray_query_vertex_positions_expression(function, query, committed)
    RayQueryGetIntersection(query, committed) =>
      self.ray_query_get_intersection_expression(function, query, committed)
    SubgroupCall(subgroup_function, arguments) =>
      "\{wgsl_ir_subgroup_function_name(subgroup_function)}(\{self.expression_list(arguments, function)})"
    CooperativeLoad(columns, rows, role, data) =>
      self.cooperative_load_expression(function, columns, rows, role, data)
    CooperativeMultiplyAdd(a, b, c) =>
      "coopMultiplyAdd(\{self.required_function_expression(function, a)}, \{self.required_function_expression(function, b)}, \{self.required_function_expression(function, c)})"
    WorkGroupUniformLoad(pointer) =>
      "workgroupUniformLoad(\{self.required_function_expression(function, pointer)})"
    Unary(op, expr) => self.unary_expression(function, op, expr)
    Binary(op, left, right) => self.binary_expression(function, op, left, right)
    Select(condition, accept, reject) =>
      self.select_expression(function, condition, accept, reject)
    Bitcast(ty, expr) =>
      "bitcast<\{self.type_name(ty)}>(\{self.required_function_expression(function, expr)})"
    As(expr, kind, width) => self.as_expression(function, expr, kind, width)
    Derivative(axis, control, expr) =>
      "\{wgsl_ir_derivative_function_name(axis, control)}(\{self.required_function_expression(function, expr)})"
    FunctionCall(handle, arguments) =>
      "\{self.function_name(handle)}(\{self.function_call_argument_list(handle, arguments, function)})"
    Math(math_function, a, b, c, d) =>
      self.math_expression(function, math_function, a, b, c, d)
    Relational(relational_function, expr) =>
      "\{self.relational_function_name(relational_function)}(\{self.required_function_expression(function, expr)})"
    CallResult(_) => raise Unsupported("raw call result")
    _ => raise Unsupported("expression kind")
  }
}

///|
fn WgslIrEmitter::as_expression(
  self : WgslIrEmitter,
  function : Function?,
  expr : Handle,
  kind : ScalarKind,
  width : Int?,
) -> String raise WgslIrEmitError {
  let scalar = Scalar::Scalar(kind, width.unwrap_or(4))
  let result_ty = match
    self.as_expression_result_type(
      self.expression_type(function, expr),
      kind,
      width.unwrap_or(4),
    ) {
    Some(ty) => ty
    None => raise Unsupported("invalid as expression result type")
  }
  let callee = match self.shader_module.types.get(result_ty) {
    Some(type_) =>
      match type_.inner {
        Vector(size, _) => "vec\{size.width()}<\{self.scalar_name(scalar)}>"
        Scalar(_) => self.scalar_name(scalar)
        _ => raise Unsupported("invalid as expression result type")
      }
    None => raise Unsupported("invalid as expression result type")
  }
  "\{callee}(\{self.required_function_expression(function, expr)})"
}

///|
fn WgslIrEmitter::math_expression(
  self : WgslIrEmitter,
  function : Function?,
  math_function : MathFunction,
  a : Handle,
  b : Handle?,
  c : Handle?,
  d : Handle?,
) -> String raise WgslIrEmitError {
  match self.folded_float_math_expression(function, math_function, a, b, c, d) {
    Some(value) => value
    None =>
      "\{self.math_function_name(math_function)}(\{self.math_argument_list(math_function, a, b, c, d, function)})"
  }
}

///|
fn WgslIrEmitter::folded_float_math_expression(
  self : WgslIrEmitter,
  function : Function?,
  math_function : MathFunction,
  a : Handle,
  b : Handle?,
  c : Handle?,
  d : Handle?,
) -> String? raise WgslIrEmitError {
  guard self.options.fold_numeric_constant_expressions() else { return None }
  guard b == None && c == None && d == None else { return None }
  guard math_function == Sqrt else { return None }
  match self.eval_float_expression_with_retained_constants(function, a) {
    Some(value) => self.float_value_literal_for_width(value.sqrt(), 4)
    None =>
      match self.eval_float_expression_for_writer(function, a) {
        Some(value) => self.float_value_literal_for_width(value.sqrt(), 4)
        None => None
      }
  }
}

///|
fn WgslIrEmitter::binary_expression(
  self : WgslIrEmitter,
  function : Function?,
  op : BinaryOperator,
  left : Handle,
  right : Handle,
) -> String raise WgslIrEmitError {
  match self.folded_float_vector_binary_expression(function, op, left, right) {
    Some(value) => return value
    None => ()
  }
  let left_text = self.binary_operand_expression(
    function, op, left, right, false,
  )
  let right_text = self.binary_operand_expression(
    function, op, right, left, true,
  )
  "(\{left_text} \{self.binary_operator(op)} \{right_text})"
}

///|
fn WgslIrEmitter::folded_float_vector_binary_expression(
  self : WgslIrEmitter,
  function : Function?,
  op : BinaryOperator,
  left : Handle,
  right : Handle,
) -> String? raise WgslIrEmitError {
  guard self.options.fold_numeric_constant_expressions() else { return None }
  guard wgsl_ir_binary_operator_is_float_arithmetic(op) else { return None }
  guard self.eval_float_vector_binary_with_retained_constants(
      function, op, left, right,
    )
    is Some(values) else {
    return None
  }
  let shape = match
    self.binary_float_vector_result_shape(function, left, right) {
    Some(shape) => Some(shape)
    None => self.float_vector_shape_from_lane_count(values.length())
  }
  guard shape is Some((size, scalar)) else { return None }
  guard values.length() == size.width() else { return None }
  let out = StringBuilder::new()
  out.write_string("vec\{size.width()}<\{self.scalar_name(scalar)}>(")
  for index in 0.. 0 {
      out.write_string(", ")
    }
    match self.float_value_literal_for_width(values[index], scalar.width) {
      Some(value) => out.write_string(value)
      None => return None
    }
  }
  out.write_string(")")
  Some(out.to_string())
}

///|
fn WgslIrEmitter::float_vector_shape_from_lane_count(
  self : WgslIrEmitter,
  lanes : Int,
) -> (VectorSize, Scalar)? {
  ignore(self)
  let size = match lanes {
    2 => VectorSize::Bi
    3 => Tri
    4 => Quad
    _ => return None
  }
  Some((size, { kind: Float, width: 4 }))
}

///|
fn WgslIrEmitter::binary_float_vector_result_shape(
  self : WgslIrEmitter,
  function : Function?,
  left : Handle,
  right : Handle,
) -> (VectorSize, Scalar)? {
  match self.float_vector_expression_shape(function, left) {
    Some(shape) => return Some(shape)
    None => ()
  }
  self.float_vector_expression_shape(function, right)
}

///|
fn WgslIrEmitter::float_vector_expression_shape(
  self : WgslIrEmitter,
  function : Function?,
  handle : Handle,
) -> (VectorSize, Scalar)? {
  match self.expression_type(function, handle) {
    Some(ty_handle) =>
      match self.shader_module.types.items.get(ty_handle.index()) {
        Some({ inner: Vector(size, scalar), .. }) =>
          match scalar.kind {
            Float =>
              Some(
                (
                  size,
                  {
                    kind: Float,
                    width: if scalar.width == 0 {
                      4
                    } else {
                      scalar.width
                    },
                  },
                ),
              )
            AbstractFloat => Some((size, { kind: Float, width: 4 }))
            _ => None
          }
        _ => None
      }
    None => None
  }
}

///|
fn WgslIrEmitter::binary_operand_expression(
  self : WgslIrEmitter,
  function : Function?,
  op : BinaryOperator,
  operand : Handle,
  sibling : Handle,
  is_right_operand : Bool,
) -> String raise WgslIrEmitError {
  if is_right_operand && (op == ShiftLeft || op == ShiftRight) {
    return self.required_function_expression(function, operand)
  }
  if wgsl_ir_binary_operator_uses_numeric_argument_context(op) &&
    self.options.contextualize_numeric_literals() {
    match self.binary_operand_scalar_context(function, operand, sibling) {
      Some(scalar) =>
        match self.contextual_scalar_literal(function, operand, scalar) {
          Some(text) => return text
          None => ()
        }
      None => ()
    }
  }
  self.required_function_expression(function, operand)
}

///|
fn WgslIrEmitter::binary_operand_scalar_context(
  self : WgslIrEmitter,
  function : Function?,
  operand : Handle,
  sibling : Handle,
) -> Scalar? {
  match self.expression_component_scalar(function, sibling) {
    Some(scalar) if !wgsl_ir_scalar_kind_is_abstract(scalar.kind) =>
      return Some(scalar)
    _ => ()
  }
  match self.expression_component_scalar(function, operand) {
    Some(scalar) if !wgsl_ir_scalar_kind_is_abstract(scalar.kind) =>
      Some(scalar)
    _ => None
  }
}

///|
fn wgsl_ir_binary_operator_is_float_arithmetic(op : BinaryOperator) -> Bool {
  match op {
    Add | Subtract | Multiply | Divide | Modulo => true
    _ => false
  }
}

///|
fn wgsl_ir_binary_operator_uses_numeric_argument_context(
  op : BinaryOperator,
) -> Bool {
  match op {
    Add
    | Subtract
    | Multiply
    | Divide
    | Modulo
    | Equal
    | NotEqual
    | Less
    | LessEqual
    | Greater
    | GreaterEqual
    | And
    | ExclusiveOr
    | InclusiveOr
    | ShiftLeft
    | ShiftRight => true
    _ => false
  }
}

///|
fn WgslIrEmitter::select_expression(
  self : WgslIrEmitter,
  function : Function?,
  condition : Handle,
  accept : Handle,
  reject : Handle,
) -> String raise WgslIrEmitError {
  let scalar_context = if self.options.contextualize_numeric_literals() {
    self.select_scalar_context(function, accept, reject)
  } else {
    None
  }
  let reject_text = self.contextual_argument_expression(
    function, reject, scalar_context,
  )
  let accept_text = self.contextual_argument_expression(
    function, accept, scalar_context,
  )
  let condition_text = self.required_function_expression(function, condition)
  "select(\{reject_text}, \{accept_text}, \{condition_text})"
}

///|
fn WgslIrEmitter::select_scalar_context(
  self : WgslIrEmitter,
  function : Function?,
  accept : Handle,
  reject : Handle,
) -> Scalar? {
  let accept_scalar = self.expression_component_scalar(function, accept)
  let reject_scalar = self.expression_component_scalar(function, reject)
  match (accept_scalar, reject_scalar) {
    (Some(scalar), _) if !wgsl_ir_scalar_kind_is_abstract(scalar.kind) =>
      Some(scalar)
    (_, Some(scalar)) if !wgsl_ir_scalar_kind_is_abstract(scalar.kind) =>
      Some(scalar)
    (Some(left), Some(right)) if wgsl_ir_select_abstract_operands_are_float(
        left.kind,
        right.kind,
      ) => Some(Scalar(Float, 4))
    (Some(scalar), _) => Some(scalar)
    (_, Some(scalar)) => Some(scalar)
    _ => None
  }
}

///|
fn wgsl_ir_select_abstract_operands_are_float(
  left : ScalarKind,
  right : ScalarKind,
) -> Bool {
  (left == AbstractFloat && right == AbstractInt) ||
  (left == AbstractInt && right == AbstractFloat) ||
  (left == AbstractFloat && right == AbstractFloat)
}

///|
fn wgsl_ir_scalar_kind_is_abstract(kind : ScalarKind) -> Bool {
  match kind {
    AbstractInt | AbstractFloat => true
    _ => false
  }
}

///|
fn WgslIrEmitter::function_call_argument_list(
  self : WgslIrEmitter,
  callee : Handle,
  arguments : Array[Handle],
  function : Function?,
) -> String raise WgslIrEmitError {
  let out = StringBuilder::new()
  let callee_function = self.shader_module.functions.items.get(callee.index())
  for i in 0.. 0 {
      out.write_string(", ")
    }
    let scalar_context = if self.options.contextualize_numeric_literals() {
      match callee_function {
        Some(callee_function) =>
          match callee_function.arguments.get(i) {
            Some(argument) => self.type_component_scalar(argument.ty)
            None => None
          }
        None => None
      }
    } else {
      None
    }
    out.write_string(
      self.function_call_argument_expression(
        function,
        callee_function,
        i,
        arguments[i],
        scalar_context,
      ),
    )
  }
  out.to_string()
}

///|
fn WgslIrEmitter::function_call_argument_expression(
  self : WgslIrEmitter,
  function : Function?,
  callee_function : Function?,
  index : Int,
  argument : Handle,
  scalar_context : Scalar?,
) -> String raise WgslIrEmitError {
  match callee_function {
    Some(callee_function) =>
      match callee_function.arguments.get(index) {
        Some(parameter) if self.type_is_pointer(parameter.ty) =>
          return self.pointer_expression(function, argument)
        _ => ()
      }
    None => ()
  }
  self.contextual_argument_expression(function, argument, scalar_context)
}

///|
fn WgslIrEmitter::contextual_argument_expression(
  self : WgslIrEmitter,
  function : Function?,
  argument : Handle,
  scalar_context : Scalar?,
) -> String raise WgslIrEmitError {
  match scalar_context {
    Some(scalar) =>
      match self.contextual_scalar_literal(function, argument, scalar) {
        Some(value) => value
        None => self.required_function_expression(function, argument)
      }
    None => self.required_function_expression(function, argument)
  }
}

///|
fn WgslIrEmitter::expression_component_scalar(
  self : WgslIrEmitter,
  function : Function?,
  handle : Handle,
) -> Scalar? {
  match self.expression_type(function, handle) {
    Some(ty_handle) => self.type_component_scalar(ty_handle)
    None => None
  }
}

///|
fn WgslIrEmitter::type_component_scalar(
  self : WgslIrEmitter,
  ty : Handle,
) -> Scalar? {
  match self.shader_module.types.items.get(ty.index()) {
    Some(type_) =>
      match type_.inner {
        Scalar(scalar) | Vector(_, scalar) | Matrix(_, _, scalar) =>
          Some(scalar)
        _ => None
      }
    None => None
  }
}

///|
fn WgslIrEmitter::splat_expression(
  self : WgslIrEmitter,
  function : Function?,
  size : VectorSize,
  inner : Handle,
) -> String raise WgslIrEmitError {
  let value = match
    self.constructor_component_folded_expression(function, inner) {
    Some(value) => value
    None => self.required_function_expression(function, inner)
  }
  "vec\{size.width()}(\{value})"
}

///|
fn WgslIrEmitter::compose_expression(
  self : WgslIrEmitter,
  ty : Handle,
  components : Array[Handle],
  function : Function?,
) -> String raise WgslIrEmitError {
  match self.folded_scalar_constructor_expression(ty, components, function) {
    Some(value) => return value
    None => ()
  }
  "\{self.compose_constructor_name_for_components(ty, components, function)}(\{self.compose_component_list(ty, components, function)})"
}

///|
fn WgslIrEmitter::compose_component_list(
  self : WgslIrEmitter,
  ty : Handle,
  components : Array[Handle],
  function : Function?,
) -> String raise WgslIrEmitError {
  match self.matrix_scalar_component_list(ty, components, function) {
    Some(value) => value
    None => {
      let out = StringBuilder::new()
      let scalar_context = if self.options.contextualize_numeric_literals() {
        self.compose_component_scalar_context(ty, components, function)
      } else {
        None
      }
      for i in 0.. 0 {
          out.write_string(", ")
        }
        out.write_string(
          self.compose_component_expression(
            function,
            components[i],
            scalar_context,
          ),
        )
      }
      out.to_string()
    }
  }
}

///|
fn WgslIrEmitter::contextual_scalar_literal(
  self : WgslIrEmitter,
  function : Function?,
  operand : Handle,
  scalar : Scalar,
) -> String? raise WgslIrEmitError {
  match function {
    Some(function) =>
      match self.function_named_expression_name(function, operand) {
        Some(_) => return None
        None => ()
      }
    None => ()
  }
  if self.options.fold_numeric_constant_expressions() {
    match self.folded_expression_for_scalar_context(function, operand, scalar) {
      Some(value) => return Some(value)
      None => ()
    }
  }
  let expression = match function {
    Some(function) => function.expressions.items.get(operand.index())
    None => self.shader_module.global_expressions.items.get(operand.index())
  }
  match expression {
    Some(Literal(literal)) => self.literal_for_scalar_context(literal, scalar)
    Some(Unary(Negate, inner)) =>
      match self.required_expression(function, inner) {
        Literal(literal) =>
          match wgsl_ir_negate_literal(literal) {
            Some(negated) => self.literal_for_scalar_context(negated, scalar)
            None => None
          }
        _ => None
      }
    Some(Splat(size, inner)) =>
      match self.contextual_scalar_literal(function, inner, scalar) {
        Some(value) => Some("vec\{size.width()}(\{value})")
        None => None
      }
    Some(Compose(ty, components)) => {
      match
        self.folded_scalar_constructor_expression(ty, components, function) {
        Some(value) => return Some(value)
        None => ()
      }
      let constructor_name = match
        self.contextual_multi_component_vector_constructor_name(
          ty, components, scalar,
        ) {
        Some(name) => name
        None =>
          self.compose_constructor_name_for_components(ty, components, function)
      }
      let out = StringBuilder::new()
      for i in 0.. 0 {
          out.write_string(", ")
        }
        out.write_string(
          self.compose_component_expression(
            function,
            components[i],
            Some(scalar),
          ),
        )
      }
      Some("\{constructor_name}(\{out.to_string()})")
    }
    _ => None
  }
}

///|
fn WgslIrEmitter::contextual_multi_component_vector_constructor_name(
  self : WgslIrEmitter,
  ty : Handle,
  components : Array[Handle],
  scalar : Scalar,
) -> String? raise WgslIrEmitError {
  guard components.length() > 1 else { return None }
  guard !wgsl_ir_scalar_kind_is_abstract(scalar.kind) else { return None }
  match self.shader_module.types.items.get(ty.index()) {
    Some({ inner: Vector(size, existing), .. }) =>
      if wgsl_ir_scalar_kind_is_abstract(existing.kind) {
        Some("vec\{size.width()}<\{self.scalar_name(scalar)}>")
      } else {
        None
      }
    Some(_) => None
    None => raise MissingHandle("type \{ty.index()}")
  }
}

///|
fn WgslIrEmitter::folded_expression_for_scalar_context(
  self : WgslIrEmitter,
  function : Function?,
  operand : Handle,
  scalar : Scalar,
) -> String? raise WgslIrEmitError {
  match scalar.kind {
    Float | AbstractFloat =>
      match self.required_expression(function, operand) {
        Constant(handle) =>
          match self.eval_constant_float_expression_for_writer(handle) {
            Some(value) =>
              self.float_value_literal_for_width(value, scalar.width)
            None => None
          }
        _ =>
          match
            self.eval_float_expression_with_retained_constants(
              function, operand,
            ) {
            Some(value) =>
              self.float_value_literal_for_width(value, scalar.width)
            None =>
              if self.expression_tree_contains_folded_local_constant(
                  function, operand,
                ) {
                None
              } else {
                match self.eval_float_expression_for_writer(function, operand) {
                  Some(value) =>
                    self.float_value_literal_for_width(value, scalar.width)
                  None => None
                }
              }
          }
      }
    Uint =>
      match self.eval_integer_expression_for_writer(function, operand) {
        Some(value) => self.integer_value_literal_for_kind(value, Uint)
        None => None
      }
    Sint =>
      match self.eval_integer_expression_for_writer(function, operand) {
        Some(value) => self.integer_value_literal_for_kind(value, Sint)
        None => None
      }
    AbstractInt =>
      match self.eval_integer_expression_for_writer(function, operand) {
        Some(value) => self.integer_value_literal_for_kind(value, AbstractInt)
        None => None
      }
    _ => None
  }
}

///|
fn WgslIrEmitter::literal_for_scalar_context(
  self : WgslIrEmitter,
  literal : Literal,
  scalar : Scalar,
) -> String? {
  match scalar.kind {
    Float | AbstractFloat =>
      match wgsl_ir_float_value_from_numeric_literal(literal) {
        Some(value) => self.float_value_literal_for_width(value, scalar.width)
        None => None
      }
    Uint =>
      match literal {
        AbstractInt(value) =>
          self.integer_value_literal_for_kind(Abstract(value), Uint)
        U32(value) => self.integer_value_literal_for_kind(Unsigned(value), Uint)
        I32(value) => self.integer_value_literal_for_kind(Signed(value), Uint)
        _ => None
      }
    Sint =>
      match literal {
        AbstractInt(value) =>
          self.integer_value_literal_for_kind(Abstract(value), Sint)
        I32(value) => self.integer_value_literal_for_kind(Signed(value), Sint)
        U32(value) => self.integer_value_literal_for_kind(Unsigned(value), Sint)
        _ => None
      }
    AbstractInt =>
      match literal {
        AbstractInt(value) =>
          self.integer_value_literal_for_kind(Abstract(value), AbstractInt)
        I32(value) =>
          self.integer_value_literal_for_kind(Signed(value), AbstractInt)
        U32(value) =>
          self.integer_value_literal_for_kind(Unsigned(value), AbstractInt)
        _ => None
      }
    _ => None
  }
}

///|
fn WgslIrEmitter::matrix_scalar_component_list(
  self : WgslIrEmitter,
  ty : Handle,
  components : Array[Handle],
  function : Function?,
) -> String? raise WgslIrEmitError {
  if !self.options.expand_matrix_scalar_constructors() {
    return None
  }
  guard self.shader_module.types.items.get(ty.index()) is Some(matrix_ty) else {
    raise MissingHandle("type \{ty.index()}")
  }
  guard matrix_ty.inner is Matrix(columns, rows, scalar) else { return None }
  let columns_len = columns.width()
  let rows_len = rows.width()
  if components.length() != columns_len * rows_len {
    return None
  }
  for component in components {
    if !self.expression_is_scalar_type(function, component, scalar) {
      return None
    }
  }
  let out = StringBuilder::new()
  for column_index in 0.. 0 {
      out.write_string(", ")
    }
    let column_components : Array[Handle] = []
    for row_index in 0..(\{self.expression_list(column_components, function)})",
    )
  }
  Some(out.to_string())
}

///|
fn WgslIrEmitter::unary_expression(
  self : WgslIrEmitter,
  function : Function?,
  op : UnaryOperator,
  expr : Handle,
) -> String raise WgslIrEmitError {
  let inner = self.required_expression(function, expr)
  match (op, inner) {
    (Negate, Literal(literal)) => "-\{self.literal(literal)}"
    _ =>
      "\{self.unary_operator(op)}(\{self.required_function_expression(function, expr)})"
  }
}

///|
fn WgslIrEmitter::required_expression(
  self : WgslIrEmitter,
  function : Function?,
  handle : Handle,
) -> Expression raise WgslIrEmitError {
  match function {
    Some(function) =>
      match function.expressions.items.get(handle.index()) {
        Some(expression) => expression
        None => raise MissingHandle("function expression \{handle.index()}")
      }
    None =>
      match self.shader_module.global_expressions.items.get(handle.index()) {
        Some(expression) => expression
        None => raise MissingHandle("global expression \{handle.index()}")
      }
  }
}

///|
fn WgslIrEmitter::optional_expression(
  self : WgslIrEmitter,
  function : Function?,
  handle : Handle,
) -> Expression? {
  match function {
    Some(function) => function.expressions.items.get(handle.index())
    None => self.shader_module.global_expressions.items.get(handle.index())
  }
}

///|
fn WgslIrEmitter::image_sample_expression(
  self : WgslIrEmitter,
  function : Function?,
  image : Handle,
  sampler : Handle,
  gather : SwizzleComponent?,
  coordinate : Handle,
  array_index : Handle?,
  offset : Handle?,
  level : SampleLevel,
  depth_ref : Handle?,
  clamp_to_edge : Bool,
) -> String raise WgslIrEmitError {
  match gather {
    Some(component) =>
      return self.image_gather_expression(
        function, image, sampler, component, coordinate, array_index, offset, depth_ref,
      )
    None => ()
  }
  if clamp_to_edge {
    if array_index != None || offset != None || depth_ref != None {
      raise Unsupported("textureSampleBaseClampToEdge advanced arguments")
    }
    let callee = "textureSampleBaseClampToEdge"
    return "\{callee}(\{self.required_function_expression(function, image)}, \{self.required_function_expression(function, sampler)}, \{self.required_function_expression(function, coordinate)})"
  }
  let callee = match depth_ref {
    Some(_) =>
      match level {
        Auto => "textureSampleCompare"
        Zero => "textureSampleCompareLevel"
        _ => raise Unsupported("texture compare sampling level mode")
      }
    None =>
      match level {
        Auto => "textureSample"
        Exact(_) => "textureSampleLevel"
        Bias(_) => "textureSampleBias"
        Gradient(_, _) => "textureSampleGrad"
        Zero => "textureSampleLevel"
      }
  }
  let out = StringBuilder::new()
  out.write_string(
    "\{callee}(\{self.required_function_expression(function, image)}, \{self.required_function_expression(function, sampler)}, \{self.required_function_expression(function, coordinate)}",
  )
  match array_index {
    Some(index) =>
      out.write_string(
        ", \{self.required_function_expression(function, index)}",
      )
    None => ()
  }
  match level {
    Exact(handle) =>
      out.write_string(
        ", \{self.required_function_expression(function, handle)}",
      )
    Bias(handle) =>
      out.write_string(
        ", \{self.required_function_expression(function, handle)}",
      )
    Gradient(ddx, ddy) =>
      out.write_string(
        ", \{self.required_function_expression(function, ddx)}, \{self.required_function_expression(function, ddy)}",
      )
    Zero if depth_ref == None => out.write_string(", 0")
    _ => ()
  }
  match depth_ref {
    Some(handle) =>
      out.write_string(
        ", \{self.required_function_expression(function, handle)}",
      )
    None => ()
  }
  match offset {
    Some(handle) =>
      out.write_string(
        ", \{self.required_function_expression(function, handle)}",
      )
    None => ()
  }
  out.write_string(")")
  out.to_string()
}

///|
fn WgslIrEmitter::atomic_call_expression(
  self : WgslIrEmitter,
  function : Function?,
  atomic_function : AtomicFunction,
  arguments : Array[Handle],
) -> String raise WgslIrEmitError {
  guard arguments is [pointer, .. rest] else {
    raise Unsupported("atomic call without pointer")
  }
  let out = StringBuilder::new()
  let pointer_text = self.pointer_builtin_argument_expression(function, pointer)
  out.write_string(
    "\{self.atomic_function_name(atomic_function)}(\{pointer_text}",
  )
  for argument in rest {
    out.write_string(
      ", \{self.required_function_expression(function, argument)}",
    )
  }
  out.write_string(")")
  out.to_string()
}

///|
fn wgsl_ir_pointer_builtin_argument_text(text : String) -> String {
  if text.length() > 0 && text[0:1] == "&" {
    "(\{text})"
  } else {
    text
  }
}

///|
fn WgslIrEmitter::pointer_builtin_argument_expression(
  self : WgslIrEmitter,
  function : Function?,
  pointer : Handle,
) -> String raise WgslIrEmitError {
  wgsl_ir_pointer_builtin_argument_text(
    self.pointer_expression(function, pointer),
  )
}

///|
fn WgslIrEmitter::pointer_expression(
  self : WgslIrEmitter,
  function : Function?,
  pointer : Handle,
) -> String raise WgslIrEmitError {
  if self.expression_is_pointer(function, pointer) {
    self.required_function_expression(function, pointer)
  } else {
    self.options.address_of_text(
      self.required_function_expression(function, pointer),
    )
  }
}

///|
fn WgslIrEmitter::emit_cooperative_store_statement(
  self : WgslIrEmitter,
  out : StringBuilder,
  function : Function,
  target : Handle,
  data : CooperativeData,
  indent : Int,
) -> Unit raise WgslIrEmitError {
  let suffix = if data.row_major { "T" } else { "" }
  out.write_string(
    "\{wgsl_ir_indent(indent)}coopStore\{suffix}(\{self.function_expression(function, target)}, \{self.pointer_expression(Some(function), data.pointer)}, \{self.function_expression(function, data.stride)});\n",
  )
}

///|
fn WgslIrEmitter::cooperative_load_expression(
  self : WgslIrEmitter,
  function : Function?,
  columns : CooperativeSize,
  rows : CooperativeSize,
  role : CooperativeRole,
  data : CooperativeData,
) -> String raise WgslIrEmitError {
  let suffix = if data.row_major { "T" } else { "" }
  let scalar = match self.cooperative_data_scalar(function, data) {
    Some(scalar) => scalar
    None => raise Unsupported("cooperative load pointer scalar type")
  }
  "coopLoad\{suffix}>(\{self.pointer_expression(function, data.pointer)}, \{self.required_function_expression(function, data.stride)})"
}

///|
fn WgslIrEmitter::cooperative_data_scalar(
  self : WgslIrEmitter,
  function : Function?,
  data : CooperativeData,
) -> Scalar? {
  match self.expression_type(function, data.pointer) {
    Some(ty) => self.cooperative_pointer_scalar(ty)
    None => None
  }
}

///|
fn WgslIrEmitter::cooperative_pointer_scalar(
  self : WgslIrEmitter,
  ty : Handle,
) -> Scalar? {
  match self.shader_module.types.get(ty) {
    Some(type_) =>
      match type_.inner {
        Pointer(base, _) => self.cooperative_pointer_scalar(base)
        ValuePointer(_, scalar, _) => Some(scalar)
        Scalar(scalar) => Some(scalar)
        _ => None
      }
    None => None
  }
}

///|
fn WgslIrEmitter::image_gather_expression(
  self : WgslIrEmitter,
  function : Function?,
  image : Handle,
  sampler : Handle,
  component : SwizzleComponent,
  coordinate : Handle,
  array_index : Handle?,
  offset : Handle?,
  depth_ref : Handle?,
) -> String raise WgslIrEmitError {
  let callee = if depth_ref != None {
    "textureGatherCompare"
  } else {
    "textureGather"
  }
  let out = StringBuilder::new()
  out.write_string("\{callee}(")
  if depth_ref == None && !self.image_expression_is_depth(function, image) {
    out.write_string("\{wgsl_ir_gather_component_index(component)}, ")
  }
  out.write_string(
    "\{self.required_function_expression(function, image)}, \{self.required_function_expression(function, sampler)}, \{self.required_function_expression(function, coordinate)}",
  )
  match array_index {
    Some(handle) =>
      out.write_string(
        ", \{self.required_function_expression(function, handle)}",
      )
    None => ()
  }
  match depth_ref {
    Some(handle) =>
      out.write_string(
        ", \{self.required_function_expression(function, handle)}",
      )
    None => ()
  }
  match offset {
    Some(handle) =>
      out.write_string(
        ", \{self.required_function_expression(function, handle)}",
      )
    None => ()
  }
  out.write_string(")")
  out.to_string()
}

///|
fn WgslIrEmitter::image_expression_is_depth(
  self : WgslIrEmitter,
  function : Function?,
  image : Handle,
) -> Bool {
  match self.expression_type(function, image) {
    Some(ty) =>
      match self.shader_module.types.get(ty) {
        Some(type_) =>
          match type_.inner {
            Image(_, _, Depth(_)) => true
            _ => false
          }
        None => false
      }
    None => false
  }
}

///|
fn WgslIrEmitter::image_load_expression(
  self : WgslIrEmitter,
  function : Function?,
  image : Handle,
  coordinate : Handle,
  array_index : Handle?,
  sample : Handle?,
  level : Handle?,
) -> String raise WgslIrEmitError {
  let out = StringBuilder::new()
  out.write_string(
    "textureLoad(\{self.required_function_expression(function, image)}, \{self.required_function_expression(function, coordinate)}",
  )
  match array_index {
    Some(index) =>
      out.write_string(
        ", \{self.required_function_expression(function, index)}",
      )
    None => ()
  }
  match sample {
    Some(handle) =>
      out.write_string(
        ", \{self.required_function_expression(function, handle)}",
      )
    None => ()
  }
  match level {
    Some(handle) =>
      out.write_string(
        ", \{self.required_function_expression(function, handle)}",
      )
    None => ()
  }
  out.write_string(")")
  out.to_string()
}

///|
fn WgslIrEmitter::image_query_expression(
  self : WgslIrEmitter,
  function : Function?,
  image : Handle,
  query : ImageQuery,
) -> String raise WgslIrEmitError {
  match query {
    Size(level) => {
      let out = StringBuilder::new()
      out.write_string(
        "textureDimensions(\{self.required_function_expression(function, image)}",
      )
      match level {
        Some(handle) =>
          out.write_string(
            ", \{self.required_function_expression(function, handle)}",
          )
        None => ()
      }
      out.write_string(")")
      out.to_string()
    }
    NumLevels =>
      "textureNumLevels(\{self.required_function_expression(function, image)})"
    NumLayers =>
      "textureNumLayers(\{self.required_function_expression(function, image)})"
    NumSamples =>
      "textureNumSamples(\{self.required_function_expression(function, image)})"
  }
}

///|
fn WgslIrEmitter::ray_query_get_intersection_expression(
  self : WgslIrEmitter,
  function : Function?,
  query : Handle,
  committed : Bool,
) -> String raise WgslIrEmitError {
  let name = if committed {
    "rayQueryGetCommittedIntersection"
  } else {
    "rayQueryGetCandidateIntersection"
  }
  "\{name}(\{self.required_function_expression(function, query)})"
}

///|
fn WgslIrEmitter::ray_query_vertex_positions_expression(
  self : WgslIrEmitter,
  function : Function?,
  query : Handle,
  committed : Bool,
) -> String raise WgslIrEmitError {
  let name = if committed {
    "getCommittedHitVertexPositions"
  } else {
    "getCandidateHitVertexPositions"
  }
  "\{name}(\{self.required_function_expression(function, query)})"
}

///|
fn WgslIrEmitter::component_expression(
  self : WgslIrEmitter,
  function : Function?,
  base : Handle,
  component : SwizzleComponent,
) -> String raise WgslIrEmitError {
  let index = wgsl_ir_gather_component_index(component)
  let base_expression = self.access_base_expression(function, base)
  if self.vector_static_index_keeps_bracket(function, base) {
    "\{base_expression}[\{index}]"
  } else {
    "\{base_expression}.\{wgsl_ir_swizzle_component(index)}"
  }
}

///|
fn WgslIrEmitter::access_index_expression(
  self : WgslIrEmitter,
  function : Function?,
  base : Handle,
  index : Int,
) -> String raise WgslIrEmitError {
  match self.folded_static_constant_array_access(function, base, index) {
    Some(value) => return value
    None => ()
  }
  let base_expression = self.access_base_expression(function, base)
  match self.expression_type(function, base) {
    Some(ty_handle) => {
      let base_type_handle = self.deref_pointer_type_handle(ty_handle)
      match self.shader_module.types.items.get(base_type_handle.index()) {
        Some(ty) =>
          match ty.inner {
            Struct(members, _) | PredeclaredStruct(members, _) =>
              match members.get(index) {
                Some(_) => {
                  let member_name = self.required_emit_name(
                    wgsl_ir_emit_name_key2(
                      "member",
                      base_type_handle.index(),
                      index,
                    ),
                  )
                  "\{base_expression}.\{member_name}"
                }
                None => raise Unsupported("struct member index \{index}")
              }
            Vector(_, _) =>
              if self.vector_static_index_keeps_bracket(function, base) {
                "\{base_expression}[\{index}]"
              } else {
                "\{base_expression}.\{wgsl_ir_swizzle_component(index)}"
              }
            Matrix(_, _, _)
            | Array(_, _, _)
            | BindingArray(_, _)
            | ValuePointer(_, _, _) => "\{base_expression}[\{index}]"
            _ =>
              raise Unsupported(
                "access-index base type \{base_type_handle.index()}",
              )
          }
        None =>
          raise MissingHandle(
            "access-index base type \{base_type_handle.index()}",
          )
      }
    }
    None => raise Unsupported("access-index expression without base type")
  }
}

///|
fn WgslIrEmitter::vector_static_index_keeps_bracket(
  self : WgslIrEmitter,
  function : Function?,
  base : Handle,
) -> Bool {
  match function {
    Some(func) =>
      self.vector_static_index_base_is_global_matrix_column(func, base)
    None => false
  }
}

///|
fn WgslIrEmitter::vector_static_index_base_is_global_matrix_column(
  self : WgslIrEmitter,
  function : Function,
  base : Handle,
) -> Bool {
  match function.expressions.items.get(base.index()) {
    Some(Load(inner)) =>
      self.vector_static_index_base_is_global_matrix_column(function, inner)
    Some(AccessIndex(matrix, _)) =>
      self.expression_is_matrix_type(Some(function), matrix) &&
      wgsl_ir_function_named_expression_name(function, matrix) == None &&
      wgsl_ir_emit_expression_reads_global_binding(function, matrix)
    _ => false
  }
}

///|
fn WgslIrEmitter::expression_is_matrix_type(
  self : WgslIrEmitter,
  function : Function?,
  handle : Handle,
) -> Bool {
  match self.expression_type(function, handle) {
    Some(ty) =>
      match
        self.shader_module.types.items.get(
          self.deref_pointer_type_handle(ty).index(),
        ) {
        Some({ inner: Matrix(_, _, _), .. }) => true
        _ => false
      }
    None => false
  }
}

///|
fn wgsl_ir_emit_expression_reads_global_binding(
  function : Function,
  handle : Handle,
) -> Bool {
  match function.expressions.items.get(handle.index()) {
    Some(GlobalVariable(_)) => true
    Some(LocalVariable(local_handle)) =>
      match function.local_variables.items.get(local_handle.index()) {
        Some(local_var) =>
          if local_var.generated_temporary {
            match local_var.init {
              Some(init) =>
                wgsl_ir_emit_expression_reads_global_binding(function, init)
              None => false
            }
          } else {
            false
          }
        None => false
      }
    Some(Load(inner)) =>
      wgsl_ir_emit_expression_reads_global_binding(function, inner)
    Some(Access(base, index)) =>
      wgsl_ir_emit_expression_reads_global_binding(function, base) ||
      wgsl_ir_emit_expression_reads_global_binding(function, index)
    Some(AccessIndex(base, _))
    | Some(Component(base, _))
    | Some(Swizzle(_, base, _)) =>
      wgsl_ir_emit_expression_reads_global_binding(function, base)
    _ => false
  }
}

///|
fn WgslIrEmitter::folded_static_constant_array_access(
  self : WgslIrEmitter,
  function : Function?,
  base : Handle,
  index : Int,
) -> String? raise WgslIrEmitError {
  guard self.options.fold_numeric_constant_expressions() else { return None }
  guard index >= 0 else { return None }
  match self.required_expression(function, base) {
    Compose(ty, components) =>
      self.folded_static_array_constructor_access(
        function, ty, components, index,
      )
    Constant(constant_handle) =>
      self.folded_static_constant_array_constructor_access(
        constant_handle, index,
      )
    _ => None
  }
}

///|
fn WgslIrEmitter::folded_static_constant_array_constructor_access(
  self : WgslIrEmitter,
  constant_handle : Handle,
  index : Int,
) -> String? raise WgslIrEmitError {
  guard self.shader_module.constants.items.get(constant_handle.index())
    is Some(constant) else {
    raise MissingHandle("constant \{constant_handle.index()}")
  }
  guard self.should_inline_constant_expression(constant) else { return None }
  guard self.shader_module.global_expressions.items.get(constant.init.index())
    is Some(Compose(ty, components)) else {
    return None
  }
  self.folded_static_array_constructor_access(None, ty, components, index)
}

///|
fn WgslIrEmitter::folded_static_array_constructor_access(
  self : WgslIrEmitter,
  function : Function?,
  ty : Handle,
  components : Array[Handle],
  index : Int,
) -> String? raise WgslIrEmitError {
  guard self.shader_module.types.items.get(ty.index())
    is Some({ inner: Array(_, _, _), .. }) else {
    return None
  }
  match components.get(index) {
    Some(component) =>
      Some(self.required_function_expression(function, component))
    None => raise Unsupported("constant array index \{index} out of bounds")
  }
}

///|
fn WgslIrEmitter::static_access_index_expression_by_index_expression(
  self : WgslIrEmitter,
  function : Function?,
  base : Handle,
  index : Handle,
) -> String? raise WgslIrEmitError {
  guard self.options.fold_numeric_constant_expressions() else { return None }
  guard self.eval_integer_expression_for_access_index(function, index)
    is Some(value) else {
    return None
  }
  guard value.to_signed_int() is Some(static_index) else { return None }
  match self.folded_static_constant_array_access(function, base, static_index) {
    Some(value) => Some(value)
    None => Some(self.access_index_expression(function, base, static_index))
  }
}

///|
fn WgslIrEmitter::access_index_operand_expression(
  self : WgslIrEmitter,
  function : Function?,
  index : Handle,
) -> String raise WgslIrEmitError {
  if self.options.fold_numeric_constant_expressions() {
    match self.eval_integer_expression_for_access_index(function, index) {
      Some(value) =>
        match value.to_unsigned() {
          Some(value) => return "\{value}"
          None => ()
        }
      None => ()
    }
  }
  self.required_function_expression(function, index)
}

///|
fn WgslIrEmitter::eval_integer_expression_for_access_index(
  self : WgslIrEmitter,
  function : Function?,
  index : Handle,
) -> WgslIrEmitIntegerValue? raise WgslIrEmitError {
  match self.required_expression(function, index) {
    Constant(handle) =>
      match self.shader_module.constants.items.get(handle.index()) {
        Some(constant) =>
          self.eval_global_integer_expression_for_emit(constant.init, Set([]))
        None => None
      }
    _ => self.eval_integer_expression_for_writer(function, index)
  }
}

///|
fn WgslIrEmitter::deref_pointer_type_handle(
  self : WgslIrEmitter,
  ty_handle : Handle,
) -> Handle {
  match self.shader_module.types.items.get(ty_handle.index()) {
    Some(ty) =>
      match ty.inner {
        Pointer(inner, _) => self.deref_pointer_type_handle(inner)
        _ => ty_handle
      }
    None => ty_handle
  }
}

///|
fn WgslIrEmitter::swizzle_components(
  self : WgslIrEmitter,
  components : Array[SwizzleComponent],
) -> String {
  ignore(self)
  let out = StringBuilder::new()
  for component in components {
    out.write_string(wgsl_ir_swizzle_component_name(component))
  }
  out.to_string()
}

///|
fn WgslIrEmitter::required_function_expression(
  self : WgslIrEmitter,
  function : Function?,
  handle : Handle,
) -> String raise WgslIrEmitError {
  match function {
    Some(value) => self.function_expression(value, handle)
    None => self.global_expression(handle)
  }
}

///|
fn WgslIrEmitter::expression_list(
  self : WgslIrEmitter,
  handles : Array[Handle],
  function : Function?,
) -> String raise WgslIrEmitError {
  let out = StringBuilder::new()
  for index in 0.. 0 {
      out.write_string(", ")
    }
    match function {
      Some(func) =>
        out.write_string(self.function_expression(func, handles[index]))
      None => out.write_string(self.global_expression(handles[index]))
    }
  }
  out.to_string()
}

///|
fn WgslIrEmitter::math_argument_list(
  self : WgslIrEmitter,
  math_function : MathFunction,
  a : Handle,
  b : Handle?,
  c : Handle?,
  d : Handle?,
  function : Function?,
) -> String raise WgslIrEmitError {
  let handles : Array[Handle] = [a]
  match b {
    Some(value) => handles.push(value)
    None => ()
  }
  match c {
    Some(value) => handles.push(value)
    None => ()
  }
  match d {
    Some(value) => handles.push(value)
    None => ()
  }
  let scalar_context = if self.options.contextualize_numeric_literals() {
    self.math_argument_scalar_context(math_function, handles, function)
  } else {
    None
  }
  let out = StringBuilder::new()
  for index in 0.. 0 {
      out.write_string(", ")
    }
    out.write_string(
      self.contextual_math_argument_expression(
        function,
        handles[index],
        scalar_context,
      ),
    )
  }
  out.to_string()
}

///|
fn WgslIrEmitter::contextual_math_argument_expression(
  self : WgslIrEmitter,
  function : Function?,
  argument : Handle,
  scalar_context : Scalar?,
) -> String raise WgslIrEmitError {
  match scalar_context {
    Some(scalar) => {
      match self.contextual_scalar_literal(function, argument, scalar) {
        Some(value) => return value
        None => ()
      }
      if self.options.fold_numeric_constant_expressions() {
        match scalar.kind {
          Float | AbstractFloat =>
            match self.eval_float_expression_for_writer(function, argument) {
              Some(value) =>
                match self.float_value_literal_for_width(value, scalar.width) {
                  Some(text) => return text
                  None => ()
                }
              None => ()
            }
          _ => ()
        }
      }
      self.required_function_expression(function, argument)
    }
    None => self.required_function_expression(function, argument)
  }
}

///|
fn WgslIrEmitter::math_argument_scalar_context(
  self : WgslIrEmitter,
  math_function : MathFunction,
  arguments : Array[Handle],
  function : Function?,
) -> Scalar? {
  if !wgsl_ir_math_function_uses_uniform_numeric_argument_context(math_function) {
    return None
  }
  for argument in arguments {
    match self.expression_component_scalar(function, argument) {
      Some(scalar) if !wgsl_ir_scalar_kind_is_abstract(scalar.kind) =>
        return Some(scalar)
      _ => ()
    }
  }
  None
}

///|
fn wgsl_ir_math_function_uses_uniform_numeric_argument_context(
  math_function : MathFunction,
) -> Bool {
  match math_function {
    Min
    | Max
    | Clamp
    | Step
    | SmoothStep
    | Mix
    | Dot
    | Distance
    | Reflect
    | FaceForward
    | Refract
    | Fma
    | Pow
    | Atan2 => true
    _ => false
  }
}