aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-17 08:36:19 +0000
committer_ov4 <ov4@ov4.org>2026-10-17 08:36:19 +0000
commit6a3b31af5dbb22749e6d5a1cad9dfaab6863b615 (patch)
tree58d2e7d956c8c605599a3f760040234ec2556d29 /tests
parentabd56ef20bc8061bbecec29edd1301d324c2cb34 (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.json1
-rw-r--r--tests/i/class-delete-designators/expected.txt8
-rw-r--r--tests/i/class-delete-designators/notes.md5
-rw-r--r--tests/i/class-delete-designators/program.cps30
-rw-r--r--tests/i/class-destructor-throws/case.json1
-rw-r--r--tests/i/class-destructor-throws/expected.txt12
-rw-r--r--tests/i/class-destructor-throws/notes.md7
-rw-r--r--tests/i/class-destructor-throws/program.cps61
-rw-r--r--tests/i/class-unwind/case.json1
-rw-r--r--tests/i/class-unwind/expected.txt14
-rw-r--r--tests/i/class-unwind/notes.md5
-rw-r--r--tests/i/class-unwind/program.cps56
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()