dol 0.8.1

DOL (Design Ontology Language) - A declarative specification language for ontology-first development
// Effect System Error Test: E0402 - Effectful in Parallel Context
// ================================================================
//
// What this test validates:
//   - Error E0402 is raised when effectful functions are used in parallel contexts
//   - Parallel combinators (par_map, par_for, etc.) require pure functions
//   - Non-determinism from effects would break parallel execution semantics
//   - Both direct and transitive effects are caught
//
// Expected error:
//   E0402: effectful function in parallel context
//   - Parallel combinators require pure functions for deterministic results
//   - Function has [IO/Msg/Mut] effect which is not safe for parallelism

gene Parallel.Violations @0.1.0 {
    """
    Examples of incorrect parallel usage with effectful functions.
    """

    // Pure function - OK for parallel
    fn square(x: Int) -> Int {
        x * x
    }

    // Effectful function - NOT OK for parallel
    fn logged_square(x: Int) -> Int {
        @io.println("Squaring: " + @fmt.int(x));
        x * x
    }

    // ERROR E0402: par_map with IO effect
    fn parallel_with_io(numbers: List[Int]) -> List[Int] {
        @par.map(numbers, logged_square)  // VIOLATION: logged_square has IO effect
    }

    // This is fine - pure function in parallel
    fn parallel_pure(numbers: List[Int]) -> List[Int] {
        @par.map(numbers, square)  // OK: square is pure
    }
}

gene Parallel.MessagingViolation @0.1.0 {
    """
    Parallel context with messaging effects.
    """

    fn send_progress(item: Int) -> Int {
        @msg.send(@msg.self(), { type: "progress", item: item });
        item * 2
    }

    // ERROR E0402: Messaging in parallel context
    fn parallel_with_messaging(items: List[Int]) -> List[Int] {
        @par.map(items, send_progress)  // VIOLATION: Msg effect
    }

    // ERROR E0402: Parallel for-each with messaging
    fn parallel_foreach_msg(items: List[Int]) -> Unit {
        @par.for_each(items, |item| {
            @msg.broadcast({ type: "processing", value: item })  // VIOLATION
        })
    }
}

gene Parallel.MutationViolation @0.1.0 {
    """
    Parallel context with mutation effects.
    This is especially dangerous as it leads to race conditions.
    """

    mut counter: Int = 0

    fn increment_and_process(x: Int) -> Int {
        self.counter = self.counter + 1;  // Race condition!
        x * self.counter
    }

    // ERROR E0402: Mutation in parallel - race condition
    fn parallel_with_mutation(items: List[Int]) -> List[Int] {
        @par.map(items, increment_and_process)  // VIOLATION: Mut effect
    }

    mut results: List[Int] = []

    fn accumulate(x: Int) -> Unit {
        self.results = @list.append(self.results, x)  // Race condition!
    }

    // ERROR E0402: Mutating shared state in parallel
    fn parallel_accumulate(items: List[Int]) -> Unit {
        @par.for_each(items, accumulate)  // VIOLATION: race on self.results
    }
}

gene Parallel.TransitiveViolation @0.1.0 {
    """
    Effectful functions hidden through call chains.
    """

    fn deep_io_effect(x: Int) -> Int {
        @io.println("deep");
        x
    }

    fn wrapper_1(x: Int) -> Int {
        deep_io_effect(x) + 1
    }

    fn wrapper_2(x: Int) -> Int {
        wrapper_1(x) * 2
    }

    fn seemingly_pure(x: Int) -> Int {
        wrapper_2(x) - 1
    }

    // ERROR E0402: Transitive IO effect through call chain
    fn parallel_transitive(items: List[Int]) -> List[Int] {
        @par.map(items, seemingly_pure)  // VIOLATION: transitive IO effect
    }
}

gene Parallel.ClosureViolation @0.1.0 {
    """
    Closures capturing effectful operations.
    """

    fn log(msg: String) -> Unit {
        @io.println(msg)
    }

    // ERROR E0402: Closure performs IO
    fn parallel_closure_io(items: List[Int]) -> List[Int] {
        @par.map(items, |x| {
            log("Processing: " + @fmt.int(x));  // VIOLATION
            x * 2
        })
    }

    mut state: Int = 0

    // ERROR E0402: Closure captures and mutates
    fn parallel_closure_mut(items: List[Int]) -> List[Int] {
        @par.map(items, |x| {
            self.state = self.state + x;  // VIOLATION: captures mut state
            x
        })
    }
}

gene Parallel.ValidUsage @0.1.0 {
    """
    Examples of CORRECT parallel usage with pure functions.
    These should NOT produce E0402 errors.
    """

    fn double(x: Int) -> Int { x * 2 }
    fn square(x: Int) -> Int { x * x }
    fn negate(x: Int) -> Int { -x }
    fn add_one(x: Int) -> Int { x + 1 }

    // All of these are valid - pure functions
    fn valid_map(items: List[Int]) -> List[Int] {
        @par.map(items, double)
    }

    fn valid_map_chain(items: List[Int]) -> List[Int] {
        let doubled = @par.map(items, double);
        @par.map(doubled, square)
    }

    fn valid_reduce(items: List[Int]) -> Int {
        @par.reduce(items, 0, |acc, x| acc + x)  // Pure lambda
    }

    fn valid_filter(items: List[Int]) -> List[Int] {
        @par.filter(items, |x| x > 0)  // Pure predicate
    }

    // Pure closure capturing immutable value
    fn valid_closure(items: List[Int], multiplier: Int) -> List[Int] {
        @par.map(items, |x| x * multiplier)  // OK: captures immutable
    }
}

// Expected diagnostic output:
// error[E0402]: effectful function in parallel context
//   --> e0402_parallel.dol:30:9
//    |
// 30 |         @par.map(numbers, logged_square)
//    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//    |
//    = note: parallel combinators require pure functions
//    = note: 'logged_square' has effect: IO
//    = note: IO effects may produce non-deterministic ordering in parallel
//    = help: remove effects from the function or use sequential processing

test expect_error {
    // Direct effect violations
    assert_error(E0402, Parallel.Violations.parallel_with_io);
    assert_no_error(Parallel.Violations.parallel_pure);

    // Messaging violations
    assert_error(E0402, Parallel.MessagingViolation.parallel_with_messaging);
    assert_error(E0402, Parallel.MessagingViolation.parallel_foreach_msg);

    // Mutation violations
    assert_error(E0402, Parallel.MutationViolation.parallel_with_mutation);
    assert_error(E0402, Parallel.MutationViolation.parallel_accumulate);

    // Transitive violations
    assert_error(E0402, Parallel.TransitiveViolation.parallel_transitive);

    // Closure violations
    assert_error(E0402, Parallel.ClosureViolation.parallel_closure_io);
    assert_error(E0402, Parallel.ClosureViolation.parallel_closure_mut);

    // Valid usage - should not error
    assert_no_error(Parallel.ValidUsage.valid_map);
    assert_no_error(Parallel.ValidUsage.valid_map_chain);
    assert_no_error(Parallel.ValidUsage.valid_reduce);
    assert_no_error(Parallel.ValidUsage.valid_filter);
    assert_no_error(Parallel.ValidUsage.valid_closure);
}