Lines
91.43 %
Functions
100 %
Branches
//! `(debug ...)` native — emits a log message + returns nil. The
//! effect-position dispatch path skips the trailing nil push.
//! Also covers PRINT / DISPLAY / NEWLINE (textual output via the host
//! `log` channel), previously phantom natives with no codegen handler.
use super::common::{compile_and_validate, compile_expect_error, wrap_with_runtime_ratio};
#[test]
fn debug_no_args() {
compile_and_validate("(debug)");
}
fn debug_with_string() {
compile_and_validate("(debug \"hello\")");
fn debug_with_multiple_args() {
compile_and_validate("(debug \"x =\" 42 \"y =\" 1/2)");
/// Debug at value position — the form's return is nil. Use it as the
/// last expression so `compile_program` routes through `compile_expr`
/// which dispatches the `compile` (not `effect`) variant.
fn debug_at_value_position() {
compile_and_validate("(debug \"trace\") (debug 1)");
/// Debug in effect position via `begin`. The first form is at effect
/// position; the second is the program's last and runs through the
/// value path.
fn debug_inside_begin() {
compile_and_validate("(begin (debug \"step1\") (debug \"step2\"))");
/// Runtime-valued arg: the eval falls back to format the runtime
/// placeholder, codegen embeds the formatted form text in the log
/// payload. Verifies the path handles non-constant args without
/// panicking.
fn debug_with_runtime_arg() {
compile_and_validate(&wrap_with_runtime_ratio("(debug \"X =\" X)"));
fn print_with_string() {
compile_and_validate("(print \"hello\")");
fn display_with_value() {
compile_and_validate("(display 42)");
fn print_at_value_position_is_nil() {
// PRINT returns nil; used as a defun body tail it must produce a value.
compile_and_validate("(defun emit (x) (print x)) (emit 1)");
fn newline_no_args() {
compile_and_validate("(newline)");
fn newline_rejects_args() {
let err = compile_expect_error("(newline 1)");
assert!(err.contains("NEWLINE"), "got: {err}");
fn print_then_newline_in_begin() {
compile_and_validate("(begin (print \"line\") (newline))");
/// Effect-position regression: a `print` whose value is discarded (first form
/// of a `begin`) must still emit its `log` side effect. Before the effect-
/// dispatch fix, PRINT/DISPLAY/NEWLINE fell through to a bare `eval_value` in
/// `compile_for_effect` and emitted NO wasm — the side effect was silently
/// dropped. The emitted wasm carrying a `log` call is the contract here.
fn print_side_effect_survives_effect_position() {
let wasm = compile_and_validate("(begin (print \"x\") 1)");
// Two `log`-import call sites would mean DEBUG; one means the print's
// side effect was emitted. We only assert the module is non-trivially
// larger than the same program without the print (the print emitted code).
let baseline = compile_and_validate("(begin 1)");
assert!(
wasm.len() > baseline.len(),
"print side effect was dropped: {} vs baseline {}",
wasm.len(),
baseline.len()
);
/// `print` as an `and` operand must still EMIT its side effect. Round-2
/// adversarial-review regression: the eval handler used to return a pure
/// `Expr::Nil`, so `and`'s short-circuit folder treated `(print …)` as a
/// side-effect-free constant and dropped it — `(and (print …) #t)` folded to
/// `#t` and emitted no `log`. The eval handlers now return a non-foldable
/// `WasmRuntime(Bool)` placeholder + carry a stack handler, so the side effect
/// survives. Asserted via wasm size: the payload-bearing variant must be
/// larger than the same `and` with no print.
fn print_side_effect_survives_and_operand() {
let with = compile_and_validate("(and (print \"xxxxxxxx\") #t)");
let without = compile_and_validate("(and #t #t)");
with.len() > without.len(),
"print side effect dropped inside (and ...): {} vs {}",
with.len(),
without.len()
/// Same latent bug existed for the pre-existing DEBUG native — `(and (debug …)
/// #t)` dropped the debug. Fixed by the same eval-handler change; lock it.
fn debug_side_effect_survives_and_operand() {
let with = compile_and_validate("(and (debug \"xxxxxxxx\") #t)");
"debug side effect dropped inside (and ...): {} vs {}",
/// `print` as a `let` init (stack position) must compile via the new stack
/// handler — previously errored "native function 'PRINT' cannot produce stack
/// value".
fn print_in_let_value_position_compiles() {
compile_and_validate("(let ((x (print \"x\"))) x)");
/// `display` in effect position, same contract as print.
fn display_side_effect_survives_effect_position() {
compile_and_validate("(begin (display 42) 1)");
fn print_with_runtime_arg() {
compile_and_validate(&wrap_with_runtime_ratio("(print X)"));