From c372c08dc940db58f1da9cadeb64b4cb3c837704 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sat, 17 Oct 2026 06:58:13 +0000 Subject: fix(classes): preserve method-only heap objects and document NEW Allocate at least one byte for method-only classes so their heap addresses remain usable through pointers. Complete constructor diagnostics, field-order and exception coverage, document construction and rebinding, and highlight NEW. Co-Authored-By: Claude Opus 5.5 --- tests/i/class-heap-new/expected.txt | 9 +++ tests/i/class-heap-new/notes.md | 4 ++ tests/i/class-heap-new/program.cps | 65 ++++++++++++++++++++++ .../class-constructor-array-args-error/case.json | 8 +++ .../class-constructor-array-args-error/program.cps | 5 ++ .../class-constructor-duplicate-error/case.json | 8 +++ .../class-constructor-duplicate-error/program.cps | 6 ++ .../class-constructor-expression-error/case.json | 8 +++ .../class-constructor-expression-error/program.cps | 4 ++ .../iii/class-constructor-function-error/case.json | 8 +++ .../class-constructor-function-error/program.cps | 5 ++ tests/iii/class-constructor-global-error/case.json | 8 +++ .../iii/class-constructor-global-error/program.cps | 2 + .../class-constructor-nested-new-error/case.json | 8 +++ .../class-constructor-nested-new-error/program.cps | 5 ++ tests/iii/class-new-errors/case.json | 12 ++++ tests/iii/class-new-errors/program.cps | 5 ++ 17 files changed, 170 insertions(+) create mode 100644 tests/iii/class-constructor-array-args-error/case.json create mode 100644 tests/iii/class-constructor-array-args-error/program.cps create mode 100644 tests/iii/class-constructor-duplicate-error/case.json create mode 100644 tests/iii/class-constructor-duplicate-error/program.cps create mode 100644 tests/iii/class-constructor-expression-error/case.json create mode 100644 tests/iii/class-constructor-expression-error/program.cps create mode 100644 tests/iii/class-constructor-function-error/case.json create mode 100644 tests/iii/class-constructor-function-error/program.cps create mode 100644 tests/iii/class-constructor-global-error/case.json create mode 100644 tests/iii/class-constructor-global-error/program.cps create mode 100644 tests/iii/class-constructor-nested-new-error/case.json create mode 100644 tests/iii/class-constructor-nested-new-error/program.cps (limited to 'tests') diff --git a/tests/i/class-heap-new/expected.txt b/tests/i/class-heap-new/expected.txt index 37e731a..27bf128 100644 --- a/tests/i/class-heap-new/expected.txt +++ b/tests/i/class-heap-new/expected.txt @@ -14,3 +14,12 @@ declare caught new caught loop:1 loop:1 +failed rebind:71,71 +nested heap:80,1 +first +second +outer +first +second +outer +method only:99 diff --git a/tests/i/class-heap-new/notes.md b/tests/i/class-heap-new/notes.md index 3a6435c..a395599 100644 --- a/tests/i/class-heap-new/notes.md +++ b/tests/i/class-heap-new/notes.md @@ -3,3 +3,7 @@ Human review: pending. Expected output was independently traced by AI. Project semantics extend guide section 10.2. Each Box constructor sets Value to 1. Rebinding A preserves the old stack object's 7 while the new object becomes 9. Ordinary A <- B copies 4 into the current heap object, leaving B and the old stack object independent. BYVAL copies 4, then independently rebinds and changes its local object to 20. BYREF NEW copies 1 into A's current object, observed through CurrentA. Replacing this likewise copies into that same object; explicit Reset calls the ordinary constructor method. Array-element, field, bare field alias and pointer-dereference NEW assignments preserve their destination addresses and copy 1. Named's argument is 42. Failing throws in both DECLARE and heap construction, reaching the surrounding catches. Loop DECLARE restores its original stack object and constructs it on every iteration, so the previous heap object's 99 is never printed. + +Fragile's failed constructor leaves Stable bound to its original object, whose Value remains 71 through both names. Heap construction of Holder constructs its Box field as 1 while the old Holder's field remains 80 through Field. Ordered prints first, second, outer for stack and heap construction, proving field declaration order before the enclosing constructor. PrivateFactory's unused Build method checks that same-class access permits private automatic and explicit construction. + +A method-only Marker class has no fields, but heap NEW must still provide a non-null object address so a pointer receiver can call Get and print 99. diff --git a/tests/i/class-heap-new/program.cps b/tests/i/class-heap-new/program.cps index 7b8363f..aff248a 100644 --- a/tests/i/class-heap-new/program.cps +++ b/tests/i/class-heap-new/program.cps @@ -98,3 +98,68 @@ FOR Index <- 1 TO 2 Local <- NEW Box Local.Value <- 99 NEXT Index +CLASS Fragile + Value : INTEGER + PROCEDURE NEW(Fail : BOOLEAN) + Value <- 70 + IF Fail THEN + THROW + ENDIF + ENDPROCEDURE +ENDCLASS +TYPE FragilePointer = ^Fragile +DECLARE Stable : Fragile(FALSE) +DECLARE Original : FragilePointer +Original <- ^Stable +Stable.Value <- 71 +TRY + Stable <- NEW Fragile(TRUE) +CATCH + OUTPUT "failed rebind:", Stable.Value, ",", Original^.Value +ENDTRY +H.Item.Value <- 80 +H <- NEW Holder() +OUTPUT "nested heap:", Field^.Value, ",", H.Item.Value +CLASS OrderedFirst + PROCEDURE NEW() + OUTPUT "first" + ENDPROCEDURE +ENDCLASS +CLASS OrderedSecond + PROCEDURE NEW() + OUTPUT "second" + ENDPROCEDURE +ENDCLASS +CLASS Ordered + First : OrderedFirst + Second : OrderedSecond + PROCEDURE NEW() + OUTPUT "outer" + ENDPROCEDURE +ENDCLASS +DECLARE Order : Ordered +Order <- NEW Ordered +CLASS PrivateFactory + Value : INTEGER + PRIVATE PROCEDURE NEW() + Value <- 1 + ENDPROCEDURE + PROCEDURE Build() + DECLARE Local : PrivateFactory + Local <- NEW PrivateFactory + Local.NEW() + ENDPROCEDURE +ENDCLASS +CLASS Marker + PROCEDURE NEW() + ENDPROCEDURE + FUNCTION Get() RETURNS INTEGER + RETURN 99 + ENDFUNCTION +ENDCLASS +TYPE MarkerPointer = ^Marker +DECLARE M : Marker +DECLARE MarkerAddress : MarkerPointer +M <- NEW Marker +MarkerAddress <- ^M +OUTPUT "method only:", MarkerAddress^.Get() diff --git a/tests/iii/class-constructor-array-args-error/case.json b/tests/iii/class-constructor-array-args-error/case.json new file mode 100644 index 0000000..6677de3 --- /dev/null +++ b/tests/iii/class-constructor-array-args-error/case.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "reason": "Unexpected token", + "line": 5 + } + ] +} diff --git a/tests/iii/class-constructor-array-args-error/program.cps b/tests/iii/class-constructor-array-args-error/program.cps new file mode 100644 index 0000000..d3c7369 --- /dev/null +++ b/tests/iii/class-constructor-array-args-error/program.cps @@ -0,0 +1,5 @@ +CLASS Box + PROCEDURE NEW(Number : INTEGER) + ENDPROCEDURE +ENDCLASS +DECLARE Items : ARRAY[1:2] OF Box(1) diff --git a/tests/iii/class-constructor-duplicate-error/case.json b/tests/iii/class-constructor-duplicate-error/case.json new file mode 100644 index 0000000..f26c170 --- /dev/null +++ b/tests/iii/class-constructor-duplicate-error/case.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "reason": "Duplicate member new in class Box", + "line": 4 + } + ] +} diff --git a/tests/iii/class-constructor-duplicate-error/program.cps b/tests/iii/class-constructor-duplicate-error/program.cps new file mode 100644 index 0000000..0782c5e --- /dev/null +++ b/tests/iii/class-constructor-duplicate-error/program.cps @@ -0,0 +1,6 @@ +CLASS Box + PROCEDURE NEW() + ENDPROCEDURE + PROCEDURE new(Number : INTEGER) + ENDPROCEDURE +ENDCLASS diff --git a/tests/iii/class-constructor-expression-error/case.json b/tests/iii/class-constructor-expression-error/case.json new file mode 100644 index 0000000..3055433 --- /dev/null +++ b/tests/iii/class-constructor-expression-error/case.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "reason": "NEW is only allowed on the right of an assignment", + "line": 4 + } + ] +} diff --git a/tests/iii/class-constructor-expression-error/program.cps b/tests/iii/class-constructor-expression-error/program.cps new file mode 100644 index 0000000..67addd5 --- /dev/null +++ b/tests/iii/class-constructor-expression-error/program.cps @@ -0,0 +1,4 @@ +CLASS Box + Value : INTEGER +ENDCLASS +OUTPUT NEW Box() diff --git a/tests/iii/class-constructor-function-error/case.json b/tests/iii/class-constructor-function-error/case.json new file mode 100644 index 0000000..f654a51 --- /dev/null +++ b/tests/iii/class-constructor-function-error/case.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "reason": "constructor NEW must be a PROCEDURE", + "line": 2 + } + ] +} diff --git a/tests/iii/class-constructor-function-error/program.cps b/tests/iii/class-constructor-function-error/program.cps new file mode 100644 index 0000000..1daa9fe --- /dev/null +++ b/tests/iii/class-constructor-function-error/program.cps @@ -0,0 +1,5 @@ +CLASS Box + FUNCTION NEW() RETURNS INTEGER + RETURN 1 + ENDFUNCTION +ENDCLASS diff --git a/tests/iii/class-constructor-global-error/case.json b/tests/iii/class-constructor-global-error/case.json new file mode 100644 index 0000000..818dfca --- /dev/null +++ b/tests/iii/class-constructor-global-error/case.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "reason": "constructor NEW is only allowed in a CLASS", + "line": 1 + } + ] +} diff --git a/tests/iii/class-constructor-global-error/program.cps b/tests/iii/class-constructor-global-error/program.cps new file mode 100644 index 0000000..4ba097a --- /dev/null +++ b/tests/iii/class-constructor-global-error/program.cps @@ -0,0 +1,2 @@ +PROCEDURE NEW() +ENDPROCEDURE diff --git a/tests/iii/class-constructor-nested-new-error/case.json b/tests/iii/class-constructor-nested-new-error/case.json new file mode 100644 index 0000000..414816c --- /dev/null +++ b/tests/iii/class-constructor-nested-new-error/case.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "reason": "NEW is only allowed on the right of an assignment", + "line": 5 + } + ] +} diff --git a/tests/iii/class-constructor-nested-new-error/program.cps b/tests/iii/class-constructor-nested-new-error/program.cps new file mode 100644 index 0000000..b0bd349 --- /dev/null +++ b/tests/iii/class-constructor-nested-new-error/program.cps @@ -0,0 +1,5 @@ +CLASS Box + PROCEDURE NEW(Other : Box) + ENDPROCEDURE +ENDCLASS +DECLARE Item : Box(NEW Box) diff --git a/tests/iii/class-new-errors/case.json b/tests/iii/class-new-errors/case.json index 8ce52ed..7f33ffd 100644 --- a/tests/iii/class-new-errors/case.json +++ b/tests/iii/class-new-errors/case.json @@ -27,6 +27,18 @@ { "reason": "NEW target must have type Hidden", "line": 26 + }, + { + "reason": "Cannot access private method NEW of type Hidden", + "line": 28 + }, + { + "reason": "has no constructor NEW", + "line": 30 + }, + { + "reason": "Cannot coerce value of LLVM type to target type INTEGER", + "line": 31 } ] } diff --git a/tests/iii/class-new-errors/program.cps b/tests/iii/class-new-errors/program.cps index 3a9e9c2..78db102 100644 --- a/tests/iii/class-new-errors/program.cps +++ b/tests/iii/class-new-errors/program.cps @@ -24,3 +24,8 @@ B <- NEW Box(1) R <- NEW Required R <- NEW Required() B <- NEW Hidden +PROCEDURE Forbidden(Target : Hidden) + Target <- NEW Hidden() +ENDPROCEDURE +B <- NEW Box("bad") +R <- NEW Required("bad") -- cgit v1.3.1-4-g156e