///|
fn WgslIrLowerer::matrix_multiply_operand_context(
  self : WgslIrLowerer,
  left_ty : Handle,
  right_ty : Handle,
) -> (Handle?, Handle?)? {
  guard self.shader_module.types.get(left_ty) is Some(left) else { return None }
  guard self.shader_module.types.get(right_ty) is Some(right) else {
    return None
  }
  match (left.inner, right.inner) {
    (Matrix(columns, _, scalar), Vector(size, _)) =>
      if columns == size {
        Some((None, Some(self.vector_type_handle(size, scalar))))
      } else {
        None
      }
    (Vector(size, _), Matrix(_, rows, scalar)) =>
      if rows == size {
        Some((Some(self.vector_type_handle(size, scalar)), None))
      } else {
        None
      }
    _ => None
  }
}

///|
fn WgslIrLowerer::lower_function_static_or_dynamic_index(
  self : WgslIrLowerer,
  base_handle : Handle,
  index : @ast.WgslExprNode,
  function : Function,
  scope : WgslIrFunctionScope,
) -> Handle raise WgslIrLowerError {
  match self.static_index_for_access_index(base_handle, index, function) {
    Some(static_index) =>
      function.expressions.append(AccessIndex(base_handle, static_index))
    None => {
      let index_handle = self.lower_function_expression_node(
        index, function, scope,
      )
      function.expressions.append(Access(base_handle, index_handle))
    }
  }
}

///|
fn WgslIrLowerer::static_index_for_access_index(
  self : WgslIrLowerer,
  base_handle : Handle,
  index : @ast.WgslExprNode,
  function : Function,
) -> Int32? raise WgslIrLowerError {
  guard self.static_index_value_from_expr_node(index) is Some(static_index) else {
    return None
  }
  guard static_index >= 0 else { return None }
  guard self.function_expression_type(function, base_handle) is Some(ty) else {
    return None
  }
  match self.shader_module.types.get(self.deref_pointer_type_handle(ty)) {
    Some(type_) =>
      match type_.inner {
        Vector(size, _) =>
          if static_index < size.width() {
            Some(static_index)
          } else {
            None
          }
        Matrix(columns, _, _) =>
          if static_index < columns.width() {
            Some(static_index)
          } else {
            None
          }
        Array(_, Constant(count), _) =>
          if static_index < count {
            Some(static_index)
          } else {
            None
          }
        Array(_, Dynamic, _) => Some(static_index)
        _ => None
      }
    None => None
  }
}

///|
fn WgslIrLowerer::static_index_value_from_expr_node(
  self : WgslIrLowerer,
  index : @ast.WgslExprNode,
) -> Int32? raise WgslIrLowerError {
  match wgsl_ir_int_literal_from_expr_node(index) {
    Some(static_index) => return Some(static_index)
    None => ()
  }
  match index.kind() {
    Group(inner) => self.static_index_value_from_expr_node(inner)
    Identifier(name) =>
      match self.ensure_constant_declaration_lowered(name.name()) {
        Some(handle) =>
          match self.shader_module.constants.items.get(handle.index()) {
            Some(constant) => {
              let evaluated = Some(
                self.eval_global_integer_expression(constant.init),
              ) catch {
                _ => None
              }
              match evaluated {
                Some(value) => Some(value.to_signed_int())
                None => None
              }
            }
            None => None
          }
        None => None
      }
    _ => None
  }
}