diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-17 08:36:19 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-17 08:36:19 +0000 |
| commit | 6a3b31af5dbb22749e6d5a1cad9dfaab6863b615 (patch) | |
| tree | 58d2e7d956c8c605599a3f760040234ec2556d29 /tests | |
| parent | abd56ef20bc8061bbecec29edd1301d324c2cb34 (diff) | |
feat(classes): destroy stack objects during exception unwinding
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/i/class-delete-designators/case.json | 1 | ||||
| -rw-r--r-- | tests/i/class-delete-designators/expected.txt | 8 | ||||
| -rw-r--r-- | tests/i/class-delete-designators/notes.md | 5 | ||||
| -rw-r--r-- | tests/i/class-delete-designators/program.cps | 30 | ||||
| -rw-r--r-- | tests/i/class-destructor-throws/case.json | 1 | ||||
| -rw-r--r-- | tests/i/class-destructor-throws/expected.txt | 12 | ||||
| -rw-r--r-- | tests/i/class-destructor-throws/notes.md | 7 | ||||
| -rw-r--r-- | tests/i/class-destructor-throws/program.cps | 61 | ||||
| -rw-r--r-- | tests/i/class-unwind/case.json | 1 | ||||
| -rw-r--r-- | tests/i/class-unwind/expected.txt | 14 | ||||
| -rw-r--r-- | tests/i/class-unwind/notes.md | 5 | ||||
| -rw-r--r-- | tests/i/class-unwind/program.cps | 56 |
12 files changed, 201 insertions, 0 deletions
diff --git a/tests/i/class-delete-designators/case.json b/tests/i/class-delete-designators/case.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/i/class-delete-designators/case.json @@ -0,0 +1 @@ +{} diff --git a/tests/i/class-delete-designators/expected.txt b/tests/i/class-delete-designators/expected.txt new file mode 100644 index 0000000..3a62de6 --- /dev/null +++ b/tests/i/class-delete-designators/expected.txt @@ -0,0 +1,8 @@ +item:1 +item:1 +item:2 +[Fatal] line 27: Invalid memory address (not an active allocation) +array caught +item:3 +item:0 +item:0 diff --git a/tests/i/class-delete-designators/notes.md b/tests/i/class-delete-designators/notes.md new file mode 100644 index 0000000..43378ae --- /dev/null +++ b/tests/i/class-delete-designators/notes.md @@ -0,0 +1,5 @@ +# Destructor regression + +Human review: pending. Expected output was independently traced by AI. + +Explicit this.DELETE prints only the method. DELETE Ptr^ and DELETE this release heap objects; deleting an array element fails validation. Main destroys the array in reverse and original Obj storage. diff --git a/tests/i/class-delete-designators/program.cps b/tests/i/class-delete-designators/program.cps new file mode 100644 index 0000000..be552e7 --- /dev/null +++ b/tests/i/class-delete-designators/program.cps @@ -0,0 +1,30 @@ +CLASS Item + Id : INTEGER + PROCEDURE DELETE() + OUTPUT "item:", Id + ENDPROCEDURE + PROCEDURE Explicit() + this.DELETE() + ENDPROCEDURE + PROCEDURE Release() + DELETE this + ENDPROCEDURE +ENDCLASS +TYPE ItemPtr = ^Item +DECLARE Obj : Item +DECLARE Ptr : ItemPtr +Obj <- NEW Item() +Obj.Id <- 1 +Ptr <- ^Obj +CALL Obj.Explicit() +DELETE Ptr^ +Obj <- NEW Item() +Obj.Id <- 2 +CALL Obj.Release() +DECLARE Arr : ARRAY[1:2] OF Item +Arr[2].Id <- 3 +TRY + DELETE Arr[2] +CATCH + OUTPUT "array caught" +ENDTRY diff --git a/tests/i/class-destructor-throws/case.json b/tests/i/class-destructor-throws/case.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/i/class-destructor-throws/case.json @@ -0,0 +1 @@ +{} diff --git a/tests/i/class-destructor-throws/expected.txt b/tests/i/class-destructor-throws/expected.txt new file mode 100644 index 0000000..0bbdb25 --- /dev/null +++ b/tests/i/class-destructor-throws/expected.txt @@ -0,0 +1,12 @@ +bomb +destroy:1 +normal caught +bomb +destroy:2 +catch exit caught +bomb +return caught locally +destroy:3 +3 +bomb +unwind throw caught diff --git a/tests/i/class-destructor-throws/notes.md b/tests/i/class-destructor-throws/notes.md new file mode 100644 index 0000000..3ccf7c5 --- /dev/null +++ b/tests/i/class-destructor-throws/notes.md @@ -0,0 +1,7 @@ +# Destructor regression + +Human review: pending. Expected output was independently traced by AI. + +A destructor throwing on normal return clears its alive flag and the unwind pad destroys the remaining Trace once. RETURN in CATCH preserves catch cleanup; RETURN inside TRY preserves its local handler. Function return values are evaluated before destruction. + +A destructor throwing during exception cleanup propagates to the caller instead of terminating. diff --git a/tests/i/class-destructor-throws/program.cps b/tests/i/class-destructor-throws/program.cps new file mode 100644 index 0000000..5ff62ac --- /dev/null +++ b/tests/i/class-destructor-throws/program.cps @@ -0,0 +1,61 @@ +CLASS Trace + Id : INTEGER + PROCEDURE DELETE() + OUTPUT "destroy:", Id + ENDPROCEDURE +ENDCLASS +CLASS Bomb + PROCEDURE DELETE() + OUTPUT "bomb" + THROW + ENDPROCEDURE +ENDCLASS +PROCEDURE Normal() + DECLARE First : Trace + First.Id <- 1 + DECLARE Last : Bomb +ENDPROCEDURE +PROCEDURE DuringUnwind() + DECLARE Last : Bomb + THROW +ENDPROCEDURE +PROCEDURE InCatch() + DECLARE First : Trace + First.Id <- 2 + TRY + THROW + CATCH + DECLARE Last : Bomb + RETURN + ENDTRY +ENDPROCEDURE +PROCEDURE InTry() + DECLARE Last : Bomb + TRY + RETURN + CATCH + OUTPUT "return caught locally" + ENDTRY +ENDPROCEDURE +FUNCTION Result() RETURNS INTEGER + DECLARE Local : Trace + Local.Id <- 3 + RETURN Local.Id +ENDFUNCTION +TRY + CALL Normal() +CATCH + OUTPUT "normal caught" +ENDTRY +TRY + CALL InCatch() +CATCH + OUTPUT "catch exit caught" +ENDTRY +CALL InTry() +OUTPUT Result() +TRY + CALL DuringUnwind() +CATCH + OUTPUT "unwind throw caught" +ENDTRY diff --git a/tests/i/class-unwind/case.json b/tests/i/class-unwind/case.json new file mode 100644 index 0000000..2c4afe3 --- /dev/null +++ b/tests/i/class-unwind/case.json @@ -0,0 +1 @@ +{"exit_code": 1} diff --git a/tests/i/class-unwind/expected.txt b/tests/i/class-unwind/expected.txt new file mode 100644 index 0000000..3503cb5 --- /dev/null +++ b/tests/i/class-unwind/expected.txt @@ -0,0 +1,14 @@ +destroy:2 +destroy:1 +destroy:3 +caught +destroy:60 +loop caught +local caught +still alive +destroy:70 +uncaught next +destroy:2 +destroy:1 +destroy:3 +Unhandled exception. Program terminated. diff --git a/tests/i/class-unwind/notes.md b/tests/i/class-unwind/notes.md new file mode 100644 index 0000000..8650a9b --- /dev/null +++ b/tests/i/class-unwind/notes.md @@ -0,0 +1,5 @@ +# Destructor regression + +Human review: pending. Expected output was independently traced by AI. + +Nested frames unwind in reverse local order before main catches. The earlier loop call sees a later DECLARE alive on iteration two. A same-function catch keeps locals alive. The uncaught path cleans procedure frames and deliberately omits main object 100. diff --git a/tests/i/class-unwind/program.cps b/tests/i/class-unwind/program.cps new file mode 100644 index 0000000..298cd3a --- /dev/null +++ b/tests/i/class-unwind/program.cps @@ -0,0 +1,56 @@ +CLASS Trace + Id : INTEGER + PROCEDURE DELETE() + OUTPUT "destroy:", Id + ENDPROCEDURE +ENDCLASS +PROCEDURE Inner() + DECLARE A : Trace + A.Id <- 1 + DECLARE B : Trace + B.Id <- 2 + THROW +ENDPROCEDURE +PROCEDURE Outer() + DECLARE A : Trace + A.Id <- 3 + CALL Inner() +ENDPROCEDURE +PROCEDURE Check(I : INTEGER) + IF I = 2 THEN + THROW + ENDIF +ENDPROCEDURE +PROCEDURE Loop() + DECLARE I : INTEGER + FOR I <- 1 TO 2 + CALL Check(I) + DECLARE Late : Trace + Late.Id <- 60 + NEXT I +ENDPROCEDURE +PROCEDURE LocalCatch() + DECLARE Kept : Trace + Kept.Id <- 70 + TRY + THROW + CATCH + OUTPUT "local caught" + ENDTRY + OUTPUT "still alive" +ENDPROCEDURE +DECLARE MainObject : Trace +MainObject.Id <- 100 +TRY + CALL Outer() +CATCH + OUTPUT "caught" +ENDTRY +TRY + CALL Loop() +CATCH + OUTPUT "loop caught" +ENDTRY +CALL LocalCatch() +OUTPUT "uncaught next" +CALL Outer() |
