///|
/// Whether a context slot can change while generated code for one function runs.
pub(all) enum ContextSlotMutability {
  Immutable
  Mutable
} derive(Eq, Debug)

///|
/// Optional physical-register cache role for a context-derived pointer.
pub(all) enum ContextSlotCacheRole {
  NoContextCache
  ContextCache0Descriptor
  ContextCache0Source
  ContextCache1
} derive(Eq, Debug)

///|
/// Opaque embedding-context slot metadata.
///
/// The `id` is owned by the embedding adapter. Reusable infrastructure may use
/// `offset`, `mutability`, and `cache_role` facts without interpreting what the
/// slot represents.
pub(all) struct ContextSlot {
  id : Int
  offset : Int
  mutability : ContextSlotMutability
  cache_role : ContextSlotCacheRole
} derive(Eq, Debug)

///|
/// Machine-level offsets for an embedding-provided runtime context.
///
/// MachV treats these as opaque offsets from the context base register. The
/// embedding owns the concrete runtime structure and translates its layout into
/// this shape before invoking lowering or emission.
pub(all) struct EmbeddingContextLayout {
  slots : Array[ContextSlot]
  active_function_index_slot : Int?
  allocation_cursor_slot : Int?
  allocation_limit_slot : Int?
}

///|
pub fn EmbeddingContextLayout::slot(
  self : EmbeddingContextLayout,
  id : Int,
) -> ContextSlot? {
  for slot in self.slots {
    if slot.id == id {
      return Some(slot)
    }
  }
  None
}

///|
pub fn EmbeddingContextLayout::slot_offset(
  self : EmbeddingContextLayout,
  id : Int,
) -> Int? {
  match self.slot(id) {
    Some(slot) => Some(slot.offset)
    None => None
  }
}

///|
pub fn EmbeddingContextLayout::slot_offset_for_cache_role(
  self : EmbeddingContextLayout,
  role : ContextSlotCacheRole,
) -> Int? {
  for slot in self.slots {
    if slot.cache_role == role {
      return Some(slot.offset)
    }
  }
  None
}

///|
pub fn EmbeddingContextLayout::require_cache_role_offset(
  self : EmbeddingContextLayout,
  role : ContextSlotCacheRole,
) -> Int {
  match self.slot_offset_for_cache_role(role) {
    Some(offset) => offset
    None => abort("missing embedding context cache role")
  }
}

///|
pub fn EmbeddingContextLayout::require_slot_offset(
  self : EmbeddingContextLayout,
  id : Int,
) -> Int {
  match self.slot_offset(id) {
    Some(offset) => offset
    None => abort("missing embedding context slot \{id}")
  }
}

///|
pub fn EmbeddingContextLayout::require_active_function_index_offset(
  self : EmbeddingContextLayout,
) -> Int {
  match self.active_function_index_slot {
    Some(id) => self.require_slot_offset(id)
    None => abort("active function index context slot is required")
  }
}

///|
pub fn EmbeddingContextLayout::require_allocation_cursor_offset(
  self : EmbeddingContextLayout,
) -> Int {
  match self.allocation_cursor_slot {
    Some(id) => self.require_slot_offset(id)
    None => abort("allocation cursor context slot is required")
  }
}

///|
pub fn EmbeddingContextLayout::require_allocation_limit_offset(
  self : EmbeddingContextLayout,
) -> Int {
  match self.allocation_limit_slot {
    Some(id) => self.require_slot_offset(id)
    None => abort("allocation limit context slot is required")
  }
}