⏺ Phase 6 is fully implemented. Here's a summary of all changes: #7
No reviewers
Labels
No labels
bug
dependencies
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
rust
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
navicore/patch-rexx!7
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "phase6"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Files Modified
File: src/ast.rs
Changes: Added Hash derive to Condition enum
────────────────────────────────────────
File: src/parser.rs
Changes: Added parse_signal() and parse_condition() methods, wired SIGNAL into parse_clause()
────────────────────────────────────────
File: src/eval.rs
Changes: Added ExecSignal::Signal(String) variant, traps/pending_signal fields, exec_signal() method, restart loop in exec(), NOVALUE trap in eval_expr,
SYNTAX trap wrapper exec_clause_outer(), Signal propagation through all DO handlers and call_routine
────────────────────────────────────────
File: src/env.rs
Changes: Added ConditionInfoData struct, condition_info field, set_condition_info() method
────────────────────────────────────────
File: src/builtins.rs
Changes: Added &Environment param to call_builtin, added CONDITION() BIF
────────────────────────────────────────
File: src/main.rs
Changes: Added ExecSignal::Signal handling in run_line()
────────────────────────────────────────
File: tests/phase6.rs
Changes: New — 22 integration tests
Features Implemented
Verification
PR #7 Review: SIGNAL Implementation (Phase 6)
Summary
This PR implements SIGNAL control flow and condition traps for the REXX interpreter. The implementation is largely correct with excellent test coverage (22 passing tests). I found 2 critical bugs and several areas for improvement.
🔴 CRITICAL BUGS
1. NOVALUE trap doesn't fire for compound variables
Location:
src/eval.rs:998-1001(Expr::Compound branch)Bug: The NOVALUE trap only checks simple variables, not compound variables.
Root cause:
env.is_set()at line 982 only checks thevarsHashMap, notstems. TheExpr::Compoundbranch (line 998) has no NOVALUE check at all.Fix: Add similar trap logic to the
Expr::Compoundbranch, checking if the compound tail is unset.Test needed: Add test case for NOVALUE on unset compound variable.
2. NOVALUE trap delayed until clause completion
Location:
src/eval.rs:982-996(eval_expr Symbol handling)Bug: When NOVALUE fires during expression evaluation,
pending_signalis set but eval_expr continues and returns the uppercase variable name. The signal doesn't fire until the clause completes.Example:
Expected: Trap immediately when
unsetvaris accessed.Actual: Returns "UNSETVAR", attempts arithmetic "UNSETVAR" + 1, fails with Error 41, THEN might check pending_signal.
REXX Spec (ANSI X3.274-1996 §7.4.10): SIGNAL ON NOVALUE should transfer control immediately when uninitialized variable is referenced.
Fix: After setting
pending_signal(line 994), return immediately instead of continuing to line 996. Consider returning a sentinel value or restructuring to check pending_signal in the caller.⚠️ MODERATE ISSUES
3. Missing test: SIGNAL VALUE to non-existent label
Gap: No test for
signal value 'NONEXISTENT'to verify Error 16 propagation.Risk: The error is caught at
eval.rs:94, but if SIGNAL VALUE is executed inside a function call expression, the pending_signal mechanism might not propagate correctly.Recommendation: Add test case:
4. CONDITION() with no active trap behavior unclear
Location:
src/builtins.rs:1632-1650Current: Returns empty strings when
condition_infois None.Spec verification needed: Per ANSI X3.274-1996 §9.4.4, verify whether
CONDITION('I')andCONDITION('S')should return different values when no trap is active vs. after trap fires. Current implementation might be oversimplified.Recommendation: Review spec section 9.4.4 and add test for CONDITION() with no active trap.
✅ CORRECTNESS VERIFIED
Parser (
src/parser.rs:1031-1121)Signal Propagation
eval.rs:89-102)phase6.rs:72-85)SYNTAX Trap (
src/eval.rs:137-162)error.number())phase6.rs:140-146)Division by Zero
/(1105),%(1123),//(1138)phase6.rs:135-147Trap Auto-Disable (ANSI §7.4.10.4)
eval.rs:993eval.rs:155phase6.rs:211-219Hash Derive for Condition Enum
eval.rs:59ast.rs:227📊 TEST COVERAGE
22 tests, all passing — Excellent coverage overall.
Missing test cases:
🔍 DETAILED FINDINGS
Error Handling: src/error.rs
Environment: src/env.rs
ConditionInfoDatastruct appropriate (4 string fields)condition_info: Option<ConditionInfoData>good designset_condition_info()simple setter (line 175)is_set()only checks simple vars, not compounds (line 180-185) — contributes to bug #1Built-ins: src/builtins.rs
&Environmentparam tocall_builtin(breaking change, all call sites updated)Main: src/main.rs
🎯 RECOMMENDATIONS
Must Fix Before Merge
Expr::Compoundbranch (eval.rs:998)eval_expr, not delayedsignal value 'NONEXISTENT'Should Fix
Documentation
eval.rs:982: why uppercase name is returnederror.rs:102: Error 42 covers both overflow AND division by zero📝 REXX SEMANTICS COMPLIANCE
✅ Correct:
⚠️ Needs verification:
🏆 STRENGTHS
🎬 CONCLUSION
Overall: Strong implementation with correct core logic. The two critical bugs are edge cases that slipped through test coverage. Once fixed, this will be a solid Phase 6 foundation.
Recommendation: Request changes for bugs #1 and #2, plus missing tests.
Confidence: High — verified against ANSI X3.274-1996 and classic REXX behavior.
Review focused on correctness, REXX semantics, arithmetic, error handling, performance, and safety per request. Style and formatting not reviewed (handled by clippy).
Code Review: Phase 6 - SIGNAL and Condition Traps
This PR implements SIGNAL instruction and condition traps for the REXX interpreter. The implementation is solid overall with good test coverage. Below are issues requiring attention:
Critical Issues
1. NOVALUE trap fires on already-trapped unset variables (src/eval.rs:982-996, 998-1014)
Severity: High — Violates REXX semantics
The NOVALUE trap correctly disables itself after firing once (line 993), but the check happens after setting
pending_signal. This creates a subtle bug: if an expression contains multiple unset variables, the first one fires the trap, but subsequent unset variables in the same expression still checkself.traps.get(&Condition::NoValue)before the trap is removed from the HashMap.The trap removal at line 993 happens inside the
if let Some(label)block, but the pending_signal is set before the trap is removed. Consider:Evaluation order:
a: trap exists → set pending_signal, remove trap+: sees pending_signal, returns early (line 1026)b: trap already removed → returns "B" normallyActually, upon closer inspection, this is correct behavior — the early return at lines 1018-1020, 1025-1027, 1029-1031 prevents further evaluation once
pending_signalis set. So this is not a bug. Disregard this point.2. Missing division-by-zero check in
to_numbercontext (src/eval.rs:818-822)Severity: Medium — Can produce confusing error messages
In
exec_do_controlled, the BY expression is checked for zero at line 819. However, the error isInvalidWholeNumberwith message "BY value in DO loop must not be zero". Per ANSI X3.274-1996 §7.3.3, BY must be numeric, and zero BY is a runtime error but the correct error number is Error 26 (invalid whole number is actually for non-integer values in contexts requiring integers).The check is correct but the error detail is slightly misleading: zero is a whole number. The REXX spec says BY must be non-zero, which is a separate semantic constraint. Consider using
BadArithmetic(Error 41) or a custom error. The existing error number (26) is acceptable if you consider "must be non-zero" part of the "whole number" constraint for BY.Recommendation: This is borderline acceptable; Error 26 is used for various numeric constraint violations. No change required unless aiming for strict error number conformance.
3. SYNTAX trap can catch errors from trap handlers themselves (src/eval.rs:138-161)
Severity: Low — Could cause infinite loops in pathological cases
exec_clause_outerwraps every clause in SYNTAX trap handling. This includes clauses inside the SYNTAX trap handler itself. Per the REXX spec, traps auto-disable after firing (line 155), so a second error in the handler won't re-trap. However, if a user does:This would jump to SYNTAX: again, creating an infinite loop (until stack overflow). Classic REXX interpreters (Regina, ooRexx) prevent this by tracking "currently executing a trap handler" state. This is edge-case behavior not covered by tests.
Recommendation: Document this behavior or add a guard (e.g.,
in_trap_handler: boolflag) to prevent re-entrancy.Parser Issues
4. SIGNAL VALUE doesn't validate label format (src/parser.rs:1070-1077)
Severity: Low — Defers error to runtime
parse_signalaccepts any expression after VALUE, but REXX labels have naming rules (no dots, must be valid symbols). Invalid computed labels (e.g.,signal value '123'orsignal value 'A.B') will only fail at runtime (Error 16: label not found) rather than being caught early. This is acceptable REXX behavior (computed labels can be arbitrary strings at parse time), but consider validating at runtime that the computed label is a valid symbol before lookup.Recommendation: No change required — runtime validation is correct per REXX spec.
Evaluator Issues
5. Potential panic on empty scopes (src/eval.rs:66, 86-88)
Severity: Low — Defensive programming issue
Environment::scopesis initialized with one scope (env.rs:53), but.last().expect("environment has no scopes")calls assume the vector is never empty. Ifpop_procedureis called more times thanpush_procedure, this could panic. The current code correctly manages the stack, but adding an assertion inpop_procedurewould prevent future bugs.Recommendation: Add
assert!(self.scopes.len() > 1)inEnvironment::pop_procedureto prevent underflow.6. SIGNAL propagation through function calls may not set RC (src/eval.rs:1055-1061)
Severity: Medium — REXX semantics unclear
When a SIGNAL fires inside a function call (line 1055-1060), it sets
pending_signaland returnsRexxValue::new(""). The signal then propagates up. However, if this was a SYNTAX trap, RC should be set (as done in exec_clause_outer). Currently RC is only set when SYNTAX fires at clause level, not inside expression evaluation.Example:
The current code sets RC at line 145 (exec_clause_outer), which wraps clauses but not function calls in expressions. When the division-by-zero happens inside
foo, it propagates as an error (Err(diag)), which is then caught by exec_clause_outer when evaluating the SAY clause. So RC is set correctly.Actually correct — exec_clause_outer wraps the SAY clause, which calls eval_expr, which calls call_routine, which eventually hits the divide-by-zero. The error bubbles up to exec_clause_outer. Disregard this point.
Test Coverage Gaps
7. No test for SIGNAL from within a function call expression
Tests cover SIGNAL from DO/SELECT but not from inside a function used in an expression:
Recommendation: Add test
signal_novalue_from_function_expression.8. No test for SIGNAL OFF without corresponding ON
Current tests enable traps with ON, then disable with OFF. Missing:
signal off novaluewhen no trap was set (should be no-op per REXX spec). Line 341 (self.traps.remove(condition)) correctly handles this (returns None if not present), but a test would document the behavior.9. No test for trap re-enabling after auto-disable
Test
novalue_trap_fires_once(line 212-218) verifies auto-disable within a handler, but doesn't test re-enabling:Correctness — Arithmetic
10. No BigDecimal precision issues detected
Arithmetic operations correctly use
self.settings.digitsandself.settings.formthroughout. Division by zero is correctly trapped (lines 1126-1128, 1144-1146, 1159-1161). Power operator limits exponent to ±1,000,000 (line 1187-1189) to prevent OOM — this is a reasonable safeguard not in the ANSI spec, but acceptable for practical implementations.Correctness — REXX Semantics
11. Everything-is-a-string preserved
Condition names, labels, and RC values are all handled as strings. Correct.
12. Unset variables return uppercase name
NOVALUE trap correctly captures the uppercase variable name in description (env.rs:988, 1006). Correct.
13. Case-insensitive label matching
Labels are uppercased (parser.rs:179, 1081) and stored in HashMap (eval.rs:79-86). Correct.
Error Handling
14. Error numbers are correct
All correct per ANSI X3.274-1996 Appendix A.
Performance
15. Unnecessary clones in label matching (src/eval.rs:93-94, 142)
self.traps.get(&Condition::Syntax).cloned()clones the String label. Since the label is immediately used and not stored, consider using a reference. However, thetraps.removeat line 155 requires ownership transfer, so the clone is necessary.Recommendation: Current implementation is optimal given the auto-disable requirement.
16. HashMap lookups in hot loop (src/eval.rs:983, 1001)
Every unset variable access checks
self.traps.get(&Condition::NoValue). This is unavoidable for correct NOVALUE semantics and HashMap lookup is O(1). Not a performance issue.Safety
17. No panics detected on malformed input
Parser validates SIGNAL syntax (lines 1038-1091) and returns proper errors for missing labels (1089-1091), invalid conditions (1107-1112). Evaluator handles missing labels (95-97) and invalid error numbers (144-145). No unwrap() calls on user-controlled data.
Minor Issues
18. Inconsistent error for bad CONDITION() option (src/builtins.rs:1642-1646)
Uses
IncorrectCall(Error 40) instead ofInvalidSubKeyword(Error 25). Per ANSI REXX, bad option letters in BIFs are typically Error 40, so this is acceptable.19. CONDITION() always returns String (src/builtins.rs:1649)
Even for empty results (no active trap), returns
RexxValue::new("")viamap_or_else(String::new, ...). This is correct per REXX spec (empty string is valid return value).Summary
Correctness: ✅ Solid implementation. No semantic bugs found.
Error Handling: ✅ Error numbers and propagation are correct.
Performance: ✅ No obvious inefficiencies.
Safety: ✅ No panics on malformed input.
Test Coverage: ⚠️ Strong, but missing edge cases (function-call SIGNAL, OFF without ON, trap re-enable).
Recommendation: Approve after addressing test coverage gaps (#7-9) and documenting trap re-entrancy behavior (#3).
Code Review: Phase 6 SIGNAL Implementation
I've reviewed the SIGNAL instruction implementation. All 32 tests pass. Here are my findings:
Critical Issues
1. NOVALUE trap check happens AFTER variable retrieval (src/eval.rs:982-997, 999-1013)
The trap check occurs after returning the variable name. Per REXX semantics, the trap should fire INSTEAD OF returning the name.
Current: Returns uppercased name even when trap fires (masked by short-circuit checks)
Impact: Low in practice but violates clean semantics
2. Missing NOVALUE trap during implicit tail resolution (src/eval.rs:967-975)
resolve_tail() evaluates tail variables without NOVALUE checking. Example: arr.unset_idx should trap on unset_idx but returns ARR.UNSET_IDX without trap.
Impact: NOVALUE semantics incomplete for compound variable tails
3. Condition info instruction field always SIGNAL (src/eval.rs:989, 1007, 151)
Per ANSI X3.274-1996 section 7.4.9, CONDITION(I) should return the instruction that CAUSED the condition, not the control transfer mechanism.
Impact: Medium - affects observability in trapped code
Safety Issues
4. Potential infinite loop in restart mechanism (src/eval.rs:89-102)
No cycle detection or iteration limit. Attack: label1: signal label2; label2: signal label1 results in infinite loop DoS
Recommendation: Add iteration limit (10000) or cycle detection
Semantic Correctness (CORRECT)
Recommendations
Priority 1 (Correctness):
Priority 2 (Safety):
3. Fix Issue 4: Add SIGNAL cycle detection or iteration limit
4. Address Issue 3: Document CONDITION(I) behavior
Overall Assessment
Code Quality: High. Clean implementation following Rust idioms.
REXX Compliance: Good. Core semantics correct with minor deviations.
Safety: Good with caveats. No memory unsafety but DoS vector exists.
Test Coverage: Excellent for implemented features.
Recommendation: Request changes for Issues 1, 2, and 4 before merge
Review by Claude Sonnet 4.5 using ANSI X3.274-1996 as reference