ruchy 4.2.0

A systems scripting language that transpiles to idiomatic Rust with extreme quality engineering
Documentation
//! OPT-022: Inline Caching Foundation - Hidden Class System
//!
//! EXTREME TDD: RED Phase - These tests MUST fail initially
//!
//! Goal: Implement hidden class system for 2-4x property access speedup
//!
//! References:
//! - Brunthaler (2010) - Inline Caching Meets Quickening
//! - Chambers et al. (1989) - An Efficient Implementation of SELF
//! - Hölzle et al. (1991) - Optimizing Dynamically-Typed Languages
//!
//! Expected Performance:
//! - Property access: 4-10x faster (monomorphic sites)
//! - Method dispatch: 2-5x faster
//! - Cache hit rate: 85-95%
//! - Overall: 2-4x speedup for property-heavy code

use assert_cmd::Command;

fn ruchy_cmd() -> Command {
    assert_cmd::cargo::cargo_bin_cmd!("ruchy")
}

// ============================================================================
// Test 1: Hidden Class Creation - Objects get assigned hidden classes
// ============================================================================

#[test]
fn test_opt_022_01_hidden_class_creation() {
    // Objects with same structure should share hidden class
    let code = r#"
struct Point { x: i32, y: i32 }

fun main() {
    let p1 = Point { x: 10, y: 20 }
    let p2 = Point { x: 30, y: 40 }

    // Internal check: p1 and p2 should share same hidden class
    // This requires runtime introspection API
    println("Points created")
}
"#;

    ruchy_cmd()
        .arg("-e")
        .arg(code)
        .assert()
        .success()
        .stdout("Points created\n");
}

// ============================================================================
// Test 2: Hidden Class Transition - Adding properties creates new classes
// ============================================================================

#[test]
fn test_opt_022_02_hidden_class_transition() {
    // Adding properties should transition to new hidden class
    let code = r#"
class Point {
    x: f64,
    y: f64
}

fun main() {
    let p = Point { x: 10.0, y: 20.0 }

    // Original hidden class: [x, y]
    println!("{}", p.x)

    // Note: Dynamic property addition not currently supported in Ruchy
    // This test documents expected behavior for future implementation
}
"#;

    ruchy_cmd()
        .arg("-e")
        .arg(code)
        .assert()
        .success()
        .stdout("10.0\n");
}

// ============================================================================
// Test 3: Monomorphic Property Access - Single type at call site
// ============================================================================

#[test]
fn test_opt_022_03_monomorphic_property_access() {
    // Property access at same site with same type = monomorphic = fast
    let code = r#"
struct Rectangle {
    width: i32,
    height: i32,

    fun area(&self) -> i32 {
        self.width * self.height
    }
}

fun main() {
    let r1 = Rectangle { width: 10, height: 20 }
    let r2 = Rectangle { width: 5, height: 8 }
    let r3 = Rectangle { width: 3, height: 7 }

    // All three calls access .area() on Rectangle
    // Should be monomorphic (same hidden class)
    println!("{}", r1.area())
    println!("{}", r2.area())
    println!("{}", r3.area())
}
"#;

    ruchy_cmd()
        .arg("-e")
        .arg(code)
        .assert()
        .success()
        .stdout("200\n40\n21\n");
}

// ============================================================================
// Test 4: Polymorphic Property Access - Multiple types at call site
// ============================================================================

#[test]
fn test_opt_022_04_polymorphic_property_access() {
    // Property access at same site with different types = polymorphic
    let code = r#"
struct Rectangle {
    width: i32,
    height: i32,

    fun area(&self) -> i32 {
        self.width * self.height
    }
}

struct Circle {
    radius: f64,

    fun area(&self) -> f64 {
        3.14159 * self.radius * self.radius
    }
}

fun main() {
    let r = Rectangle { width: 10, height: 20 }
    let c = Circle { radius: 5.0 }

    // Polymorphic call site - two different types
    println!("{}", r.area())
    println!("{}", c.area())
}
"#;

    ruchy_cmd().arg("-e").arg(code).assert().success();
}

// ============================================================================
// Test 5: Property Access Cache Hit - Repeated access to same property
// ============================================================================

#[test]
fn test_opt_022_05_property_cache_hit() {
    // Accessing same property multiple times should hit inline cache
    let code = r#"
struct Point {
    x: i32,
    y: i32
}

fun main() {
    let p = Point { x: 10, y: 20 }

    // All these accesses to p.x should hit the inline cache
    let sum = p.x + p.x + p.x + p.x + p.x
    println!("{}", sum)
}
"#;

    ruchy_cmd()
        .arg("-e")
        .arg(code)
        .assert()
        .success()
        .stdout("50\n");
}

// ============================================================================
// Test 6: Method Dispatch Cache - Repeated method calls
// ============================================================================

#[test]
fn test_opt_022_06_method_dispatch_cache() {
    // Method dispatch should be cached for performance
    let code = r#"
struct Counter {
    count: i32,

    fun new() -> Counter {
        Counter { count: 0 }
    }

    fun get(&self) -> i32 {
        self.count
    }
}

fun main() {
    let c = Counter::new()

    // Multiple calls to .get() should hit method dispatch cache
    println!("{}", c.get())
    println!("{}", c.get())
    println!("{}", c.get())
}
"#;

    ruchy_cmd()
        .arg("-e")
        .arg(code)
        .assert()
        .success()
        .stdout("0\n0\n0\n");
}

// ============================================================================
// Test 7: Cache Invalidation - Struct field mutation
// ============================================================================

#[test]
fn test_opt_022_07_cache_invalidation_on_mutation() {
    // Mutating struct fields should invalidate caches appropriately
    let code = r#"
struct Counter {
    count: i32,

    fun new() -> Counter {
        Counter { count: 0 }
    }

    fun increment(&mut self) {
        self.count += 1
    }

    fun get(&self) -> i32 {
        self.count
    }
}

fun main() {
    let mut c = Counter::new()

    println!("{}", c.get())  // 0
    c.increment()
    println!("{}", c.get())  // 1
    c.increment()
    println!("{}", c.get())  // 2
}
"#;

    ruchy_cmd()
        .arg("-e")
        .arg(code)
        .assert()
        .success()
        .stdout("0\n1\n2\n");
}

// ============================================================================
// Test 8: Hidden Class Shape Consistency - Property order matters
// ============================================================================

#[test]
fn test_opt_022_08_hidden_class_property_order() {
    // Objects with same properties in different order = different hidden classes
    // Note: Rust/Ruchy structs have fixed property order, so this tests
    // that the hidden class system respects declaration order
    let code = r#"
struct PointXY {
    x: i32,
    y: i32
}

struct PointYX {
    y: i32,
    x: i32
}

fun main() {
    let p1 = PointXY { x: 10, y: 20 }
    let p2 = PointYX { y: 20, x: 10 }

    // Different struct types = different hidden classes
    println!("{} {}", p1.x, p1.y)
    println!("{} {}", p2.x, p2.y)
}
"#;

    ruchy_cmd()
        .arg("-e")
        .arg(code)
        .assert()
        .success()
        .stdout("10 20\n10 20\n");
}

// ============================================================================
// Test 9: Performance Baseline - Property access timing
// ============================================================================

#[test]
fn test_opt_022_09_property_access_performance() {
    // Baseline: How fast is property access WITHOUT inline caching?
    // This establishes the performance to beat
    let code = r#"
struct Point {
    x: i32,
    y: i32,
    z: i32
}

fun main() {
    let p = Point { x: 10, y: 20, z: 30 }

    // Access properties 10,000 times
    let mut sum = 0
    let mut i = 0
    while i < 10000 {
        sum = sum + p.x + p.y + p.z
        i = i + 1
    }

    println!("{}", sum)
}
"#;

    ruchy_cmd()
        .arg("-e")
        .arg(code)
        .assert()
        .success()
        .stdout("600000\n");
}

// ============================================================================
// Test 10: Cache Statistics - Monitoring cache effectiveness
// ============================================================================

#[test]
fn test_opt_022_10_cache_statistics() {
    // System should track cache hit/miss statistics
    // Target: 85-95% hit rate
    let code = r#"
struct Point {
    x: i32,
    y: i32
}

fun main() {
    let p1 = Point { x: 10, y: 20 }
    let p2 = Point { x: 30, y: 40 }

    // Monomorphic access pattern (should have high cache hit rate)
    let s1 = p1.x + p1.y
    let s2 = p2.x + p2.y
    let s3 = p1.x + p1.y

    println!("{} {} {}", s1, s2, s3)

    // TODO: Add introspection API to check cache statistics
    // Expected: >90% hit rate for this code
}
"#;

    ruchy_cmd()
        .arg("-e")
        .arg(code)
        .assert()
        .success()
        .stdout("30 70 30\n");
}