2025-08-12 11:33:48 +00:00
|
|
|
|
/*!
|
|
|
|
|
|
* MIR Effect System - Track side effects for optimization
|
2025-09-17 07:43:07 +09:00
|
|
|
|
*
|
2025-08-12 11:33:48 +00:00
|
|
|
|
* Based on ChatGPT5's design for parallel execution and optimization safety
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2025-08-26 05:49:23 +09:00
|
|
|
|
use crate::debug::log as dlog;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
use std::fmt;
|
2025-08-12 11:33:48 +00:00
|
|
|
|
|
|
|
|
|
|
/// Effect flags for tracking side effects and enabling optimizations
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
|
|
pub struct EffectMask(u16);
|
|
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
/// Individual effect types for the 4-category MIR hierarchy
|
2025-08-12 11:33:48 +00:00
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
|
pub enum Effect {
|
2025-08-14 02:39:04 +00:00
|
|
|
|
/// Pure computation with no side effects (Tier-0: reorderable, CSE/LICM eligible)
|
2025-08-12 11:33:48 +00:00
|
|
|
|
Pure = 0x0001,
|
2025-08-14 02:39:04 +00:00
|
|
|
|
/// Mutable operations (Tier-1: same Box/Field dependency preservation)
|
|
|
|
|
|
Mut = 0x0002,
|
|
|
|
|
|
/// I/O operations (Tier-1: no reordering, side effects present)
|
|
|
|
|
|
Io = 0x0004,
|
|
|
|
|
|
/// Control flow operations (Tier-0: affects execution flow)
|
|
|
|
|
|
Control = 0x0008,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
// Legacy effects for compatibility (will be mapped to above categories)
|
|
|
|
|
|
/// Reads from heap/memory (maps to Pure if read-only)
|
|
|
|
|
|
ReadHeap = 0x0010,
|
|
|
|
|
|
/// Writes to heap/memory (maps to Mut)
|
|
|
|
|
|
WriteHeap = 0x0020,
|
|
|
|
|
|
/// P2P/network communication (maps to Io)
|
|
|
|
|
|
P2P = 0x0040,
|
|
|
|
|
|
/// Foreign Function Interface calls (maps to Io)
|
|
|
|
|
|
FFI = 0x0080,
|
|
|
|
|
|
/// May panic or throw exceptions (maps to Io)
|
|
|
|
|
|
Panic = 0x0100,
|
|
|
|
|
|
/// Allocates memory (maps to Mut)
|
|
|
|
|
|
Alloc = 0x0200,
|
|
|
|
|
|
/// Accesses global state (maps to Io)
|
|
|
|
|
|
Global = 0x0400,
|
|
|
|
|
|
/// Thread/async operations (maps to Io)
|
|
|
|
|
|
Async = 0x0800,
|
|
|
|
|
|
/// Unsafe operations (maps to Io)
|
|
|
|
|
|
Unsafe = 0x1000,
|
|
|
|
|
|
/// Debug/logging operations (maps to Io)
|
|
|
|
|
|
Debug = 0x2000,
|
|
|
|
|
|
/// Memory barrier operations (maps to Io)
|
|
|
|
|
|
Barrier = 0x4000,
|
2025-08-12 11:33:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl EffectMask {
|
|
|
|
|
|
/// No effects - pure computation
|
|
|
|
|
|
pub const PURE: Self = Self(Effect::Pure as u16);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
/// Mutable operations (writes, ownership changes)
|
|
|
|
|
|
pub const MUT: Self = Self(Effect::Mut as u16);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
/// I/O operations (external effects, cannot reorder)
|
|
|
|
|
|
pub const IO: Self = Self(Effect::Io as u16);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
/// Control flow operations
|
|
|
|
|
|
pub const CONTROL: Self = Self(Effect::Control as u16);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
// Legacy constants for compatibility
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Memory read effects
|
|
|
|
|
|
pub const READ: Self = Self(Effect::ReadHeap as u16);
|
2025-08-16 17:39:04 +09:00
|
|
|
|
pub const READ_ALIAS: Self = Self::READ; // Uppercase alias for compatibility
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Memory write effects (includes read)
|
|
|
|
|
|
pub const WRITE: Self = Self((Effect::WriteHeap as u16) | (Effect::ReadHeap as u16));
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
/// P2P communication effects
|
2025-08-12 11:33:48 +00:00
|
|
|
|
pub const P2P: Self = Self(Effect::P2P as u16);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-13 06:23:28 +00:00
|
|
|
|
/// Panic/exception effects
|
|
|
|
|
|
pub const PANIC: Self = Self(Effect::Panic as u16);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// All effects - maximum side effects
|
|
|
|
|
|
pub const ALL: Self = Self(0xFFFF);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Create an empty effect mask
|
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
|
Self(0)
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Create effect mask from raw bits
|
|
|
|
|
|
pub fn from_bits(bits: u16) -> Self {
|
|
|
|
|
|
Self(bits)
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Get raw bits
|
|
|
|
|
|
pub fn bits(self) -> u16 {
|
|
|
|
|
|
self.0
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Add an effect to the mask
|
|
|
|
|
|
pub fn add(self, effect: Effect) -> Self {
|
|
|
|
|
|
Self(self.0 | (effect as u16))
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Remove an effect from the mask
|
|
|
|
|
|
pub fn remove(self, effect: Effect) -> Self {
|
|
|
|
|
|
Self(self.0 & !(effect as u16))
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Check if mask contains an effect
|
|
|
|
|
|
pub fn contains(self, effect: Effect) -> bool {
|
|
|
|
|
|
(self.0 & (effect as u16)) != 0
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Check if mask contains any of the given effects
|
|
|
|
|
|
pub fn contains_any(self, mask: EffectMask) -> bool {
|
|
|
|
|
|
(self.0 & mask.0) != 0
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Check if mask contains all of the given effects
|
|
|
|
|
|
pub fn contains_all(self, mask: EffectMask) -> bool {
|
|
|
|
|
|
(self.0 & mask.0) == mask.0
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Combine two effect masks
|
|
|
|
|
|
pub fn union(self, other: EffectMask) -> Self {
|
|
|
|
|
|
Self(self.0 | other.0)
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Get intersection of two effect masks
|
|
|
|
|
|
pub fn intersection(self, other: EffectMask) -> Self {
|
|
|
|
|
|
Self(self.0 & other.0)
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Check if the computation is pure (no side effects)
|
|
|
|
|
|
pub fn is_pure(self) -> bool {
|
2025-08-26 05:49:23 +09:00
|
|
|
|
// READ/WRITE/IO/CONTROLがあれば純粋ではない(READはreadonly扱い)
|
|
|
|
|
|
let pure = !self.contains(Effect::ReadHeap)
|
|
|
|
|
|
&& !self.is_mut()
|
|
|
|
|
|
&& !self.is_io()
|
|
|
|
|
|
&& !self.is_control();
|
|
|
|
|
|
if dlog::on("NYASH_DEBUG_EFFECTS") {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[EFFECT] bits={:#06x} primary={:?} is_pure={} read_only={} mut={} io={}",
|
|
|
|
|
|
self.bits(),
|
|
|
|
|
|
self.primary_category(),
|
|
|
|
|
|
pure,
|
|
|
|
|
|
self.is_read_only(),
|
|
|
|
|
|
self.is_mut(),
|
|
|
|
|
|
self.is_io()
|
|
|
|
|
|
);
|
2025-08-26 05:49:23 +09:00
|
|
|
|
}
|
|
|
|
|
|
pure
|
2025-08-14 02:39:04 +00:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
/// Check if the computation is mutable (modifies state)
|
|
|
|
|
|
pub fn is_mut(self) -> bool {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.contains(Effect::Mut)
|
|
|
|
|
|
|| self.contains(Effect::WriteHeap)
|
|
|
|
|
|
|| self.contains(Effect::Alloc)
|
2025-08-14 02:39:04 +00:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
/// Check if the computation has I/O effects (external side effects)
|
|
|
|
|
|
pub fn is_io(self) -> bool {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.contains(Effect::Io)
|
|
|
|
|
|
|| self.contains(Effect::P2P)
|
|
|
|
|
|
|| self.contains(Effect::FFI)
|
|
|
|
|
|
|| self.contains(Effect::Global)
|
|
|
|
|
|
|| self.contains(Effect::Async)
|
|
|
|
|
|
|| self.contains(Effect::Unsafe)
|
|
|
|
|
|
|| self.contains(Effect::Debug)
|
|
|
|
|
|
|| self.contains(Effect::Barrier)
|
|
|
|
|
|
|| self.contains(Effect::Panic)
|
2025-08-14 02:39:04 +00:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
/// Check if the computation affects control flow
|
|
|
|
|
|
pub fn is_control(self) -> bool {
|
|
|
|
|
|
self.contains(Effect::Control)
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
/// Get the primary effect category for MIR optimization
|
|
|
|
|
|
pub fn primary_category(self) -> Effect {
|
|
|
|
|
|
if self.is_control() {
|
|
|
|
|
|
Effect::Control
|
|
|
|
|
|
} else if self.is_io() {
|
|
|
|
|
|
Effect::Io
|
|
|
|
|
|
} else if self.is_mut() {
|
|
|
|
|
|
Effect::Mut
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Effect::Pure
|
|
|
|
|
|
}
|
2025-08-12 11:33:48 +00:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Check if the computation only reads (doesn't modify state)
|
|
|
|
|
|
pub fn is_read_only(self) -> bool {
|
2025-08-14 02:39:04 +00:00
|
|
|
|
!self.is_mut() && !self.is_io()
|
2025-08-12 11:33:48 +00:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Check if parallel execution is safe
|
|
|
|
|
|
pub fn is_parallel_safe(self) -> bool {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
!self.contains(Effect::WriteHeap)
|
|
|
|
|
|
&& !self.contains(Effect::Global)
|
|
|
|
|
|
&& !self.contains(Effect::Unsafe)
|
2025-08-12 11:33:48 +00:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Check if operation can be moved across other operations
|
|
|
|
|
|
pub fn is_moveable(self) -> bool {
|
|
|
|
|
|
self.is_pure() || self.is_read_only()
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Get a human-readable list of effects
|
|
|
|
|
|
pub fn effect_names(self) -> Vec<&'static str> {
|
|
|
|
|
|
let mut names = Vec::new();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
// Primary categories
|
2025-09-17 07:43:07 +09:00
|
|
|
|
if self.contains(Effect::Pure) {
|
|
|
|
|
|
names.push("pure");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::Mut) {
|
|
|
|
|
|
names.push("mut");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::Io) {
|
|
|
|
|
|
names.push("io");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::Control) {
|
|
|
|
|
|
names.push("control");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
// Legacy effects for detailed tracking
|
2025-09-17 07:43:07 +09:00
|
|
|
|
if self.contains(Effect::ReadHeap) {
|
|
|
|
|
|
names.push("read");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::WriteHeap) {
|
|
|
|
|
|
names.push("write");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::P2P) {
|
|
|
|
|
|
names.push("p2p");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::FFI) {
|
|
|
|
|
|
names.push("ffi");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::Panic) {
|
|
|
|
|
|
names.push("panic");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::Alloc) {
|
|
|
|
|
|
names.push("alloc");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::Global) {
|
|
|
|
|
|
names.push("global");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::Async) {
|
|
|
|
|
|
names.push("async");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::Unsafe) {
|
|
|
|
|
|
names.push("unsafe");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::Debug) {
|
|
|
|
|
|
names.push("debug");
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.contains(Effect::Barrier) {
|
|
|
|
|
|
names.push("barrier");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
if names.is_empty() {
|
|
|
|
|
|
names.push("none");
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
names
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Default for EffectMask {
|
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
|
Self::PURE
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for EffectMask {
|
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
|
let names = self.effect_names();
|
|
|
|
|
|
if names.is_empty() {
|
|
|
|
|
|
write!(f, "none")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
write!(f, "{}", names.join("|"))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl std::ops::BitOr for EffectMask {
|
|
|
|
|
|
type Output = Self;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
fn bitor(self, rhs: Self) -> Self {
|
|
|
|
|
|
self.union(rhs)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl std::ops::BitOrAssign for EffectMask {
|
|
|
|
|
|
fn bitor_assign(&mut self, rhs: Self) {
|
|
|
|
|
|
*self = *self | rhs;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl std::ops::BitAnd for EffectMask {
|
|
|
|
|
|
type Output = Self;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
fn bitand(self, rhs: Self) -> Self {
|
|
|
|
|
|
self.intersection(rhs)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl std::ops::BitAndAssign for EffectMask {
|
|
|
|
|
|
fn bitand_assign(&mut self, rhs: Self) {
|
|
|
|
|
|
*self = *self & rhs;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use super::*;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_effect_mask_creation() {
|
|
|
|
|
|
let pure = EffectMask::PURE;
|
|
|
|
|
|
let read = EffectMask::READ;
|
|
|
|
|
|
let write = EffectMask::WRITE;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
assert!(pure.is_pure());
|
|
|
|
|
|
assert!(!read.is_pure());
|
|
|
|
|
|
assert!(!write.is_pure());
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
assert!(read.is_read_only());
|
|
|
|
|
|
assert!(!write.is_read_only());
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_effect_combination() {
|
|
|
|
|
|
let mut effects = EffectMask::new();
|
|
|
|
|
|
assert!(effects.is_pure());
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
effects = effects.add(Effect::ReadHeap);
|
|
|
|
|
|
assert!(effects.contains(Effect::ReadHeap));
|
|
|
|
|
|
assert!(effects.is_read_only());
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
effects = effects.add(Effect::WriteHeap);
|
|
|
|
|
|
assert!(effects.contains(Effect::WriteHeap));
|
|
|
|
|
|
assert!(!effects.is_read_only());
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-14 02:39:04 +00:00
|
|
|
|
effects = effects.add(Effect::Io);
|
|
|
|
|
|
assert!(effects.contains(Effect::Io));
|
2025-08-12 11:33:48 +00:00
|
|
|
|
assert!(!effects.is_parallel_safe());
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_effect_union() {
|
|
|
|
|
|
let read_effect = EffectMask::READ;
|
|
|
|
|
|
let io_effect = EffectMask::IO;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
let combined = read_effect | io_effect;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
assert!(combined.contains(Effect::ReadHeap));
|
2025-08-14 02:39:04 +00:00
|
|
|
|
assert!(combined.contains(Effect::Io));
|
2025-08-12 11:33:48 +00:00
|
|
|
|
assert!(!combined.is_pure());
|
2025-08-20 04:45:26 +09:00
|
|
|
|
// IO + read remains parallel-safe under current semantics
|
|
|
|
|
|
assert!(combined.is_parallel_safe());
|
2025-08-12 11:33:48 +00:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_parallel_safety() {
|
|
|
|
|
|
let pure = EffectMask::PURE;
|
|
|
|
|
|
let read = EffectMask::READ;
|
|
|
|
|
|
let write = EffectMask::WRITE;
|
|
|
|
|
|
let io = EffectMask::IO;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
assert!(pure.is_parallel_safe());
|
|
|
|
|
|
assert!(read.is_parallel_safe());
|
|
|
|
|
|
assert!(!write.is_parallel_safe());
|
|
|
|
|
|
assert!(io.is_parallel_safe()); // I/O can be parallel if properly synchronized
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_effect_names() {
|
|
|
|
|
|
let pure = EffectMask::PURE;
|
|
|
|
|
|
assert_eq!(pure.effect_names(), vec!["pure"]);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
let read_write = EffectMask::READ.add(Effect::WriteHeap);
|
|
|
|
|
|
let names = read_write.effect_names();
|
|
|
|
|
|
assert!(names.contains(&"read"));
|
|
|
|
|
|
assert!(names.contains(&"write"));
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_effect_display() {
|
|
|
|
|
|
let pure = EffectMask::PURE;
|
|
|
|
|
|
assert_eq!(format!("{}", pure), "pure");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
let read_io = EffectMask::READ | EffectMask::IO;
|
|
|
|
|
|
let display = format!("{}", read_io);
|
|
|
|
|
|
assert!(display.contains("read"));
|
|
|
|
|
|
assert!(display.contains("io"));
|
2025-08-24 01:58:41 +09:00
|
|
|
|
|
|
|
|
|
|
// is_pure should be false when IO is present, even if PURE bit was set initially
|
|
|
|
|
|
let mixed = EffectMask::PURE | EffectMask::IO;
|
|
|
|
|
|
assert!(!mixed.is_pure());
|
2025-08-12 11:33:48 +00:00
|
|
|
|
}
|
2025-08-20 04:45:26 +09:00
|
|
|
|
}
|