chore(mir): prune legacy instruction sets (#129)

This commit is contained in:
moe-charm
2025-09-10 17:23:26 +09:00
committed by GitHub
parent 370e725926
commit 06c77c1026
7 changed files with 18 additions and 2122 deletions

View File

@ -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);
}
} }
} }

View File

@ -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");
}
}

View File

@ -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);

View File

@ -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());
}
}

View File

@ -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");
}
}

View File

@ -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");
} }

View File

@ -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);
}