#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct ItemIndex(u16);
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[error("invalid item index: too many items")]
pub struct ItemIndexError {
attempted: usize,
}
impl ItemIndex {
pub const MAX_ITEMS: usize = u16::MAX as usize + 1;
pub fn new(id: usize) -> Self {
Self::try_new(id).expect("invalid item index: too many items")
}
pub fn try_new(id: usize) -> Result<Self, ItemIndexError> {
let raw = id.try_into().map_err(|_| ItemIndexError { attempted: id })?;
Ok(Self(raw))
}
#[inline(always)]
pub const fn const_new(id: u16) -> Self {
Self(id)
}
#[inline(always)]
pub const fn as_usize(&self) -> usize {
self.0 as usize
}
}
impl core::fmt::Display for ItemIndex {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_usize())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GlobalItemIndex {
pub module: ModuleIndex,
pub index: ItemIndex,
}
impl core::fmt::Display for GlobalItemIndex {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}:{}", self.module, self.index)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct ModuleIndex(u16);
impl ModuleIndex {
pub fn new(index: usize) -> Self {
Self(index.try_into().expect("invalid module index: too many modules"))
}
pub const fn const_new(index: u16) -> Self {
Self(index)
}
#[inline(always)]
pub const fn as_usize(&self) -> usize {
self.0 as usize
}
}
impl core::ops::Add<ItemIndex> for ModuleIndex {
type Output = GlobalItemIndex;
fn add(self, rhs: ItemIndex) -> Self::Output {
GlobalItemIndex { module: self, index: rhs }
}
}
impl core::fmt::Display for ModuleIndex {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_usize())
}
}
#[cfg(test)]
mod regression_tests {
use std::string::String;
use crate::sema::{LimitKind, SemanticAnalysisError, SyntaxError};
fn huge_library_masm() -> String {
let num_consts = usize::from(u16::MAX) + 2;
let mut masm = String::with_capacity(num_consts * 16);
masm.push_str("namespace ::m::huge\n\n");
for i in 0..num_consts {
masm.push_str("const A");
masm.push_str(&format!("{i}"));
masm.push_str(" = 0\n");
}
masm
}
#[test]
fn too_many_items_in_module_is_rejected_during_analysis() {
let test = crate::testing::SyntaxTestContext::new();
let err = test
.parse_module(&huge_library_masm())
.expect_err("expected oversized module to be rejected during analysis");
let syntax_error = err.downcast_ref::<SyntaxError>().expect("expected SyntaxError report");
assert!(
syntax_error.errors.iter().any(|error| {
matches!(error, SemanticAnalysisError::LimitExceeded { kind: LimitKind::Items, .. })
}),
"expected item-limit error, got {:?}",
syntax_error.errors
);
}
}