Stack pollution detection - words leaving extra values #228
Labels
No labels
bug
dependencies
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
refactor
rust
technical-debt
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
navicore/patch-seq#228
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Problem
The type checker catches type mismatches and underflow, but there's no explicit check that words don't leave garbage on the stack beyond their declared effect.
Example
Current Behavior
This may be caught implicitly via stack shape mismatch during unification, but:
Feasibility Questions
..rest) accidentally allow extra values to "hide"?Proposed Work
Acceptance Criteria
Test Results: Stack Pollution Detection
Added 12 tests to
typechecker.rsto probe stack pollution detection.✅ Caught (7 tests passed)
test_pollution_extra_push( Int -- Int ) 42 ;- pushes extra valuetest_pollution_extra_dup( Int -- Int ) dup ;- dup produces 2test_pollution_consumes_extra( Int -- Int ) drop drop 42 ;- consumes 2test_pollution_in_then_branchif 1 2 else 3 then- branches differtest_pollution_in_else_branchif 1 else 2 3 then- branches differtest_pollution_both_branches_extraif 1 2 else 3 4 then- both extratest_pollution_branch_consumes_extra❌ NOT Caught (2 tests failed - GAPS FOUND)
test_pollution_quotation_wrong_arity_output( Int -- Int ) [ dup ] call ;test_pollution_quotation_wrong_arity_input( Int -- Int ) [ drop drop 42 ] call ;Analysis
The type checker correctly catches pollution for direct code, but quotation effects are not properly verified when
callis applied.When
[ dup ] callruns:[ dup ]is inferred as( ..a T -- ..a T T )callapplies this effect to current stackRoot Cause Hypothesis
The quotation's effect is inferred independently, but the final stack after
callisn't being unified against the word's declared output type. The unification may be treating the inferred row variable as flexible rather than rigid.Valid Tests (all passed)
test_no_effect_annotation_pollution- inferred effects worktest_valid_effect_exact_match- exact matches worktest_valid_polymorphic_passthrough- polymorphic identity worksNext Steps
callRoot Cause Identified
The
callbuiltin has signature:Where
..aand..bare independent row variables. This means:..acaptures the stack below the quotationQcaptures the quotation type..bis completely unconstrained - it doesn't use the quotation's actual effect!The Problem
When we have:
( ..rest Int )(from declared input)( ..rest Int Quot[..x T -- ..x T T] )callsignature( ..a Q -- ..b ):..aunifies with( ..rest Int )Qunifies withQuot[...]..bis fresh and NOT constrained by the quotation's effect..b(unconstrained)( ..rest Int )unifies with..b- always succeeds!The quotation's actual effect
( ..x T -- ..x T T )is never applied.The Fix
infer_word_callneeds special handling forcall:This would properly propagate the quotation's stack effect through
call.Scope
This affects:
callbuiltinbi,dip,keep, and other combinators that invoke quotationsFix Implemented
Added special handling for
callininfer_word_call(typechecker.rs:1017-1071):Test Results
All 9 pollution tests now pass:
test_pollution_extra_pushtest_pollution_extra_duptest_pollution_consumes_extratest_pollution_in_then_branchtest_pollution_in_else_branchtest_pollution_both_branches_extratest_pollution_branch_consumes_extratest_pollution_quotation_wrong_arity_output(previously failing)test_pollution_quotation_wrong_arity_input(previously failing)Full test suite passes (255+ tests).
Other Combinators
Checked
times,while,until- these correctly use the same row variable throughout their signatures:The row variable
..ais shared, so the quotation's effect is properly constrained by unification. Onlycallhad independent row variables.Note on
dip,keep,biThese combinators don't exist in the language yet. If/when added, they should follow the pattern of using shared row variables, not the broken
callpattern.https://github.com/navicore/patch-seq/pull/232