chore(mir): prune legacy instruction sets (#129)
This commit is contained in:
@ -1,116 +1,40 @@
|
|||||||
//! Introspection helpers for MIR instruction set
|
//! Introspection helpers for the MIR14 instruction set
|
||||||
//!
|
|
||||||
//! Migration note:
|
|
||||||
//! - Historically we synced to a canonical 26-instruction doc list.
|
|
||||||
//! - We are migrating to Core-15 for enforcement and tests. During migration,
|
|
||||||
//! docs may still list 26; tests should use `core15_instruction_names()`.
|
|
||||||
|
|
||||||
/// Returns the legacy canonical list of core MIR instruction names (26 items).
|
/// Return the canonical list of MIR14 instruction names.
|
||||||
/// This list matched docs/reference/mir/INSTRUCTION_SET.md under "Core Instructions".
|
pub fn mir14_instruction_names() -> &'static [&'static str] {
|
||||||
pub fn core_instruction_names() -> &'static [&'static str] {
|
|
||||||
&[
|
&[
|
||||||
"Const",
|
// values / arithmetic
|
||||||
"Copy",
|
|
||||||
"Load",
|
|
||||||
"Store",
|
|
||||||
"UnaryOp",
|
|
||||||
"BinOp",
|
|
||||||
"Compare",
|
|
||||||
"Jump",
|
|
||||||
"Branch",
|
|
||||||
"Phi",
|
|
||||||
"Return",
|
|
||||||
"Call",
|
|
||||||
"ExternCall",
|
|
||||||
"BoxCall",
|
|
||||||
"NewBox",
|
|
||||||
"ArrayGet",
|
|
||||||
"ArraySet",
|
|
||||||
"RefNew",
|
|
||||||
"RefGet",
|
|
||||||
"RefSet",
|
|
||||||
"Await",
|
|
||||||
"Print",
|
|
||||||
"TypeOp",
|
|
||||||
"WeakRef",
|
|
||||||
"Barrier",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the Core-15 instruction names used for the minimal kernel enforcement.
|
|
||||||
/// This list is implementation-driven for migration stage; docs may differ temporarily.
|
|
||||||
pub fn core15_instruction_names() -> &'static [&'static str] {
|
|
||||||
&[
|
|
||||||
// 値/計算
|
|
||||||
"Const",
|
"Const",
|
||||||
"UnaryOp",
|
"UnaryOp",
|
||||||
"BinOp",
|
"BinOp",
|
||||||
"Compare",
|
"Compare",
|
||||||
"TypeOp",
|
"TypeOp",
|
||||||
// メモリ
|
// memory
|
||||||
"Load",
|
"Load",
|
||||||
"Store",
|
"Store",
|
||||||
// 制御
|
// control flow
|
||||||
"Jump",
|
"Jump",
|
||||||
"Branch",
|
"Branch",
|
||||||
"Return",
|
"Return",
|
||||||
"Phi",
|
"Phi",
|
||||||
// 呼び出し/Box
|
// boxes / external
|
||||||
"Call",
|
|
||||||
"NewBox",
|
"NewBox",
|
||||||
"BoxCall",
|
"BoxCall",
|
||||||
"ExternCall",
|
"ExternCall",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the Core-13 instruction names (final minimal kernel).
|
|
||||||
/// This is the fixed target set used for MIR unification.
|
|
||||||
pub fn core13_instruction_names() -> &'static [&'static str] {
|
|
||||||
&[
|
|
||||||
// 値/計算
|
|
||||||
"Const",
|
|
||||||
"BinOp",
|
|
||||||
"Compare",
|
|
||||||
// 制御
|
|
||||||
"Jump",
|
|
||||||
"Branch",
|
|
||||||
"Return",
|
|
||||||
"Phi",
|
|
||||||
// 呼び出し
|
|
||||||
"Call",
|
|
||||||
"BoxCall",
|
|
||||||
"ExternCall",
|
|
||||||
// メタ
|
|
||||||
"TypeOp",
|
|
||||||
"Safepoint",
|
|
||||||
"Barrier",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
// Core-15 enforcement: only count check; names are implementation-defined during migration.
|
|
||||||
#[test]
|
#[test]
|
||||||
fn core15_instruction_count_is_15() {
|
fn mir14_instruction_count_is_14() {
|
||||||
let impl_names = core15_instruction_names();
|
let names = mir14_instruction_names();
|
||||||
assert_eq!(impl_names.len(), 15, "Core-15 must contain exactly 15 instructions");
|
assert_eq!(names.len(), 14, "MIR14 must contain exactly 14 instructions");
|
||||||
// basic sanity: includes a few key ops
|
let set: BTreeSet<_> = names.iter().copied().collect();
|
||||||
let set: BTreeSet<_> = impl_names.iter().copied().collect();
|
for must in ["Const", "UnaryOp", "BoxCall"] { assert!(set.contains(must), "missing '{}'", must); }
|
||||||
for must in ["Const", "BinOp", "Return", "ExternCall"] { assert!(set.contains(must), "missing '{}'", must); }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn core13_instruction_count_is_13() {
|
|
||||||
let impl_names = core13_instruction_names();
|
|
||||||
assert_eq!(impl_names.len(), 13, "Core-13 must contain exactly 13 instructions");
|
|
||||||
let set: BTreeSet<_> = impl_names.iter().copied().collect();
|
|
||||||
for must in ["Const", "BinOp", "Return", "BoxCall", "ExternCall", "TypeOp"] {
|
|
||||||
assert!(set.contains(must), "missing '{}'", must);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,543 +0,0 @@
|
|||||||
/*!
|
|
||||||
* MIR 25-Instruction Specification Implementation
|
|
||||||
*
|
|
||||||
* Complete hierarchical MIR instruction set based on ChatGPT5 + AI Council design
|
|
||||||
*/
|
|
||||||
|
|
||||||
use super::{ValueId, EffectMask, Effect, BasicBlockId};
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
/// MIR instruction types - exactly 25 instructions per specification
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum MirInstructionV2 {
|
|
||||||
// === TIER-0: UNIVERSAL CORE (8 instructions) ===
|
|
||||||
|
|
||||||
/// Load a constant value (pure)
|
|
||||||
/// `%dst = const value`
|
|
||||||
Const {
|
|
||||||
dst: ValueId,
|
|
||||||
value: ConstValue,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Binary arithmetic operation (pure)
|
|
||||||
/// `%dst = %lhs op %rhs`
|
|
||||||
BinOp {
|
|
||||||
dst: ValueId,
|
|
||||||
op: BinaryOp,
|
|
||||||
lhs: ValueId,
|
|
||||||
rhs: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Compare two values (pure)
|
|
||||||
/// `%dst = %lhs cmp %rhs`
|
|
||||||
Compare {
|
|
||||||
dst: ValueId,
|
|
||||||
op: CompareOp,
|
|
||||||
lhs: ValueId,
|
|
||||||
rhs: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Conditional branch (control)
|
|
||||||
/// `br %condition -> %then_bb, %else_bb`
|
|
||||||
Branch {
|
|
||||||
condition: ValueId,
|
|
||||||
then_bb: BasicBlockId,
|
|
||||||
else_bb: BasicBlockId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Unconditional jump (control)
|
|
||||||
/// `jmp %target_bb`
|
|
||||||
Jump {
|
|
||||||
target: BasicBlockId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// SSA phi function for merging values (pure)
|
|
||||||
/// `%dst = phi [%val1 from %bb1, %val2 from %bb2, ...]`
|
|
||||||
Phi {
|
|
||||||
dst: ValueId,
|
|
||||||
inputs: Vec<(BasicBlockId, ValueId)>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// External function call (context-dependent)
|
|
||||||
/// `%dst = call %func(%args...)`
|
|
||||||
Call {
|
|
||||||
dst: Option<ValueId>,
|
|
||||||
func: ValueId,
|
|
||||||
args: Vec<ValueId>,
|
|
||||||
effects: EffectMask,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Return from function (control)
|
|
||||||
/// `ret %value` or `ret void`
|
|
||||||
Return {
|
|
||||||
value: Option<ValueId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// === TIER-1: NYASH SEMANTICS (12 instructions) ===
|
|
||||||
|
|
||||||
/// Create a new Box instance (strong ownership node in ownership forest)
|
|
||||||
/// `%dst = new_box "BoxType"(%args...)`
|
|
||||||
NewBox {
|
|
||||||
dst: ValueId,
|
|
||||||
box_type: String,
|
|
||||||
args: Vec<ValueId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Load Box field value (pure)
|
|
||||||
/// `%dst = %box.field`
|
|
||||||
BoxFieldLoad {
|
|
||||||
dst: ValueId,
|
|
||||||
box_val: ValueId,
|
|
||||||
field: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Store value to Box field (mut)
|
|
||||||
/// `%box.field = %value`
|
|
||||||
BoxFieldStore {
|
|
||||||
box_val: ValueId,
|
|
||||||
field: String,
|
|
||||||
value: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Box method invocation (context-dependent)
|
|
||||||
/// `%dst = %box.method(%args...)`
|
|
||||||
BoxCall {
|
|
||||||
dst: Option<ValueId>,
|
|
||||||
box_val: ValueId,
|
|
||||||
method: String,
|
|
||||||
args: Vec<ValueId>,
|
|
||||||
effects: EffectMask,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Safepoint for finalization/interrupts (io)
|
|
||||||
/// `safepoint`
|
|
||||||
Safepoint,
|
|
||||||
|
|
||||||
/// Get reference as value (pure)
|
|
||||||
/// `%dst = ref_get %reference`
|
|
||||||
RefGet {
|
|
||||||
dst: ValueId,
|
|
||||||
reference: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Set/replace reference target with ownership validation (mut)
|
|
||||||
/// `ref_set %reference = %new_target`
|
|
||||||
RefSet {
|
|
||||||
reference: ValueId,
|
|
||||||
new_target: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Create weak reference handle (non-owning link) (pure)
|
|
||||||
/// `%dst = weak_new %box`
|
|
||||||
WeakNew {
|
|
||||||
dst: ValueId,
|
|
||||||
box_val: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Load from weak reference with liveness check (returns null if dead) (pure)
|
|
||||||
/// `%dst = weak_load %weak_ref`
|
|
||||||
WeakLoad {
|
|
||||||
dst: ValueId,
|
|
||||||
weak_ref: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Check weak reference validity (returns bool) (pure)
|
|
||||||
/// `%dst = weak_check %weak_ref`
|
|
||||||
WeakCheck {
|
|
||||||
dst: ValueId,
|
|
||||||
weak_ref: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Send message via Bus system (io)
|
|
||||||
/// `send %bus, %message`
|
|
||||||
Send {
|
|
||||||
bus: ValueId,
|
|
||||||
message: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Receive message from Bus system (io)
|
|
||||||
/// `%dst = recv %bus`
|
|
||||||
Recv {
|
|
||||||
dst: ValueId,
|
|
||||||
bus: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
// === TIER-2: IMPLEMENTATION ASSISTANCE (5 instructions) ===
|
|
||||||
|
|
||||||
/// Tail call optimization (control)
|
|
||||||
/// `tail_call %func(%args...)`
|
|
||||||
TailCall {
|
|
||||||
func: ValueId,
|
|
||||||
args: Vec<ValueId>,
|
|
||||||
effects: EffectMask,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Ownership transfer: this takes strong ownership of child (mut)
|
|
||||||
/// `adopt %parent, %child`
|
|
||||||
Adopt {
|
|
||||||
parent: ValueId,
|
|
||||||
child: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Release strong ownership (weakify or nullify) (mut)
|
|
||||||
/// `release %reference`
|
|
||||||
Release {
|
|
||||||
reference: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Optimized memory copy for structs/arrays (mut)
|
|
||||||
/// `memcopy %dest, %src, %size`
|
|
||||||
MemCopy {
|
|
||||||
dest: ValueId,
|
|
||||||
src: ValueId,
|
|
||||||
size: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Atomic fence for concurrency ordering at Actor/Port boundaries (io)
|
|
||||||
/// `atomic_fence %ordering`
|
|
||||||
AtomicFence {
|
|
||||||
ordering: AtomicOrdering,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constant values in MIR
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum ConstValue {
|
|
||||||
Integer(i64),
|
|
||||||
Float(f64),
|
|
||||||
Bool(bool),
|
|
||||||
String(String),
|
|
||||||
Null,
|
|
||||||
Void,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Binary operations
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum BinaryOp {
|
|
||||||
// Arithmetic
|
|
||||||
Add, Sub, Mul, Div, Mod,
|
|
||||||
|
|
||||||
// Bitwise
|
|
||||||
BitAnd, BitOr, BitXor, Shl, Shr,
|
|
||||||
|
|
||||||
// Logical
|
|
||||||
And, Or,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Comparison operations
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum CompareOp {
|
|
||||||
Eq, Ne, Lt, Le, Gt, Ge,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Atomic ordering for AtomicFence instruction
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum AtomicOrdering {
|
|
||||||
Relaxed,
|
|
||||||
Acquire,
|
|
||||||
Release,
|
|
||||||
AcqRel,
|
|
||||||
SeqCst,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MirInstructionV2 {
|
|
||||||
/// Get the effect mask for this instruction according to 4-category system
|
|
||||||
pub fn effects(&self) -> EffectMask {
|
|
||||||
match self {
|
|
||||||
// TIER-0: Universal Core
|
|
||||||
// Pure operations
|
|
||||||
MirInstructionV2::Const { .. } |
|
|
||||||
MirInstructionV2::BinOp { .. } |
|
|
||||||
MirInstructionV2::Compare { .. } |
|
|
||||||
MirInstructionV2::Phi { .. } => EffectMask::PURE,
|
|
||||||
|
|
||||||
// Control flow operations
|
|
||||||
MirInstructionV2::Branch { .. } |
|
|
||||||
MirInstructionV2::Jump { .. } |
|
|
||||||
MirInstructionV2::Return { .. } => EffectMask::CONTROL,
|
|
||||||
|
|
||||||
// Context-dependent operations
|
|
||||||
MirInstructionV2::Call { effects, .. } => *effects,
|
|
||||||
|
|
||||||
// TIER-1: Nyash Semantics
|
|
||||||
// Pure operations
|
|
||||||
MirInstructionV2::BoxFieldLoad { .. } |
|
|
||||||
MirInstructionV2::RefGet { .. } |
|
|
||||||
MirInstructionV2::WeakNew { .. } |
|
|
||||||
MirInstructionV2::WeakLoad { .. } |
|
|
||||||
MirInstructionV2::WeakCheck { .. } => EffectMask::PURE,
|
|
||||||
|
|
||||||
// Mutable operations
|
|
||||||
MirInstructionV2::NewBox { .. } => EffectMask::MUT.add(Effect::Alloc),
|
|
||||||
MirInstructionV2::BoxFieldStore { .. } |
|
|
||||||
MirInstructionV2::RefSet { .. } => EffectMask::MUT,
|
|
||||||
|
|
||||||
// I/O operations
|
|
||||||
MirInstructionV2::Safepoint |
|
|
||||||
MirInstructionV2::Send { .. } |
|
|
||||||
MirInstructionV2::Recv { .. } => EffectMask::IO,
|
|
||||||
|
|
||||||
// Context-dependent operations
|
|
||||||
MirInstructionV2::BoxCall { effects, .. } => *effects,
|
|
||||||
|
|
||||||
// TIER-2: Implementation Assistance
|
|
||||||
// Control flow operations
|
|
||||||
MirInstructionV2::TailCall { .. } => EffectMask::CONTROL,
|
|
||||||
|
|
||||||
// Mutable operations
|
|
||||||
MirInstructionV2::Adopt { .. } |
|
|
||||||
MirInstructionV2::Release { .. } |
|
|
||||||
MirInstructionV2::MemCopy { .. } => EffectMask::MUT,
|
|
||||||
|
|
||||||
// I/O operations
|
|
||||||
MirInstructionV2::AtomicFence { .. } => EffectMask::IO.add(Effect::Barrier),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the destination ValueId if this instruction produces a value
|
|
||||||
pub fn dst_value(&self) -> Option<ValueId> {
|
|
||||||
match self {
|
|
||||||
MirInstructionV2::Const { dst, .. } |
|
|
||||||
MirInstructionV2::BinOp { dst, .. } |
|
|
||||||
MirInstructionV2::Compare { dst, .. } |
|
|
||||||
MirInstructionV2::Phi { dst, .. } |
|
|
||||||
MirInstructionV2::NewBox { dst, .. } |
|
|
||||||
MirInstructionV2::BoxFieldLoad { dst, .. } |
|
|
||||||
MirInstructionV2::RefGet { dst, .. } |
|
|
||||||
MirInstructionV2::WeakNew { dst, .. } |
|
|
||||||
MirInstructionV2::WeakLoad { dst, .. } |
|
|
||||||
MirInstructionV2::WeakCheck { dst, .. } |
|
|
||||||
MirInstructionV2::Recv { dst, .. } => Some(*dst),
|
|
||||||
|
|
||||||
MirInstructionV2::Call { dst, .. } |
|
|
||||||
MirInstructionV2::BoxCall { dst, .. } => *dst,
|
|
||||||
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get all ValueIds used by this instruction
|
|
||||||
pub fn used_values(&self) -> Vec<ValueId> {
|
|
||||||
match self {
|
|
||||||
MirInstructionV2::Const { .. } => vec![],
|
|
||||||
|
|
||||||
MirInstructionV2::BinOp { lhs, rhs, .. } |
|
|
||||||
MirInstructionV2::Compare { lhs, rhs, .. } => vec![*lhs, *rhs],
|
|
||||||
|
|
||||||
MirInstructionV2::Branch { condition, .. } => vec![*condition],
|
|
||||||
|
|
||||||
MirInstructionV2::Jump { .. } => vec![],
|
|
||||||
|
|
||||||
MirInstructionV2::Phi { inputs, .. } => {
|
|
||||||
inputs.iter().map(|(_, value_id)| *value_id).collect()
|
|
||||||
},
|
|
||||||
|
|
||||||
MirInstructionV2::Call { func, args, .. } => {
|
|
||||||
let mut values = vec![*func];
|
|
||||||
values.extend(args.iter().copied());
|
|
||||||
values
|
|
||||||
},
|
|
||||||
|
|
||||||
MirInstructionV2::Return { value } => {
|
|
||||||
value.map(|v| vec![v]).unwrap_or_default()
|
|
||||||
},
|
|
||||||
|
|
||||||
MirInstructionV2::NewBox { args, .. } => args.clone(),
|
|
||||||
|
|
||||||
MirInstructionV2::BoxFieldLoad { box_val, .. } => vec![*box_val],
|
|
||||||
|
|
||||||
MirInstructionV2::BoxFieldStore { box_val, value, .. } => vec![*box_val, *value],
|
|
||||||
|
|
||||||
MirInstructionV2::BoxCall { box_val, args, .. } => {
|
|
||||||
let mut values = vec![*box_val];
|
|
||||||
values.extend(args.iter().copied());
|
|
||||||
values
|
|
||||||
},
|
|
||||||
|
|
||||||
MirInstructionV2::Safepoint => vec![],
|
|
||||||
|
|
||||||
MirInstructionV2::RefGet { reference, .. } => vec![*reference],
|
|
||||||
|
|
||||||
MirInstructionV2::RefSet { reference, new_target, .. } => vec![*reference, *new_target],
|
|
||||||
|
|
||||||
MirInstructionV2::WeakNew { box_val, .. } => vec![*box_val],
|
|
||||||
|
|
||||||
MirInstructionV2::WeakLoad { weak_ref, .. } |
|
|
||||||
MirInstructionV2::WeakCheck { weak_ref, .. } => vec![*weak_ref],
|
|
||||||
|
|
||||||
MirInstructionV2::Send { bus, message, .. } => vec![*bus, *message],
|
|
||||||
|
|
||||||
MirInstructionV2::Recv { bus, .. } => vec![*bus],
|
|
||||||
|
|
||||||
MirInstructionV2::TailCall { func, args, .. } => {
|
|
||||||
let mut values = vec![*func];
|
|
||||||
values.extend(args.iter().copied());
|
|
||||||
values
|
|
||||||
},
|
|
||||||
|
|
||||||
MirInstructionV2::Adopt { parent, child, .. } => vec![*parent, *child],
|
|
||||||
|
|
||||||
MirInstructionV2::Release { reference, .. } => vec![*reference],
|
|
||||||
|
|
||||||
MirInstructionV2::MemCopy { dest, src, size, .. } => vec![*dest, *src, *size],
|
|
||||||
|
|
||||||
MirInstructionV2::AtomicFence { .. } => vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the instruction tier (0, 1, or 2)
|
|
||||||
pub fn tier(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
// Tier-0: Universal Core
|
|
||||||
MirInstructionV2::Const { .. } |
|
|
||||||
MirInstructionV2::BinOp { .. } |
|
|
||||||
MirInstructionV2::Compare { .. } |
|
|
||||||
MirInstructionV2::Branch { .. } |
|
|
||||||
MirInstructionV2::Jump { .. } |
|
|
||||||
MirInstructionV2::Phi { .. } |
|
|
||||||
MirInstructionV2::Call { .. } |
|
|
||||||
MirInstructionV2::Return { .. } => 0,
|
|
||||||
|
|
||||||
// Tier-1: Nyash Semantics
|
|
||||||
MirInstructionV2::NewBox { .. } |
|
|
||||||
MirInstructionV2::BoxFieldLoad { .. } |
|
|
||||||
MirInstructionV2::BoxFieldStore { .. } |
|
|
||||||
MirInstructionV2::BoxCall { .. } |
|
|
||||||
MirInstructionV2::Safepoint { .. } |
|
|
||||||
MirInstructionV2::RefGet { .. } |
|
|
||||||
MirInstructionV2::RefSet { .. } |
|
|
||||||
MirInstructionV2::WeakNew { .. } |
|
|
||||||
MirInstructionV2::WeakLoad { .. } |
|
|
||||||
MirInstructionV2::WeakCheck { .. } |
|
|
||||||
MirInstructionV2::Send { .. } |
|
|
||||||
MirInstructionV2::Recv { .. } => 1,
|
|
||||||
|
|
||||||
// Tier-2: Implementation Assistance
|
|
||||||
MirInstructionV2::TailCall { .. } |
|
|
||||||
MirInstructionV2::Adopt { .. } |
|
|
||||||
MirInstructionV2::Release { .. } |
|
|
||||||
MirInstructionV2::MemCopy { .. } |
|
|
||||||
MirInstructionV2::AtomicFence { .. } => 2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a human-readable description of the instruction
|
|
||||||
pub fn description(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
// Tier-0
|
|
||||||
MirInstructionV2::Const { .. } => "Load constant value",
|
|
||||||
MirInstructionV2::BinOp { .. } => "Binary arithmetic operation",
|
|
||||||
MirInstructionV2::Compare { .. } => "Compare two values",
|
|
||||||
MirInstructionV2::Branch { .. } => "Conditional branch",
|
|
||||||
MirInstructionV2::Jump { .. } => "Unconditional jump",
|
|
||||||
MirInstructionV2::Phi { .. } => "SSA phi function",
|
|
||||||
MirInstructionV2::Call { .. } => "External function call",
|
|
||||||
MirInstructionV2::Return { .. } => "Return from function",
|
|
||||||
|
|
||||||
// Tier-1
|
|
||||||
MirInstructionV2::NewBox { .. } => "Create Box instance",
|
|
||||||
MirInstructionV2::BoxFieldLoad { .. } => "Load Box field value",
|
|
||||||
MirInstructionV2::BoxFieldStore { .. } => "Store to Box field",
|
|
||||||
MirInstructionV2::BoxCall { .. } => "Box method invocation",
|
|
||||||
MirInstructionV2::Safepoint => "Finalization/interrupt safepoint",
|
|
||||||
MirInstructionV2::RefGet { .. } => "Get reference as value",
|
|
||||||
MirInstructionV2::RefSet { .. } => "Set reference target",
|
|
||||||
MirInstructionV2::WeakNew { .. } => "Create weak reference",
|
|
||||||
MirInstructionV2::WeakLoad { .. } => "Load from weak reference",
|
|
||||||
MirInstructionV2::WeakCheck { .. } => "Check weak reference validity",
|
|
||||||
MirInstructionV2::Send { .. } => "Send Bus message",
|
|
||||||
MirInstructionV2::Recv { .. } => "Receive Bus message",
|
|
||||||
|
|
||||||
// Tier-2
|
|
||||||
MirInstructionV2::TailCall { .. } => "Tail call optimization",
|
|
||||||
MirInstructionV2::Adopt { .. } => "Transfer ownership",
|
|
||||||
MirInstructionV2::Release { .. } => "Release ownership",
|
|
||||||
MirInstructionV2::MemCopy { .. } => "Optimized memory copy",
|
|
||||||
MirInstructionV2::AtomicFence { .. } => "Atomic memory fence",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for MirInstructionV2 {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.description())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::mir::{ValueIdGenerator, BasicBlockIdGenerator};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_instruction_count() {
|
|
||||||
// Verify we have exactly 25 instruction variants
|
|
||||||
// This is a compile-time verification
|
|
||||||
let _tier0_count = 8; // Const, BinOp, Compare, Branch, Jump, Phi, Call, Return
|
|
||||||
let _tier1_count = 12; // NewBox, BoxFieldLoad/Store, BoxCall, Safepoint, RefGet/Set, WeakNew/Load/Check, Send, Recv
|
|
||||||
let _tier2_count = 5; // TailCall, Adopt, Release, MemCopy, AtomicFence
|
|
||||||
let _total = _tier0_count + _tier1_count + _tier2_count;
|
|
||||||
assert_eq!(_total, 25, "MIR instruction set must have exactly 25 instructions");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_effect_categories() {
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
let mut bb_gen = BasicBlockIdGenerator::new();
|
|
||||||
|
|
||||||
// Test pure operations
|
|
||||||
let const_inst = MirInstructionV2::Const {
|
|
||||||
dst: value_gen.next(),
|
|
||||||
value: ConstValue::Integer(42),
|
|
||||||
};
|
|
||||||
assert!(const_inst.effects().is_pure(), "Const should be pure");
|
|
||||||
assert_eq!(const_inst.tier(), 0, "Const should be Tier-0");
|
|
||||||
|
|
||||||
// Test mut operations
|
|
||||||
let store_inst = MirInstructionV2::BoxFieldStore {
|
|
||||||
box_val: value_gen.next(),
|
|
||||||
field: "value".to_string(),
|
|
||||||
value: value_gen.next(),
|
|
||||||
};
|
|
||||||
assert!(store_inst.effects().is_mut(), "BoxFieldStore should be mut");
|
|
||||||
assert_eq!(store_inst.tier(), 1, "BoxFieldStore should be Tier-1");
|
|
||||||
|
|
||||||
// Test io operations
|
|
||||||
let send_inst = MirInstructionV2::Send {
|
|
||||||
bus: value_gen.next(),
|
|
||||||
message: value_gen.next(),
|
|
||||||
};
|
|
||||||
assert!(send_inst.effects().is_io(), "Send should be io");
|
|
||||||
assert_eq!(send_inst.tier(), 1, "Send should be Tier-1");
|
|
||||||
|
|
||||||
// Test control operations
|
|
||||||
let branch_inst = MirInstructionV2::Branch {
|
|
||||||
condition: value_gen.next(),
|
|
||||||
then_bb: bb_gen.next(),
|
|
||||||
else_bb: bb_gen.next(),
|
|
||||||
};
|
|
||||||
assert!(branch_inst.effects().is_control(), "Branch should be control");
|
|
||||||
assert_eq!(branch_inst.tier(), 0, "Branch should be Tier-0");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ownership_operations() {
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
// Test ownership transfer
|
|
||||||
let adopt_inst = MirInstructionV2::Adopt {
|
|
||||||
parent: value_gen.next(),
|
|
||||||
child: value_gen.next(),
|
|
||||||
};
|
|
||||||
assert!(adopt_inst.effects().is_mut(), "Adopt should be mut");
|
|
||||||
assert_eq!(adopt_inst.tier(), 2, "Adopt should be Tier-2");
|
|
||||||
|
|
||||||
// Test weak reference operations
|
|
||||||
let weak_check = MirInstructionV2::WeakCheck {
|
|
||||||
dst: value_gen.next(),
|
|
||||||
weak_ref: value_gen.next(),
|
|
||||||
};
|
|
||||||
assert!(weak_check.effects().is_pure(), "WeakCheck should be pure");
|
|
||||||
assert_eq!(weak_check.tier(), 1, "WeakCheck should be Tier-1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,15 +6,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod instruction;
|
pub mod instruction;
|
||||||
pub mod instruction_v2; // New 25-instruction specification
|
pub mod instruction_introspection; // Introspection helpers for tests (instruction names)
|
||||||
pub mod instruction_introspection; // Introspection helpers for tests (core instruction names)
|
|
||||||
pub mod basic_block;
|
pub mod basic_block;
|
||||||
pub mod function;
|
pub mod function;
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
pub mod loop_builder; // SSA loop construction with phi nodes
|
pub mod loop_builder; // SSA loop construction with phi nodes
|
||||||
pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready)
|
pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready)
|
||||||
pub mod verification;
|
pub mod verification;
|
||||||
pub mod ownership_verifier_simple; // Simple ownership forest verification for current MIR
|
|
||||||
pub mod printer;
|
pub mod printer;
|
||||||
pub mod value_id;
|
pub mod value_id;
|
||||||
pub mod effect;
|
pub mod effect;
|
||||||
@ -26,12 +24,10 @@ pub mod passes; // Optimization subpasses (e.g., type_hints)
|
|||||||
|
|
||||||
// Re-export main types for easy access
|
// Re-export main types for easy access
|
||||||
pub use instruction::{MirInstruction, BinaryOp, CompareOp, UnaryOp, ConstValue, MirType, TypeOpKind, WeakRefOp, BarrierOp};
|
pub use instruction::{MirInstruction, BinaryOp, CompareOp, UnaryOp, ConstValue, MirType, TypeOpKind, WeakRefOp, BarrierOp};
|
||||||
pub use instruction_v2::{MirInstructionV2, AtomicOrdering}; // New 25-instruction set
|
|
||||||
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
||||||
pub use function::{MirFunction, MirModule, FunctionSignature};
|
pub use function::{MirFunction, MirModule, FunctionSignature};
|
||||||
pub use builder::MirBuilder;
|
pub use builder::MirBuilder;
|
||||||
pub use verification::{MirVerifier, VerificationError};
|
pub use verification::{MirVerifier, VerificationError};
|
||||||
pub use ownership_verifier_simple::{OwnershipVerifier, OwnershipError, OwnershipStats}; // Simple ownership forest verification
|
|
||||||
pub use printer::MirPrinter;
|
pub use printer::MirPrinter;
|
||||||
pub use value_id::{ValueId, LocalId, ValueIdGenerator};
|
pub use value_id::{ValueId, LocalId, ValueIdGenerator};
|
||||||
pub use effect::{EffectMask, Effect};
|
pub use effect::{EffectMask, Effect};
|
||||||
@ -85,79 +81,6 @@ impl MirCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Core-13 strict: forbid legacy ops in final MIR when enabled
|
|
||||||
if crate::config::env::mir_core13() || crate::config::env::opt_diag_forbid_legacy() {
|
|
||||||
let mut legacy_count = 0usize;
|
|
||||||
for (_fname, function) in &module.functions {
|
|
||||||
for (_bb, block) in &function.blocks {
|
|
||||||
for inst in &block.instructions {
|
|
||||||
if matches!(inst,
|
|
||||||
MirInstruction::TypeCheck { .. }
|
|
||||||
| MirInstruction::Cast { .. }
|
|
||||||
| MirInstruction::WeakNew { .. }
|
|
||||||
| MirInstruction::WeakLoad { .. }
|
|
||||||
| MirInstruction::BarrierRead { .. }
|
|
||||||
| MirInstruction::BarrierWrite { .. }
|
|
||||||
| MirInstruction::ArrayGet { .. }
|
|
||||||
| MirInstruction::ArraySet { .. }
|
|
||||||
| MirInstruction::RefGet { .. }
|
|
||||||
| MirInstruction::RefSet { .. }
|
|
||||||
| MirInstruction::PluginInvoke { .. }
|
|
||||||
) { legacy_count += 1; }
|
|
||||||
}
|
|
||||||
if let Some(term) = &block.terminator {
|
|
||||||
if matches!(term,
|
|
||||||
MirInstruction::TypeCheck { .. }
|
|
||||||
| MirInstruction::Cast { .. }
|
|
||||||
| MirInstruction::WeakNew { .. }
|
|
||||||
| MirInstruction::WeakLoad { .. }
|
|
||||||
| MirInstruction::BarrierRead { .. }
|
|
||||||
| MirInstruction::BarrierWrite { .. }
|
|
||||||
| MirInstruction::ArrayGet { .. }
|
|
||||||
| MirInstruction::ArraySet { .. }
|
|
||||||
| MirInstruction::RefGet { .. }
|
|
||||||
| MirInstruction::RefSet { .. }
|
|
||||||
| MirInstruction::PluginInvoke { .. }
|
|
||||||
) { legacy_count += 1; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if legacy_count > 0 {
|
|
||||||
return Err(format!("Core-13 strict: final MIR contains {} legacy ops", legacy_count));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Core-13 pure: allow only the 13 canonical ops; reject all others
|
|
||||||
if crate::config::env::mir_core13_pure() {
|
|
||||||
let mut bad = 0usize;
|
|
||||||
let is_allowed = |i: &MirInstruction| -> bool {
|
|
||||||
matches!(i,
|
|
||||||
MirInstruction::Const { .. }
|
|
||||||
| MirInstruction::BinOp { .. }
|
|
||||||
| MirInstruction::Compare { .. }
|
|
||||||
| MirInstruction::Jump { .. }
|
|
||||||
| MirInstruction::Branch { .. }
|
|
||||||
| MirInstruction::Return { .. }
|
|
||||||
| MirInstruction::Phi { .. }
|
|
||||||
| MirInstruction::Call { .. }
|
|
||||||
| MirInstruction::BoxCall { .. }
|
|
||||||
| MirInstruction::ExternCall { .. }
|
|
||||||
| MirInstruction::TypeOp { .. }
|
|
||||||
| MirInstruction::Safepoint
|
|
||||||
| MirInstruction::Barrier { .. }
|
|
||||||
)
|
|
||||||
};
|
|
||||||
for (_fname, function) in &module.functions {
|
|
||||||
for (_bb, block) in &function.blocks {
|
|
||||||
for inst in &block.instructions { if !is_allowed(inst) { bad += 1; } }
|
|
||||||
if let Some(term) = &block.terminator { if !is_allowed(term) { bad += 1; } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if bad > 0 {
|
|
||||||
return Err(format!("Core-13 pure strict: final MIR contains {} non-Core-13 ops", bad));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the generated MIR
|
// Verify the generated MIR
|
||||||
let verification_result = self.verifier.verify_module(&module);
|
let verification_result = self.verifier.verify_module(&module);
|
||||||
|
|
||||||
|
|||||||
@ -1,559 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Ownership Forest Verification System
|
|
||||||
*
|
|
||||||
* Implements ownership forest validation rules per ChatGPT5 specification:
|
|
||||||
* - Ownership forest: strong in-degree ≤ 1
|
|
||||||
* - Strong cycle prohibition: strong edges form DAG (forest)
|
|
||||||
* - Weak/strong interaction: bidirectional strong → error
|
|
||||||
* - RefSet safety: strong→strong requires Release of old target
|
|
||||||
* - WeakLoad/WeakCheck deterministic behavior: null/false on expiration
|
|
||||||
*/
|
|
||||||
|
|
||||||
use super::{MirInstruction, ValueId, MirFunction, MirModule};
|
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
|
||||||
|
|
||||||
/// Ownership forest verification errors
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum OwnershipError {
|
|
||||||
/// Strong reference has multiple owners (violates forest constraint)
|
|
||||||
MultipleStrongOwners {
|
|
||||||
target: ValueId,
|
|
||||||
owners: Vec<ValueId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Strong reference cycle detected (violates DAG constraint)
|
|
||||||
StrongCycle {
|
|
||||||
cycle: Vec<ValueId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Bidirectional strong references (should be strong + weak)
|
|
||||||
BidirectionalStrong {
|
|
||||||
first: ValueId,
|
|
||||||
second: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// RefSet without proper Release of old target
|
|
||||||
UnsafeRefSet {
|
|
||||||
reference: ValueId,
|
|
||||||
old_target: ValueId,
|
|
||||||
new_target: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// WeakLoad on expired reference (should return null deterministically)
|
|
||||||
WeakLoadExpired {
|
|
||||||
weak_ref: ValueId,
|
|
||||||
dead_target: ValueId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Use after Release (accessing released ownership)
|
|
||||||
UseAfterRelease {
|
|
||||||
value: ValueId,
|
|
||||||
released_at: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Invalid ownership transfer via Adopt
|
|
||||||
InvalidAdopt {
|
|
||||||
parent: ValueId,
|
|
||||||
child: ValueId,
|
|
||||||
reason: String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ownership forest verifier
|
|
||||||
pub struct OwnershipVerifier {
|
|
||||||
/// Strong ownership edges: child -> parent
|
|
||||||
strong_edges: HashMap<ValueId, ValueId>,
|
|
||||||
|
|
||||||
/// Weak reference edges: weak_ref -> target
|
|
||||||
weak_edges: HashMap<ValueId, ValueId>,
|
|
||||||
|
|
||||||
/// Released references (no longer valid for ownership)
|
|
||||||
released: HashSet<ValueId>,
|
|
||||||
|
|
||||||
/// Track live weak references for liveness checking
|
|
||||||
live_weak_refs: HashSet<ValueId>,
|
|
||||||
|
|
||||||
/// Track dead targets for WeakLoad/WeakCheck determinism
|
|
||||||
dead_targets: HashSet<ValueId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OwnershipVerifier {
|
|
||||||
/// Create a new ownership verifier
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
strong_edges: HashMap::new(),
|
|
||||||
weak_edges: HashMap::new(),
|
|
||||||
released: HashSet::new(),
|
|
||||||
live_weak_refs: HashSet::new(),
|
|
||||||
dead_targets: HashSet::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify ownership forest properties for an entire module
|
|
||||||
pub fn verify_module(&mut self, module: &MirModule) -> Result<(), Vec<OwnershipError>> {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
|
|
||||||
for function in module.functions.values() {
|
|
||||||
if let Err(mut function_errors) = self.verify_function(function) {
|
|
||||||
errors.append(&mut function_errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.is_empty() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify ownership forest properties for a single function
|
|
||||||
pub fn verify_function(&mut self, function: &MirFunction) -> Result<(), Vec<OwnershipError>> {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
|
|
||||||
// Reset state for this function
|
|
||||||
self.strong_edges.clear();
|
|
||||||
self.weak_edges.clear();
|
|
||||||
self.released.clear();
|
|
||||||
self.live_weak_refs.clear();
|
|
||||||
self.dead_targets.clear();
|
|
||||||
|
|
||||||
// Process all instructions to build ownership graph
|
|
||||||
for block in function.blocks.values() {
|
|
||||||
for instruction in block.all_instructions() {
|
|
||||||
if let Err(mut inst_errors) = self.process_instruction(instruction) {
|
|
||||||
errors.append(&mut inst_errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify global ownership forest properties
|
|
||||||
if let Err(mut forest_errors) = self.verify_ownership_forest() {
|
|
||||||
errors.append(&mut forest_errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.is_empty() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Process a single instruction and update ownership state
|
|
||||||
fn process_instruction(&mut self, instruction: &MirInstruction) -> Result<(), Vec<OwnershipError>> {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
|
|
||||||
match instruction {
|
|
||||||
// NewBox creates a new ownership root
|
|
||||||
MirInstructionV2::NewBox { dst, .. } => {
|
|
||||||
// New boxes are ownership roots (no parent)
|
|
||||||
// Clear any existing ownership for this value
|
|
||||||
self.strong_edges.remove(dst);
|
|
||||||
},
|
|
||||||
|
|
||||||
// RefSet changes ownership relationships
|
|
||||||
MirInstructionV2::RefSet { reference, new_target } => {
|
|
||||||
// Check if the reference currently has a strong target
|
|
||||||
if let Some(old_target) = self.strong_edges.get(reference) {
|
|
||||||
// Strong→Strong replacement requires explicit Release
|
|
||||||
if !self.released.contains(old_target) {
|
|
||||||
errors.push(OwnershipError::UnsafeRefSet {
|
|
||||||
reference: *reference,
|
|
||||||
old_target: *old_target,
|
|
||||||
new_target: *new_target,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set new strong ownership
|
|
||||||
self.strong_edges.insert(*reference, *new_target);
|
|
||||||
|
|
||||||
// Verify no multiple strong owners after this change
|
|
||||||
if let Err(mut multiple_errors) = self.check_multiple_owners(*new_target) {
|
|
||||||
errors.append(&mut multiple_errors);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Adopt transfers ownership
|
|
||||||
MirInstructionV2::Adopt { parent, child } => {
|
|
||||||
// Verify the adoption is valid
|
|
||||||
if self.released.contains(child) {
|
|
||||||
errors.push(OwnershipError::InvalidAdopt {
|
|
||||||
parent: *parent,
|
|
||||||
child: *child,
|
|
||||||
reason: "Cannot adopt released reference".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for cycle creation
|
|
||||||
if self.would_create_cycle(*parent, *child) {
|
|
||||||
errors.push(OwnershipError::InvalidAdopt {
|
|
||||||
parent: *parent,
|
|
||||||
child: *child,
|
|
||||||
reason: "Would create strong cycle".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establish strong ownership
|
|
||||||
self.strong_edges.insert(*child, *parent);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Release removes ownership
|
|
||||||
MirInstructionV2::Release { reference } => {
|
|
||||||
self.strong_edges.remove(reference);
|
|
||||||
self.released.insert(*reference);
|
|
||||||
|
|
||||||
// Mark any targets of this reference as potentially dead
|
|
||||||
if let Some(target) = self.weak_edges.get(reference) {
|
|
||||||
self.dead_targets.insert(*target);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// WeakNew creates weak reference
|
|
||||||
MirInstructionV2::WeakNew { dst, box_val } => {
|
|
||||||
self.weak_edges.insert(*dst, *box_val);
|
|
||||||
self.live_weak_refs.insert(*dst);
|
|
||||||
},
|
|
||||||
|
|
||||||
// WeakLoad checks liveness
|
|
||||||
MirInstructionV2::WeakLoad { weak_ref, .. } => {
|
|
||||||
if let Some(target) = self.weak_edges.get(weak_ref) {
|
|
||||||
if self.dead_targets.contains(target) {
|
|
||||||
// This is actually expected behavior - WeakLoad should return null
|
|
||||||
// We track this for deterministic behavior verification
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// WeakCheck verifies liveness
|
|
||||||
MirInstructionV2::WeakCheck { weak_ref, .. } => {
|
|
||||||
if let Some(target) = self.weak_edges.get(weak_ref) {
|
|
||||||
if self.dead_targets.contains(target) {
|
|
||||||
// This is expected - WeakCheck should return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Other instructions don't affect ownership
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.is_empty() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify global ownership forest properties
|
|
||||||
fn verify_ownership_forest(&self) -> Result<(), Vec<OwnershipError>> {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
|
|
||||||
// Check for multiple strong owners (violates forest constraint)
|
|
||||||
let mut target_owners: HashMap<ValueId, Vec<ValueId>> = HashMap::new();
|
|
||||||
for (child, parent) in &self.strong_edges {
|
|
||||||
target_owners.entry(*parent).or_insert_with(Vec::new).push(*child);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (target, owners) in target_owners {
|
|
||||||
if owners.len() > 1 {
|
|
||||||
errors.push(OwnershipError::MultipleStrongOwners { target, owners });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for strong cycles (violates DAG constraint)
|
|
||||||
if let Some(cycle) = self.find_strong_cycle() {
|
|
||||||
errors.push(OwnershipError::StrongCycle { cycle });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for bidirectional strong edges
|
|
||||||
for (child, parent) in &self.strong_edges {
|
|
||||||
if let Some(grandparent) = self.strong_edges.get(parent) {
|
|
||||||
if grandparent == child {
|
|
||||||
errors.push(OwnershipError::BidirectionalStrong {
|
|
||||||
first: *child,
|
|
||||||
second: *parent,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.is_empty() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if a value has multiple strong owners
|
|
||||||
fn check_multiple_owners(&self, target: ValueId) -> Result<(), Vec<OwnershipError>> {
|
|
||||||
let owners: Vec<ValueId> = self.strong_edges
|
|
||||||
.iter()
|
|
||||||
.filter(|(_, &parent)| parent == target)
|
|
||||||
.map(|(&child, _)| child)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if owners.len() > 1 {
|
|
||||||
Err(vec![OwnershipError::MultipleStrongOwners { target, owners }])
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if adding an edge would create a cycle
|
|
||||||
fn would_create_cycle(&self, parent: ValueId, child: ValueId) -> bool {
|
|
||||||
// DFS to see if parent is reachable from child through strong edges
|
|
||||||
let mut visited = HashSet::new();
|
|
||||||
let mut stack = vec![child];
|
|
||||||
|
|
||||||
while let Some(current) = stack.pop() {
|
|
||||||
if current == parent {
|
|
||||||
return true; // Cycle detected
|
|
||||||
}
|
|
||||||
|
|
||||||
if visited.insert(current) {
|
|
||||||
// Add all strong children of current to stack
|
|
||||||
for (&potential_child, &potential_parent) in &self.strong_edges {
|
|
||||||
if potential_parent == current {
|
|
||||||
stack.push(potential_child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find any strong cycle in the ownership graph
|
|
||||||
fn find_strong_cycle(&self) -> Option<Vec<ValueId>> {
|
|
||||||
let mut visited = HashSet::new();
|
|
||||||
let mut rec_stack = HashSet::new();
|
|
||||||
let mut path = Vec::new();
|
|
||||||
|
|
||||||
// Get all nodes in the graph
|
|
||||||
let mut all_nodes = HashSet::new();
|
|
||||||
for (&child, &parent) in &self.strong_edges {
|
|
||||||
all_nodes.insert(child);
|
|
||||||
all_nodes.insert(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// DFS from each unvisited node
|
|
||||||
for &node in &all_nodes {
|
|
||||||
if !visited.contains(&node) {
|
|
||||||
if let Some(cycle) = self.dfs_cycle(node, &mut visited, &mut rec_stack, &mut path) {
|
|
||||||
return Some(cycle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DFS cycle detection helper
|
|
||||||
fn dfs_cycle(
|
|
||||||
&self,
|
|
||||||
node: ValueId,
|
|
||||||
visited: &mut HashSet<ValueId>,
|
|
||||||
rec_stack: &mut HashSet<ValueId>,
|
|
||||||
path: &mut Vec<ValueId>,
|
|
||||||
) -> Option<Vec<ValueId>> {
|
|
||||||
visited.insert(node);
|
|
||||||
rec_stack.insert(node);
|
|
||||||
path.push(node);
|
|
||||||
|
|
||||||
// Visit all strong children
|
|
||||||
for (&child, &parent) in &self.strong_edges {
|
|
||||||
if parent == node {
|
|
||||||
if rec_stack.contains(&child) {
|
|
||||||
// Found cycle - return path from child to current
|
|
||||||
let cycle_start = path.iter().position(|&x| x == child).unwrap();
|
|
||||||
return Some(path[cycle_start..].to_vec());
|
|
||||||
}
|
|
||||||
|
|
||||||
if !visited.contains(&child) {
|
|
||||||
if let Some(cycle) = self.dfs_cycle(child, visited, rec_stack, path) {
|
|
||||||
return Some(cycle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rec_stack.remove(&node);
|
|
||||||
path.pop();
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get ownership statistics for debugging
|
|
||||||
pub fn ownership_stats(&self) -> OwnershipStats {
|
|
||||||
OwnershipStats {
|
|
||||||
strong_edges: self.strong_edges.len(),
|
|
||||||
weak_edges: self.weak_edges.len(),
|
|
||||||
released_count: self.released.len(),
|
|
||||||
live_weak_refs: self.live_weak_refs.len(),
|
|
||||||
dead_targets: self.dead_targets.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ownership statistics for debugging and analysis
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct OwnershipStats {
|
|
||||||
pub strong_edges: usize,
|
|
||||||
pub weak_edges: usize,
|
|
||||||
pub released_count: usize,
|
|
||||||
pub live_weak_refs: usize,
|
|
||||||
pub dead_targets: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for OwnershipVerifier {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::mir::{ValueIdGenerator, ConstValue};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ownership_forest_basic() {
|
|
||||||
let mut verifier = OwnershipVerifier::new();
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let parent = value_gen.next();
|
|
||||||
let child = value_gen.next();
|
|
||||||
|
|
||||||
// Create ownership relationship
|
|
||||||
let adopt = MirInstructionV2::Adopt { parent, child };
|
|
||||||
assert!(verifier.process_instruction(&adopt).is_ok());
|
|
||||||
|
|
||||||
// Verify forest properties
|
|
||||||
assert!(verifier.verify_ownership_forest().is_ok());
|
|
||||||
|
|
||||||
let stats = verifier.ownership_stats();
|
|
||||||
assert_eq!(stats.strong_edges, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_multiple_owners_error() {
|
|
||||||
let mut verifier = OwnershipVerifier::new();
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let parent1 = value_gen.next();
|
|
||||||
let parent2 = value_gen.next();
|
|
||||||
let child = value_gen.next();
|
|
||||||
|
|
||||||
// Create multiple ownership (invalid)
|
|
||||||
verifier.strong_edges.insert(child, parent1);
|
|
||||||
verifier.strong_edges.insert(child, parent2); // This overwrites, but we'll manually create conflict
|
|
||||||
|
|
||||||
// Manually create the conflicting state for testing
|
|
||||||
verifier.strong_edges.clear();
|
|
||||||
verifier.strong_edges.insert(parent1, child); // parent1 -> child
|
|
||||||
verifier.strong_edges.insert(parent2, child); // parent2 -> child (multiple owners of child)
|
|
||||||
|
|
||||||
let result = verifier.verify_ownership_forest();
|
|
||||||
assert!(result.is_err());
|
|
||||||
|
|
||||||
if let Err(errors) = result {
|
|
||||||
assert!(errors.iter().any(|e| matches!(e, OwnershipError::MultipleStrongOwners { .. })));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_strong_cycle_detection() {
|
|
||||||
let mut verifier = OwnershipVerifier::new();
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let a = value_gen.next();
|
|
||||||
let b = value_gen.next();
|
|
||||||
let c = value_gen.next();
|
|
||||||
|
|
||||||
// Create cycle: a -> b -> c -> a
|
|
||||||
verifier.strong_edges.insert(b, a);
|
|
||||||
verifier.strong_edges.insert(c, b);
|
|
||||||
verifier.strong_edges.insert(a, c);
|
|
||||||
|
|
||||||
let result = verifier.verify_ownership_forest();
|
|
||||||
assert!(result.is_err());
|
|
||||||
|
|
||||||
if let Err(errors) = result {
|
|
||||||
assert!(errors.iter().any(|e| matches!(e, OwnershipError::StrongCycle { .. })));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_weak_reference_safety() {
|
|
||||||
let mut verifier = OwnershipVerifier::new();
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let target = value_gen.next();
|
|
||||||
let weak_ref = value_gen.next();
|
|
||||||
|
|
||||||
// Create weak reference
|
|
||||||
let weak_new = MirInstructionV2::WeakNew {
|
|
||||||
dst: weak_ref,
|
|
||||||
box_val: target,
|
|
||||||
};
|
|
||||||
assert!(verifier.process_instruction(&weak_new).is_ok());
|
|
||||||
|
|
||||||
// Release the target
|
|
||||||
let release = MirInstructionV2::Release {
|
|
||||||
reference: target,
|
|
||||||
};
|
|
||||||
assert!(verifier.process_instruction(&release).is_ok());
|
|
||||||
|
|
||||||
// WeakLoad should handle expired reference gracefully
|
|
||||||
let weak_load = MirInstructionV2::WeakLoad {
|
|
||||||
dst: value_gen.next(),
|
|
||||||
weak_ref,
|
|
||||||
};
|
|
||||||
assert!(verifier.process_instruction(&weak_load).is_ok());
|
|
||||||
|
|
||||||
let stats = verifier.ownership_stats();
|
|
||||||
assert_eq!(stats.weak_edges, 1);
|
|
||||||
assert_eq!(stats.dead_targets, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_unsafe_ref_set() {
|
|
||||||
let mut verifier = OwnershipVerifier::new();
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let reference = value_gen.next();
|
|
||||||
let old_target = value_gen.next();
|
|
||||||
let new_target = value_gen.next();
|
|
||||||
|
|
||||||
// Set initial strong ownership
|
|
||||||
verifier.strong_edges.insert(reference, old_target);
|
|
||||||
|
|
||||||
// Try to change without Release (should error)
|
|
||||||
let ref_set = MirInstructionV2::RefSet { reference, new_target };
|
|
||||||
let result = verifier.process_instruction(&ref_set);
|
|
||||||
|
|
||||||
assert!(result.is_err());
|
|
||||||
if let Err(errors) = result {
|
|
||||||
assert!(errors.iter().any(|e| matches!(e, OwnershipError::UnsafeRefSet { .. })));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_safe_ref_set_with_release() {
|
|
||||||
let mut verifier = OwnershipVerifier::new();
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let reference = value_gen.next();
|
|
||||||
let old_target = value_gen.next();
|
|
||||||
let new_target = value_gen.next();
|
|
||||||
|
|
||||||
// Set initial strong ownership
|
|
||||||
verifier.strong_edges.insert(reference, old_target);
|
|
||||||
|
|
||||||
// Release old target first
|
|
||||||
verifier.released.insert(old_target);
|
|
||||||
|
|
||||||
// Now RefSet should be safe
|
|
||||||
let ref_set = MirInstructionV2::RefSet { reference, new_target };
|
|
||||||
assert!(verifier.process_instruction(&ref_set).is_ok());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,370 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Ownership Forest Verification System (Simplified for Current MIR)
|
|
||||||
*
|
|
||||||
* Basic implementation working with current MirInstruction enum
|
|
||||||
* Will be expanded when MirInstructionV2 is integrated
|
|
||||||
*/
|
|
||||||
|
|
||||||
use super::{MirInstruction, ValueId, MirFunction, MirModule};
|
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
|
|
||||||
/// Ownership forest verification errors
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum OwnershipError {
|
|
||||||
/// Strong reference has multiple owners (violates forest constraint)
|
|
||||||
MultipleStrongOwners {
|
|
||||||
target: ValueId,
|
|
||||||
owners: Vec<ValueId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Strong reference cycle detected (violates DAG constraint)
|
|
||||||
StrongCycle {
|
|
||||||
cycle: Vec<ValueId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// RefSet without proper Release of old target
|
|
||||||
UnsafeRefSet {
|
|
||||||
reference: ValueId,
|
|
||||||
old_target: ValueId,
|
|
||||||
new_target: ValueId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ownership forest verifier
|
|
||||||
pub struct OwnershipVerifier {
|
|
||||||
/// Strong ownership edges: child -> parent
|
|
||||||
strong_edges: HashMap<ValueId, ValueId>,
|
|
||||||
|
|
||||||
/// Weak reference edges: weak_ref -> target
|
|
||||||
weak_edges: HashMap<ValueId, ValueId>,
|
|
||||||
|
|
||||||
/// Released references (no longer valid for ownership)
|
|
||||||
released: HashSet<ValueId>,
|
|
||||||
|
|
||||||
/// Track live weak references for liveness checking
|
|
||||||
live_weak_refs: HashSet<ValueId>,
|
|
||||||
|
|
||||||
/// Track dead targets for WeakLoad/WeakCheck determinism
|
|
||||||
dead_targets: HashSet<ValueId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OwnershipVerifier {
|
|
||||||
/// Create a new ownership verifier
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
strong_edges: HashMap::new(),
|
|
||||||
weak_edges: HashMap::new(),
|
|
||||||
released: HashSet::new(),
|
|
||||||
live_weak_refs: HashSet::new(),
|
|
||||||
dead_targets: HashSet::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify ownership forest properties for an entire module
|
|
||||||
pub fn verify_module(&mut self, module: &MirModule) -> Result<(), Vec<OwnershipError>> {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
|
|
||||||
for function in module.functions.values() {
|
|
||||||
if let Err(mut function_errors) = self.verify_function(function) {
|
|
||||||
errors.append(&mut function_errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.is_empty() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify ownership forest properties for a single function
|
|
||||||
pub fn verify_function(&mut self, function: &MirFunction) -> Result<(), Vec<OwnershipError>> {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
|
|
||||||
// Reset state for this function
|
|
||||||
self.strong_edges.clear();
|
|
||||||
self.weak_edges.clear();
|
|
||||||
self.released.clear();
|
|
||||||
self.live_weak_refs.clear();
|
|
||||||
self.dead_targets.clear();
|
|
||||||
|
|
||||||
// Process all instructions to build ownership graph
|
|
||||||
for block in function.blocks.values() {
|
|
||||||
for instruction in block.all_instructions() {
|
|
||||||
if let Err(mut inst_errors) = self.process_instruction(instruction) {
|
|
||||||
errors.append(&mut inst_errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify global ownership forest properties
|
|
||||||
if let Err(mut forest_errors) = self.verify_ownership_forest() {
|
|
||||||
errors.append(&mut forest_errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.is_empty() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Process a single instruction and update ownership state
|
|
||||||
pub fn process_instruction(&mut self, instruction: &MirInstruction) -> Result<(), Vec<OwnershipError>> {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
|
|
||||||
match instruction {
|
|
||||||
// NewBox creates a new ownership root
|
|
||||||
MirInstruction::NewBox { dst, .. } => {
|
|
||||||
// New boxes are ownership roots (no parent)
|
|
||||||
self.strong_edges.remove(dst);
|
|
||||||
},
|
|
||||||
|
|
||||||
// RefSet changes ownership relationships
|
|
||||||
MirInstruction::RefSet { reference, field: _, value } => {
|
|
||||||
// Check if the reference currently has a strong target
|
|
||||||
if let Some(old_target) = self.strong_edges.get(reference) {
|
|
||||||
// Strong→Strong replacement requires explicit Release
|
|
||||||
if !self.released.contains(old_target) {
|
|
||||||
errors.push(OwnershipError::UnsafeRefSet {
|
|
||||||
reference: *reference,
|
|
||||||
old_target: *old_target,
|
|
||||||
new_target: *value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set new strong ownership
|
|
||||||
self.strong_edges.insert(*reference, *value);
|
|
||||||
|
|
||||||
// Verify no multiple owners after this change
|
|
||||||
if let Err(mut multiple_errors) = self.check_multiple_owners(*value) {
|
|
||||||
errors.append(&mut multiple_errors);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// WeakNew creates weak reference
|
|
||||||
MirInstruction::WeakNew { dst, box_val } => {
|
|
||||||
self.weak_edges.insert(*dst, *box_val);
|
|
||||||
self.live_weak_refs.insert(*dst);
|
|
||||||
},
|
|
||||||
|
|
||||||
// WeakLoad checks liveness
|
|
||||||
MirInstruction::WeakLoad { weak_ref, .. } => {
|
|
||||||
if let Some(target) = self.weak_edges.get(weak_ref) {
|
|
||||||
if self.dead_targets.contains(target) {
|
|
||||||
// This is expected behavior - WeakLoad should return null deterministically
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Other instructions don't affect ownership in current implementation
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.is_empty() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify global ownership forest properties
|
|
||||||
fn verify_ownership_forest(&self) -> Result<(), Vec<OwnershipError>> {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
|
|
||||||
// Check for multiple strong owners (violates forest constraint)
|
|
||||||
let mut target_owners: HashMap<ValueId, Vec<ValueId>> = HashMap::new();
|
|
||||||
for (child, parent) in &self.strong_edges {
|
|
||||||
target_owners.entry(*parent).or_insert_with(Vec::new).push(*child);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (target, owners) in target_owners {
|
|
||||||
if owners.len() > 1 {
|
|
||||||
errors.push(OwnershipError::MultipleStrongOwners { target, owners });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for strong cycles (violates DAG constraint)
|
|
||||||
if let Some(cycle) = self.find_strong_cycle() {
|
|
||||||
errors.push(OwnershipError::StrongCycle { cycle });
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.is_empty() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if a value has multiple strong owners
|
|
||||||
fn check_multiple_owners(&self, target: ValueId) -> Result<(), Vec<OwnershipError>> {
|
|
||||||
let owners: Vec<ValueId> = self.strong_edges
|
|
||||||
.iter()
|
|
||||||
.filter(|(_, &parent)| parent == target)
|
|
||||||
.map(|(&child, _)| child)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if owners.len() > 1 {
|
|
||||||
Err(vec![OwnershipError::MultipleStrongOwners { target, owners }])
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find any strong cycle in the ownership graph
|
|
||||||
fn find_strong_cycle(&self) -> Option<Vec<ValueId>> {
|
|
||||||
let mut visited = HashSet::new();
|
|
||||||
let mut rec_stack = HashSet::new();
|
|
||||||
let mut path = Vec::new();
|
|
||||||
|
|
||||||
// Get all nodes in the graph
|
|
||||||
let mut all_nodes = HashSet::new();
|
|
||||||
for (&child, &parent) in &self.strong_edges {
|
|
||||||
all_nodes.insert(child);
|
|
||||||
all_nodes.insert(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// DFS from each unvisited node
|
|
||||||
for &node in &all_nodes {
|
|
||||||
if !visited.contains(&node) {
|
|
||||||
if let Some(cycle) = self.dfs_cycle(node, &mut visited, &mut rec_stack, &mut path) {
|
|
||||||
return Some(cycle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DFS cycle detection helper
|
|
||||||
fn dfs_cycle(
|
|
||||||
&self,
|
|
||||||
node: ValueId,
|
|
||||||
visited: &mut HashSet<ValueId>,
|
|
||||||
rec_stack: &mut HashSet<ValueId>,
|
|
||||||
path: &mut Vec<ValueId>,
|
|
||||||
) -> Option<Vec<ValueId>> {
|
|
||||||
visited.insert(node);
|
|
||||||
rec_stack.insert(node);
|
|
||||||
path.push(node);
|
|
||||||
|
|
||||||
// Visit all strong children
|
|
||||||
for (&child, &parent) in &self.strong_edges {
|
|
||||||
if parent == node {
|
|
||||||
if rec_stack.contains(&child) {
|
|
||||||
// Found cycle - return path from child to current
|
|
||||||
let cycle_start = path.iter().position(|&x| x == child).unwrap();
|
|
||||||
return Some(path[cycle_start..].to_vec());
|
|
||||||
}
|
|
||||||
|
|
||||||
if !visited.contains(&child) {
|
|
||||||
if let Some(cycle) = self.dfs_cycle(child, visited, rec_stack, path) {
|
|
||||||
return Some(cycle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rec_stack.remove(&node);
|
|
||||||
path.pop();
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get ownership statistics for debugging
|
|
||||||
pub fn ownership_stats(&self) -> OwnershipStats {
|
|
||||||
OwnershipStats {
|
|
||||||
strong_edges: self.strong_edges.len(),
|
|
||||||
weak_edges: self.weak_edges.len(),
|
|
||||||
released_count: self.released.len(),
|
|
||||||
live_weak_refs: self.live_weak_refs.len(),
|
|
||||||
dead_targets: self.dead_targets.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ownership statistics for debugging and analysis
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct OwnershipStats {
|
|
||||||
pub strong_edges: usize,
|
|
||||||
pub weak_edges: usize,
|
|
||||||
pub released_count: usize,
|
|
||||||
pub live_weak_refs: usize,
|
|
||||||
pub dead_targets: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for OwnershipVerifier {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::mir::ValueIdGenerator;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ownership_forest_basic() {
|
|
||||||
let mut verifier = OwnershipVerifier::new();
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let parent = value_gen.next();
|
|
||||||
let child = value_gen.next();
|
|
||||||
|
|
||||||
// Create NewBox instruction (current MirInstruction)
|
|
||||||
let new_box = MirInstruction::NewBox {
|
|
||||||
dst: parent,
|
|
||||||
box_type: "TestBox".to_string(),
|
|
||||||
args: vec![]
|
|
||||||
};
|
|
||||||
assert!(verifier.process_instruction(&new_box).is_ok());
|
|
||||||
|
|
||||||
// Verify forest properties
|
|
||||||
assert!(verifier.verify_ownership_forest().is_ok());
|
|
||||||
|
|
||||||
let stats = verifier.ownership_stats();
|
|
||||||
assert_eq!(stats.strong_edges, 0); // NewBox doesn't create edges, just roots
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_weak_reference_tracking() {
|
|
||||||
let mut verifier = OwnershipVerifier::new();
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let target = value_gen.next();
|
|
||||||
let weak_ref = value_gen.next();
|
|
||||||
|
|
||||||
// Create weak reference
|
|
||||||
let weak_new = MirInstruction::WeakNew { dst: weak_ref, box_val: target };
|
|
||||||
assert!(verifier.process_instruction(&weak_new).is_ok(), "Weak reference creation should succeed");
|
|
||||||
|
|
||||||
let stats = verifier.ownership_stats();
|
|
||||||
assert_eq!(stats.weak_edges, 1, "Should have one weak edge");
|
|
||||||
assert_eq!(stats.live_weak_refs, 1, "Should have one live weak reference");
|
|
||||||
|
|
||||||
// WeakLoad should handle gracefully
|
|
||||||
let weak_load = MirInstruction::WeakLoad { dst: value_gen.next(), weak_ref };
|
|
||||||
assert!(verifier.process_instruction(&weak_load).is_ok(), "WeakLoad should succeed");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_basic_ref_set() {
|
|
||||||
let mut verifier = OwnershipVerifier::new();
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let reference = value_gen.next();
|
|
||||||
let target = value_gen.next();
|
|
||||||
|
|
||||||
// Basic RefSet without prior ownership (should succeed)
|
|
||||||
let ref_set = MirInstruction::RefSet {
|
|
||||||
reference,
|
|
||||||
field: "test".to_string(),
|
|
||||||
value: target
|
|
||||||
};
|
|
||||||
assert!(verifier.process_instruction(&ref_set).is_ok(), "Basic RefSet should succeed");
|
|
||||||
|
|
||||||
let stats = verifier.ownership_stats();
|
|
||||||
assert_eq!(stats.strong_edges, 1, "Should have one strong edge");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
use nyash_rust::mir::instruction_introspection;
|
use nyash_rust::mir::instruction_introspection;
|
||||||
|
|
||||||
// Core-15: enforce fixed instruction count at 15 (migration mode; docs may differ)
|
// MIR14: ensure instruction count stays fixed at 14
|
||||||
#[test]
|
#[test]
|
||||||
fn mir_core15_instruction_count() {
|
fn mir14_instruction_count() {
|
||||||
let impl_names = instruction_introspection::core15_instruction_names();
|
let impl_names = instruction_introspection::mir14_instruction_names();
|
||||||
assert_eq!(impl_names.len(), 15, "Core-15 must contain exactly 15 instructions");
|
assert_eq!(impl_names.len(), 14, "MIR14 must contain exactly 14 instructions");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,479 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Phase 8.5 MIR 25-Instruction Hierarchical Implementation Tests
|
|
||||||
*
|
|
||||||
* Comprehensive test suite for the ChatGPT5 + AI Council designed MIR system
|
|
||||||
*/
|
|
||||||
|
|
||||||
#![cfg(feature = "mir-v2")]
|
|
||||||
use nyash_rust::mir::{
|
|
||||||
MirInstructionV2, ConstValue, BinaryOp, CompareOp, AtomicOrdering,
|
|
||||||
EffectMask, Effect, ValueIdGenerator, BasicBlockIdGenerator,
|
|
||||||
OwnershipVerifier, OwnershipError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Test that we have exactly 25 instructions in the specification
|
|
||||||
#[test]
|
|
||||||
fn test_mir_instruction_count() {
|
|
||||||
// This is verified at compile time by the instruction enum
|
|
||||||
// Each tier should have the correct count:
|
|
||||||
// Tier-0: 8 instructions
|
|
||||||
// Tier-1: 12 instructions
|
|
||||||
// Tier-2: 5 instructions
|
|
||||||
// Total: 25 instructions
|
|
||||||
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
let mut bb_gen = BasicBlockIdGenerator::new();
|
|
||||||
|
|
||||||
// Tier-0: Universal Core (8 instructions)
|
|
||||||
let tier0_instructions = vec![
|
|
||||||
MirInstructionV2::Const { dst: value_gen.next(), value: ConstValue::Integer(42) },
|
|
||||||
MirInstructionV2::BinOp { dst: value_gen.next(), op: BinaryOp::Add, lhs: value_gen.next(), rhs: value_gen.next() },
|
|
||||||
MirInstructionV2::Compare { dst: value_gen.next(), op: CompareOp::Eq, lhs: value_gen.next(), rhs: value_gen.next() },
|
|
||||||
MirInstructionV2::Branch { condition: value_gen.next(), then_bb: bb_gen.next(), else_bb: bb_gen.next() },
|
|
||||||
MirInstructionV2::Jump { target: bb_gen.next() },
|
|
||||||
MirInstructionV2::Phi { dst: value_gen.next(), inputs: vec![(bb_gen.next(), value_gen.next())] },
|
|
||||||
MirInstructionV2::Call { dst: Some(value_gen.next()), func: value_gen.next(), args: vec![], effects: EffectMask::PURE },
|
|
||||||
MirInstructionV2::Return { value: Some(value_gen.next()) },
|
|
||||||
];
|
|
||||||
|
|
||||||
for inst in &tier0_instructions {
|
|
||||||
assert_eq!(inst.tier(), 0, "Tier-0 instruction should have tier 0");
|
|
||||||
}
|
|
||||||
assert_eq!(tier0_instructions.len(), 8, "Tier-0 should have exactly 8 instructions");
|
|
||||||
|
|
||||||
// Tier-1: Nyash Semantics (12 instructions)
|
|
||||||
let tier1_instructions = vec![
|
|
||||||
MirInstructionV2::NewBox { dst: value_gen.next(), box_type: "TestBox".to_string(), args: vec![] },
|
|
||||||
MirInstructionV2::BoxFieldLoad { dst: value_gen.next(), box_val: value_gen.next(), field: "value".to_string() },
|
|
||||||
MirInstructionV2::BoxFieldStore { box_val: value_gen.next(), field: "value".to_string(), value: value_gen.next() },
|
|
||||||
MirInstructionV2::BoxCall { dst: Some(value_gen.next()), box_val: value_gen.next(), method: "test".to_string(), args: vec![], effects: EffectMask::PURE },
|
|
||||||
MirInstructionV2::Safepoint,
|
|
||||||
MirInstructionV2::RefGet { dst: value_gen.next(), reference: value_gen.next() },
|
|
||||||
MirInstructionV2::RefSet { reference: value_gen.next(), new_target: value_gen.next() },
|
|
||||||
MirInstructionV2::WeakNew { dst: value_gen.next(), box_val: value_gen.next() },
|
|
||||||
MirInstructionV2::WeakLoad { dst: value_gen.next(), weak_ref: value_gen.next() },
|
|
||||||
MirInstructionV2::WeakCheck { dst: value_gen.next(), weak_ref: value_gen.next() },
|
|
||||||
MirInstructionV2::Send { bus: value_gen.next(), message: value_gen.next() },
|
|
||||||
MirInstructionV2::Recv { dst: value_gen.next(), bus: value_gen.next() },
|
|
||||||
];
|
|
||||||
|
|
||||||
for inst in &tier1_instructions {
|
|
||||||
assert_eq!(inst.tier(), 1, "Tier-1 instruction should have tier 1");
|
|
||||||
}
|
|
||||||
assert_eq!(tier1_instructions.len(), 12, "Tier-1 should have exactly 12 instructions");
|
|
||||||
|
|
||||||
// Tier-2: Implementation Assistance (5 instructions)
|
|
||||||
let tier2_instructions = vec![
|
|
||||||
MirInstructionV2::TailCall { func: value_gen.next(), args: vec![], effects: EffectMask::PURE },
|
|
||||||
MirInstructionV2::Adopt { parent: value_gen.next(), child: value_gen.next() },
|
|
||||||
MirInstructionV2::Release { reference: value_gen.next() },
|
|
||||||
MirInstructionV2::MemCopy { dest: value_gen.next(), src: value_gen.next(), size: value_gen.next() },
|
|
||||||
MirInstructionV2::AtomicFence { ordering: AtomicOrdering::SeqCst },
|
|
||||||
];
|
|
||||||
|
|
||||||
for inst in &tier2_instructions {
|
|
||||||
assert_eq!(inst.tier(), 2, "Tier-2 instruction should have tier 2");
|
|
||||||
}
|
|
||||||
assert_eq!(tier2_instructions.len(), 5, "Tier-2 should have exactly 5 instructions");
|
|
||||||
|
|
||||||
// Total verification
|
|
||||||
let total_instructions = tier0_instructions.len() + tier1_instructions.len() + tier2_instructions.len();
|
|
||||||
assert_eq!(total_instructions, 25, "Total instruction count must be exactly 25");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test the 4-category effect system
|
|
||||||
#[test]
|
|
||||||
fn test_effect_categories() {
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
let mut bb_gen = BasicBlockIdGenerator::new();
|
|
||||||
|
|
||||||
// Test Pure effects
|
|
||||||
let pure_instructions = vec![
|
|
||||||
MirInstructionV2::Const { dst: value_gen.next(), value: ConstValue::Integer(42) },
|
|
||||||
MirInstructionV2::BinOp { dst: value_gen.next(), op: BinaryOp::Add, lhs: value_gen.next(), rhs: value_gen.next() },
|
|
||||||
MirInstructionV2::Compare { dst: value_gen.next(), op: CompareOp::Eq, lhs: value_gen.next(), rhs: value_gen.next() },
|
|
||||||
MirInstructionV2::Phi { dst: value_gen.next(), inputs: vec![(bb_gen.next(), value_gen.next())] },
|
|
||||||
MirInstructionV2::BoxFieldLoad { dst: value_gen.next(), box_val: value_gen.next(), field: "value".to_string() },
|
|
||||||
MirInstructionV2::RefGet { dst: value_gen.next(), reference: value_gen.next() },
|
|
||||||
MirInstructionV2::WeakNew { dst: value_gen.next(), box_val: value_gen.next() },
|
|
||||||
MirInstructionV2::WeakLoad { dst: value_gen.next(), weak_ref: value_gen.next() },
|
|
||||||
MirInstructionV2::WeakCheck { dst: value_gen.next(), weak_ref: value_gen.next() },
|
|
||||||
];
|
|
||||||
|
|
||||||
for inst in pure_instructions {
|
|
||||||
let effects = inst.effects();
|
|
||||||
assert!(effects.is_pure() || effects.primary_category() == Effect::Pure,
|
|
||||||
"Instruction should be pure: {:?}", inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test Mut effects
|
|
||||||
let mut_instructions = vec![
|
|
||||||
MirInstructionV2::BoxFieldStore { box_val: value_gen.next(), field: "value".to_string(), value: value_gen.next() },
|
|
||||||
MirInstructionV2::RefSet { reference: value_gen.next(), new_target: value_gen.next() },
|
|
||||||
MirInstructionV2::Adopt { parent: value_gen.next(), child: value_gen.next() },
|
|
||||||
MirInstructionV2::Release { reference: value_gen.next() },
|
|
||||||
MirInstructionV2::MemCopy { dest: value_gen.next(), src: value_gen.next(), size: value_gen.next() },
|
|
||||||
];
|
|
||||||
|
|
||||||
for inst in mut_instructions {
|
|
||||||
let effects = inst.effects();
|
|
||||||
assert!(effects.is_mut() || effects.primary_category() == Effect::Mut,
|
|
||||||
"Instruction should be mut: {:?}", inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test Io effects
|
|
||||||
let io_instructions = vec![
|
|
||||||
MirInstructionV2::Safepoint,
|
|
||||||
MirInstructionV2::Send { bus: value_gen.next(), message: value_gen.next() },
|
|
||||||
MirInstructionV2::Recv { dst: value_gen.next(), bus: value_gen.next() },
|
|
||||||
MirInstructionV2::AtomicFence { ordering: AtomicOrdering::SeqCst },
|
|
||||||
];
|
|
||||||
|
|
||||||
for inst in io_instructions {
|
|
||||||
let effects = inst.effects();
|
|
||||||
assert!(effects.is_io() || effects.primary_category() == Effect::Io,
|
|
||||||
"Instruction should be io: {:?}", inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test Control effects
|
|
||||||
let control_instructions = vec![
|
|
||||||
MirInstructionV2::Branch { condition: value_gen.next(), then_bb: bb_gen.next(), else_bb: bb_gen.next() },
|
|
||||||
MirInstructionV2::Jump { target: bb_gen.next() },
|
|
||||||
MirInstructionV2::Return { value: Some(value_gen.next()) },
|
|
||||||
MirInstructionV2::TailCall { func: value_gen.next(), args: vec![], effects: EffectMask::PURE },
|
|
||||||
];
|
|
||||||
|
|
||||||
for inst in control_instructions {
|
|
||||||
let effects = inst.effects();
|
|
||||||
assert!(effects.is_control() || effects.primary_category() == Effect::Control,
|
|
||||||
"Instruction should be control: {:?}", inst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test optimization safety based on effect categories
|
|
||||||
#[test]
|
|
||||||
fn test_optimization_safety() {
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
// Pure operations should be reorderable and eligible for CSE/LICM
|
|
||||||
let const_inst = MirInstructionV2::Const { dst: value_gen.next(), value: ConstValue::Integer(42) };
|
|
||||||
let binop_inst = MirInstructionV2::BinOp {
|
|
||||||
dst: value_gen.next(),
|
|
||||||
op: BinaryOp::Add,
|
|
||||||
lhs: value_gen.next(),
|
|
||||||
rhs: value_gen.next()
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(const_inst.effects().is_pure(), "Const should be pure and reorderable");
|
|
||||||
assert!(binop_inst.effects().is_pure(), "BinOp should be pure and reorderable");
|
|
||||||
|
|
||||||
// Mut operations should preserve same Box/Field dependencies
|
|
||||||
let store_inst = MirInstructionV2::BoxFieldStore {
|
|
||||||
box_val: value_gen.next(),
|
|
||||||
field: "value".to_string(),
|
|
||||||
value: value_gen.next()
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(store_inst.effects().is_mut(), "BoxFieldStore should be mut");
|
|
||||||
assert!(!store_inst.effects().is_pure(), "Mut operations cannot be reordered freely");
|
|
||||||
|
|
||||||
// Io operations should not be reordered
|
|
||||||
let send_inst = MirInstructionV2::Send {
|
|
||||||
bus: value_gen.next(),
|
|
||||||
message: value_gen.next()
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(send_inst.effects().is_io(), "Send should be io");
|
|
||||||
assert!(!send_inst.effects().is_read_only(), "Io operations have external effects");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test ownership forest verification
|
|
||||||
#[test]
|
|
||||||
fn test_ownership_forest_verification() {
|
|
||||||
let mut verifier = OwnershipVerifier::new();
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
// Test basic ownership establishment
|
|
||||||
let parent = value_gen.next();
|
|
||||||
let child = value_gen.next();
|
|
||||||
|
|
||||||
let adopt_inst = MirInstructionV2::Adopt { parent, child };
|
|
||||||
assert!(verifier.process_instruction(&adopt_inst).is_ok(), "Basic adoption should succeed");
|
|
||||||
|
|
||||||
let stats = verifier.ownership_stats();
|
|
||||||
assert_eq!(stats.strong_edges, 1, "Should have one strong edge");
|
|
||||||
|
|
||||||
// Test forest property verification
|
|
||||||
assert!(verifier.verify_ownership_forest().is_ok(), "Basic forest should be valid");
|
|
||||||
|
|
||||||
// Test weak reference creation
|
|
||||||
let weak_ref = value_gen.next();
|
|
||||||
let weak_new_inst = MirInstructionV2::WeakNew { dst: weak_ref, box_val: child };
|
|
||||||
assert!(verifier.process_instruction(&weak_new_inst).is_ok(), "Weak reference creation should succeed");
|
|
||||||
|
|
||||||
let stats_after_weak = verifier.ownership_stats();
|
|
||||||
assert_eq!(stats_after_weak.weak_edges, 1, "Should have one weak edge");
|
|
||||||
assert_eq!(stats_after_weak.live_weak_refs, 1, "Should have one live weak reference");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test ownership forest violations
|
|
||||||
#[test]
|
|
||||||
fn test_ownership_violations() {
|
|
||||||
let mut verifier = OwnershipVerifier::new();
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
// Test unsafe RefSet (changing strong reference without Release)
|
|
||||||
let reference = value_gen.next();
|
|
||||||
let old_target = value_gen.next();
|
|
||||||
let new_target = value_gen.next();
|
|
||||||
|
|
||||||
// Manually set up initial state
|
|
||||||
verifier.strong_edges.insert(reference, old_target);
|
|
||||||
|
|
||||||
// Try to change reference without releasing old target
|
|
||||||
let unsafe_ref_set = MirInstructionV2::RefSet { reference, new_target };
|
|
||||||
let result = verifier.process_instruction(&unsafe_ref_set);
|
|
||||||
|
|
||||||
assert!(result.is_err(), "Unsafe RefSet should be rejected");
|
|
||||||
if let Err(errors) = result {
|
|
||||||
assert!(errors.iter().any(|e| matches!(e, OwnershipError::UnsafeRefSet { .. })),
|
|
||||||
"Should detect unsafe RefSet");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test weak reference liveness tracking
|
|
||||||
#[test]
|
|
||||||
fn test_weak_reference_liveness() {
|
|
||||||
let mut verifier = OwnershipVerifier::new();
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let target = value_gen.next();
|
|
||||||
let weak_ref = value_gen.next();
|
|
||||||
|
|
||||||
// Create weak reference to target
|
|
||||||
let weak_new = MirInstructionV2::WeakNew { dst: weak_ref, box_val: target };
|
|
||||||
assert!(verifier.process_instruction(&weak_new).is_ok());
|
|
||||||
|
|
||||||
// Release the target
|
|
||||||
let release = MirInstructionV2::Release { reference: target };
|
|
||||||
assert!(verifier.process_instruction(&release).is_ok());
|
|
||||||
|
|
||||||
// Check that target is now considered dead
|
|
||||||
let stats = verifier.ownership_stats();
|
|
||||||
assert_eq!(stats.dead_targets, 1, "Target should be marked as dead");
|
|
||||||
|
|
||||||
// WeakLoad should handle expired reference deterministically
|
|
||||||
let weak_load = MirInstructionV2::WeakLoad { dst: value_gen.next(), weak_ref };
|
|
||||||
assert!(verifier.process_instruction(&weak_load).is_ok(),
|
|
||||||
"WeakLoad should handle expired reference gracefully");
|
|
||||||
|
|
||||||
// WeakCheck should also handle expired reference deterministically
|
|
||||||
let weak_check = MirInstructionV2::WeakCheck { dst: value_gen.next(), weak_ref };
|
|
||||||
assert!(verifier.process_instruction(&weak_check).is_ok(),
|
|
||||||
"WeakCheck should handle expired reference gracefully");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test Bus communication instructions
|
|
||||||
#[test]
|
|
||||||
fn test_bus_operations() {
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let bus = value_gen.next();
|
|
||||||
let message = value_gen.next();
|
|
||||||
|
|
||||||
// Test Send instruction
|
|
||||||
let send_inst = MirInstructionV2::Send { bus, message };
|
|
||||||
assert_eq!(send_inst.tier(), 1, "Send should be Tier-1");
|
|
||||||
assert!(send_inst.effects().is_io(), "Send should have io effects");
|
|
||||||
|
|
||||||
let used_values = send_inst.used_values();
|
|
||||||
assert_eq!(used_values.len(), 2, "Send should use bus and message");
|
|
||||||
assert!(used_values.contains(&bus) && used_values.contains(&message));
|
|
||||||
|
|
||||||
// Test Recv instruction
|
|
||||||
let recv_inst = MirInstructionV2::Recv { dst: value_gen.next(), bus };
|
|
||||||
assert_eq!(recv_inst.tier(), 1, "Recv should be Tier-1");
|
|
||||||
assert!(recv_inst.effects().is_io(), "Recv should have io effects");
|
|
||||||
|
|
||||||
let recv_used = recv_inst.used_values();
|
|
||||||
assert_eq!(recv_used.len(), 1, "Recv should use only bus");
|
|
||||||
assert!(recv_used.contains(&bus));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test implementation assistance instructions (Tier-2)
|
|
||||||
#[test]
|
|
||||||
fn test_implementation_assistance() {
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
// Test TailCall
|
|
||||||
let tail_call = MirInstructionV2::TailCall {
|
|
||||||
func: value_gen.next(),
|
|
||||||
args: vec![value_gen.next()],
|
|
||||||
effects: EffectMask::PURE
|
|
||||||
};
|
|
||||||
assert_eq!(tail_call.tier(), 2, "TailCall should be Tier-2");
|
|
||||||
assert!(tail_call.effects().is_control(), "TailCall should be control flow");
|
|
||||||
|
|
||||||
// Test MemCopy
|
|
||||||
let mem_copy = MirInstructionV2::MemCopy {
|
|
||||||
dest: value_gen.next(),
|
|
||||||
src: value_gen.next(),
|
|
||||||
size: value_gen.next()
|
|
||||||
};
|
|
||||||
assert_eq!(mem_copy.tier(), 2, "MemCopy should be Tier-2");
|
|
||||||
assert!(mem_copy.effects().is_mut(), "MemCopy should be mut");
|
|
||||||
|
|
||||||
// Test AtomicFence
|
|
||||||
let atomic_fence = MirInstructionV2::AtomicFence { ordering: AtomicOrdering::AcqRel };
|
|
||||||
assert_eq!(atomic_fence.tier(), 2, "AtomicFence should be Tier-2");
|
|
||||||
assert!(atomic_fence.effects().is_io(), "AtomicFence should be io");
|
|
||||||
assert!(atomic_fence.effects().contains(Effect::Barrier), "AtomicFence should have barrier effect");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test instruction descriptions and display
|
|
||||||
#[test]
|
|
||||||
fn test_instruction_descriptions() {
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let const_inst = MirInstructionV2::Const { dst: value_gen.next(), value: ConstValue::Integer(42) };
|
|
||||||
assert_eq!(const_inst.description(), "Load constant value");
|
|
||||||
|
|
||||||
let send_inst = MirInstructionV2::Send { bus: value_gen.next(), message: value_gen.next() };
|
|
||||||
assert_eq!(send_inst.description(), "Send Bus message");
|
|
||||||
|
|
||||||
let adopt_inst = MirInstructionV2::Adopt { parent: value_gen.next(), child: value_gen.next() };
|
|
||||||
assert_eq!(adopt_inst.description(), "Transfer ownership");
|
|
||||||
|
|
||||||
// Test Display trait
|
|
||||||
assert_eq!(format!("{}", const_inst), "Load constant value");
|
|
||||||
assert_eq!(format!("{}", send_inst), "Send Bus message");
|
|
||||||
assert_eq!(format!("{}", adopt_inst), "Transfer ownership");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test value ID tracking for dependencies
|
|
||||||
#[test]
|
|
||||||
fn test_value_id_tracking() {
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
|
|
||||||
let dst = value_gen.next();
|
|
||||||
let lhs = value_gen.next();
|
|
||||||
let rhs = value_gen.next();
|
|
||||||
|
|
||||||
let binop = MirInstructionV2::BinOp { dst, op: BinaryOp::Add, lhs, rhs };
|
|
||||||
|
|
||||||
// Test destination value
|
|
||||||
assert_eq!(binop.dst_value(), Some(dst), "BinOp should produce destination value");
|
|
||||||
|
|
||||||
// Test used values
|
|
||||||
let used = binop.used_values();
|
|
||||||
assert_eq!(used.len(), 2, "BinOp should use two values");
|
|
||||||
assert!(used.contains(&lhs) && used.contains(&rhs), "Should use lhs and rhs");
|
|
||||||
|
|
||||||
// Test instruction with no destination
|
|
||||||
let store = MirInstructionV2::BoxFieldStore {
|
|
||||||
box_val: value_gen.next(),
|
|
||||||
field: "value".to_string(),
|
|
||||||
value: value_gen.next()
|
|
||||||
};
|
|
||||||
assert_eq!(store.dst_value(), None, "BoxFieldStore should not produce destination value");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test the complete 25-instruction specification compliance
|
|
||||||
#[test]
|
|
||||||
fn test_complete_specification_compliance() {
|
|
||||||
// This test verifies that our implementation matches the exact specification
|
|
||||||
|
|
||||||
// Verify we can create all 25 instruction types without compilation errors
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
let mut bb_gen = BasicBlockIdGenerator::new();
|
|
||||||
|
|
||||||
let all_instructions = vec![
|
|
||||||
// Tier-0: Universal Core (8)
|
|
||||||
MirInstructionV2::Const { dst: value_gen.next(), value: ConstValue::Integer(42) },
|
|
||||||
MirInstructionV2::BinOp { dst: value_gen.next(), op: BinaryOp::Add, lhs: value_gen.next(), rhs: value_gen.next() },
|
|
||||||
MirInstructionV2::Compare { dst: value_gen.next(), op: CompareOp::Eq, lhs: value_gen.next(), rhs: value_gen.next() },
|
|
||||||
MirInstructionV2::Branch { condition: value_gen.next(), then_bb: bb_gen.next(), else_bb: bb_gen.next() },
|
|
||||||
MirInstructionV2::Jump { target: bb_gen.next() },
|
|
||||||
MirInstructionV2::Phi { dst: value_gen.next(), inputs: vec![] },
|
|
||||||
MirInstructionV2::Call { dst: Some(value_gen.next()), func: value_gen.next(), args: vec![], effects: EffectMask::PURE },
|
|
||||||
MirInstructionV2::Return { value: Some(value_gen.next()) },
|
|
||||||
|
|
||||||
// Tier-1: Nyash Semantics (12)
|
|
||||||
MirInstructionV2::NewBox { dst: value_gen.next(), box_type: "TestBox".to_string(), args: vec![] },
|
|
||||||
MirInstructionV2::BoxFieldLoad { dst: value_gen.next(), box_val: value_gen.next(), field: "field".to_string() },
|
|
||||||
MirInstructionV2::BoxFieldStore { box_val: value_gen.next(), field: "field".to_string(), value: value_gen.next() },
|
|
||||||
MirInstructionV2::BoxCall { dst: Some(value_gen.next()), box_val: value_gen.next(), method: "method".to_string(), args: vec![], effects: EffectMask::PURE },
|
|
||||||
MirInstructionV2::Safepoint,
|
|
||||||
MirInstructionV2::RefGet { dst: value_gen.next(), reference: value_gen.next() },
|
|
||||||
MirInstructionV2::RefSet { reference: value_gen.next(), new_target: value_gen.next() },
|
|
||||||
MirInstructionV2::WeakNew { dst: value_gen.next(), box_val: value_gen.next() },
|
|
||||||
MirInstructionV2::WeakLoad { dst: value_gen.next(), weak_ref: value_gen.next() },
|
|
||||||
MirInstructionV2::WeakCheck { dst: value_gen.next(), weak_ref: value_gen.next() },
|
|
||||||
MirInstructionV2::Send { bus: value_gen.next(), message: value_gen.next() },
|
|
||||||
MirInstructionV2::Recv { dst: value_gen.next(), bus: value_gen.next() },
|
|
||||||
|
|
||||||
// Tier-2: Implementation Assistance (5)
|
|
||||||
MirInstructionV2::TailCall { func: value_gen.next(), args: vec![], effects: EffectMask::PURE },
|
|
||||||
MirInstructionV2::Adopt { parent: value_gen.next(), child: value_gen.next() },
|
|
||||||
MirInstructionV2::Release { reference: value_gen.next() },
|
|
||||||
MirInstructionV2::MemCopy { dest: value_gen.next(), src: value_gen.next(), size: value_gen.next() },
|
|
||||||
MirInstructionV2::AtomicFence { ordering: AtomicOrdering::SeqCst },
|
|
||||||
];
|
|
||||||
|
|
||||||
assert_eq!(all_instructions.len(), 25, "Must have exactly 25 instructions");
|
|
||||||
|
|
||||||
// Verify tier distribution
|
|
||||||
let tier0_count = all_instructions.iter().filter(|i| i.tier() == 0).count();
|
|
||||||
let tier1_count = all_instructions.iter().filter(|i| i.tier() == 1).count();
|
|
||||||
let tier2_count = all_instructions.iter().filter(|i| i.tier() == 2).count();
|
|
||||||
|
|
||||||
assert_eq!(tier0_count, 8, "Tier-0 should have 8 instructions");
|
|
||||||
assert_eq!(tier1_count, 12, "Tier-1 should have 12 instructions");
|
|
||||||
assert_eq!(tier2_count, 5, "Tier-2 should have 5 instructions");
|
|
||||||
|
|
||||||
// Verify each instruction has proper effect classification
|
|
||||||
for instruction in &all_instructions {
|
|
||||||
let effects = instruction.effects();
|
|
||||||
let category = effects.primary_category();
|
|
||||||
|
|
||||||
// Ensure every instruction has a valid effect category
|
|
||||||
assert!(
|
|
||||||
matches!(category, Effect::Pure | Effect::Mut | Effect::Io | Effect::Control),
|
|
||||||
"Instruction must have valid effect category: {:?}", instruction
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performance test: Ensure effect calculations are fast
|
|
||||||
#[test]
|
|
||||||
fn test_effect_calculation_performance() {
|
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
let mut value_gen = ValueIdGenerator::new();
|
|
||||||
let mut bb_gen = BasicBlockIdGenerator::new();
|
|
||||||
|
|
||||||
// Create a large number of instructions
|
|
||||||
let mut instructions = Vec::new();
|
|
||||||
for _ in 0..10000 {
|
|
||||||
instructions.push(MirInstructionV2::BinOp {
|
|
||||||
dst: value_gen.next(),
|
|
||||||
op: BinaryOp::Add,
|
|
||||||
lhs: value_gen.next(),
|
|
||||||
rhs: value_gen.next()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Measure effect calculation time
|
|
||||||
let start = Instant::now();
|
|
||||||
for instruction in &instructions {
|
|
||||||
let _ = instruction.effects();
|
|
||||||
let _ = instruction.tier();
|
|
||||||
let _ = instruction.dst_value();
|
|
||||||
let _ = instruction.used_values();
|
|
||||||
}
|
|
||||||
let elapsed = start.elapsed();
|
|
||||||
|
|
||||||
// Should be very fast (< 10ms for 10k instructions)
|
|
||||||
assert!(elapsed.as_millis() < 100,
|
|
||||||
"Effect calculations should be fast, took {:?}", elapsed);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user