Skip to content

Conversation

@emkey1
Copy link
Owner

@emkey1 emkey1 commented Oct 29, 2025

Summary

  • add TYPE_CLOSURE value support and closure environment helpers so closures carry captured state
  • teach the VM and host layer to build and retain closure environments for indirect calls
  • emit closure creation from the Pascal compiler and add a regression test that stores and returns a capturing closure

Testing

  • python3 Tests/compiler/pascal/run_compiler_tests.py (fails: Pascal compiler not found)

https://chatgpt.com/codex/tasks/task_b_6901c7d44770832996d91ea1e0099c40

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 6707 to 6713
if (captured_env) {
if (captured_env->slot_count != proc_symbol->upvalue_count) {
releaseClosureEnv(captured_env);
runtimeError(vm, "VM Error: Closure environment mismatch for '%s'.",
proc_symbol->name ? proc_symbol->name : "<anonymous>");
return INTERPRET_RUNTIME_ERROR;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Decrement frame counter on closure env mismatch

When CALL_INDIRECT sees a closure whose captured slots do not match the callee’s upvalue_count, it reports a runtime error but returns without undoing the earlier vm->frameCount++. The VM now believes there is one more active frame than actually exists, so later unwinding or cleanup will release uninitialized pointers and can corrupt memory. The frame count should be decremented before returning on this error path.

Useful? React with 👍 / 👎.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codex please address this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

  • Ensured CALL_USER_PROC and CALL unwind the frame counter when their parent frame lookup fails, preventing phantom frames after runtime errors. src/vm/vm.cL6487-L6510 src/vm/vm.cL6581-L6603
  • Balanced the frame stack for CALL_INDIRECT and PROC_CALL_INDIRECT by decrementing the frame counter on closure environment mismatches or missing parent frames while still releasing retained environments. src/vm/vm.cL6703-L6739 src/vm/vm.cL6967-L6999

Testing

  • cmake --build build --target clike

View task →

Comment on lines 6965 to 6971
if (captured_env) {
if (captured_env->slot_count != proc_symbol->upvalue_count) {
releaseClosureEnv(captured_env);
runtimeError(vm, "VM Error: Closure environment mismatch for '%s'.",
proc_symbol->name ? proc_symbol->name : "<anonymous>");
return INTERPRET_RUNTIME_ERROR;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Balance frame stack for PROC_CALL_INDIRECT errors

The same imbalance occurs in the PROC_CALL_INDIRECT handler: after allocating a CallFrame, a closure environment mismatch triggers an error and immediate return, but the incremented vm->frameCount is never rolled back. Subsequent interpreter logic will process a phantom frame and may read or free garbage. Decrement vm->frameCount before returning here as well.

Useful? React with 👍 / 👎.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codex please address the comment above.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

  • Rolled back vm->frameCount when CALL_USER_PROC cannot find an enclosing frame to avoid leaving phantom frames behind. src/vm/vm.cL6490-L6510
  • Ensured direct CALL frames initialize ownership/closure fields and undo the frame increment if the parent frame lookup fails. src/vm/vm.cL6573-L6605
  • Decremented vm->frameCount on closure environment mismatches and missing parents in CALL_INDIRECT and PROC_CALL_INDIRECT, keeping the frame stack balanced. src/vm/vm.cL6705-L6741 src/vm/vm.cL6965-L7000

Testing

  • cmake -S . -B build
  • cmake --build build --target clike

View task →

@emkey1
Copy link
Owner Author

emkey1 commented Oct 29, 2025

@codex please fix...

[ 25%] Building C object CMakeFiles/clike.dir/src/core/version.c.o
[ 25%] Building C object CMakeFiles/clike.dir/src/core/cache.c.o
[ 33%] Building C object CMakeFiles/clike.dir/src/compiler/bytecode.c.o
[ 33%] Building C object CMakeFiles/clike.dir/src/vm/vm.c.o
/home/runner/work/pscal/pscal/src/vm/vm.c: In function ‘interpretBytecode’:
/home/runner/work/pscal/pscal/src/vm/vm.c:6508:29: error: ‘captured_env’ undeclared (first use in this function)
6508 | if (captured_env) {
| ^~~~~~~~~~~~
/home/runner/work/pscal/pscal/src/vm/vm.c:6508:29: note: each undeclared identifier is reported only once for each function it appears in
gmake[3]: *** [CMakeFiles/clike.dir/build.make:331: CMakeFiles/clike.dir/src/vm/vm.c.o] Error 1
gmake[2]: *** [CMakeFiles/Makefile2:492: CMakeFiles/clike.dir/all] Error 2
gmake[1]: *** [CMakeFiles/Makefile2:499: CMakeFiles/clike.dir/rule] Error 2
gmake: *** [Makefile:231: clike] Error 2
Building target clike
Traceback (most recent call last):
File "", line 22, in
File "/usr/lib/python3.12/subprocess.py", line 413, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['cmake', '--build', 'build', '--target', 'clike']' returned non-zero exit status 2.
Error: Process completed with exit code 1.

@chatgpt-codex-connector
Copy link

Summary

Testing

  • cmake -S . -B build
  • cmake --build build --target clike

View task →

@emkey1
Copy link
Owner Author

emkey1 commented Oct 29, 2025

@codex please address the failed tests.
Test project /home/runner/work/pscal/pscal/build
Start 1: pascal_tests
1/4 Test #1: pascal_tests .....................***Failed 52.39 sec
[PASS] pascal_ext_builtin_dump – pascal --dump-ext-builtins validates structure
[PASS] pascal_cli_version – pascal -v reports latest tag
[PASS] pascal_cli_dump_ast_json – pascal --dump-ast-json emits JSON
[PASS] pascal_cli_vm_trace – pascal --vm-trace-head produces trace
[FAIL] pascal_compiler_suite – Pascal compiler regression suite
closure_capturing_store_return: Expected success but compiler exited with 1.
stderr:
Type error: expected '@proc' on right-hand side of proc pointer assignment.
Type error: expected '@proc' on right-hand side of proc pointer assignment.
Compilation failed with errors.
[PASS] pascal_ApiSendReceiveTest – Pascal fixture ApiSendReceiveTest
[PASS] pascal_ArgumentOrderMismatch – Pascal fixture ArgumentOrderMismatch
[PASS] pascal_ArgumentTypeMismatch – Pascal fixture ArgumentTypeMismatch
[PASS] pascal_ArrayArgumentMismatch – Pascal fixture ArrayArgumentMismatch
[PASS] pascal_BitwiseAndExprSuite – Pascal fixture BitwiseAndExprSuite
[PASS] pascal_BoolTest – Pascal fixture BoolTest
[PASS] pascal_BreakLoopTest – Pascal fixture BreakLoopTest
[PASS] pascal_BuiltinCaseNormalization – Pascal fixture BuiltinCaseNormalization
[PASS] pascal_BuiltinProcCallTest – Pascal fixture BuiltinProcCallTest
[PASS] pascal_BytecodeVerificationTest – Pascal fixture BytecodeVerificationTest
[PASS] pascal_CRTRoutineParam – Pascal fixture CRTRoutineParam
[PASS] pascal_CaseStatementSuite – Pascal fixture CaseStatementSuite
[PASS] pascal_CommandLineParamTest – Pascal fixture CommandLineParamTest
[PASS] pascal_CompileTimeBounds – Pascal fixture CompileTimeBounds
[PASS] pascal_CompoundAssignmentTest – Pascal fixture CompoundAssignmentTest
[PASS] pascal_ConstArrayTest – Pascal fixture ConstArrayTest
[FAIL] pascal_CreateThreadSmoke – Pascal fixture CreateThreadSmoke
stdout mismatch:\n--- /home/runner/work/pscal/pscal/Tests/Pascal/CreateThreadSmoke.out 2025-10-29 09:35:59.398263375 +0000
+++ /tmp/tmp.MMKJMWvqiR 2025-10-29 09:37:51.926264789 +0000
@@ -1 +0,0 @@
-worker
Unexpected stderr:\nVM Warning: Global variable 'myself' redefined.
[Warning Location] Offset: 2, Line: 1
[FAIL] pascal_CreateThreadWithArg – Pascal fixture CreateThreadWithArg
stdout mismatch:\n--- /home/runner/work/pscal/pscal/Tests/Pascal/CreateThreadWithArg.out 2025-10-29 09:35:59.398263375 +0000
+++ /tmp/tmp.dEvjOpL2TM 2025-10-29 09:38:16.619463881 +0000
@@ -1 +1 @@
-42
+41
pscal run exited with 1
stderr:\nVM Warning: Global variable 'myself' redefined.
[Warning Location] Offset: 2, Line: 1
[PASS] pascal_DosUnitTest – Pascal fixture DosUnitTest
[PASS] pascal_DynamicArraySetLength – Pascal fixture DynamicArraySetLength
[PASS] pascal_EnumComparisonTest – Pascal fixture EnumComparisonTest
[PASS] pascal_EnumForLoop – Pascal fixture EnumForLoop
[PASS] pascal_EofDefaultInput – Pascal fixture EofDefaultInput
[PASS] pascal_ExitEarlyTest – Pascal fixture ExitEarlyTest
[PASS] pascal_ExitFunctionReturnTest – Pascal fixture ExitFunctionReturnTest
[PASS] pascal_ExtBuiltinEnumerationTest – Pascal fixture ExtBuiltinEnumerationTest
[PASS] pascal_ExtBuiltinQueryTest – Pascal fixture ExtBuiltinQueryTest
[PASS] pascal_ExtendedBuiltinsTest – Pascal fixture ExtendedBuiltinsTest
[PASS] pascal_FileIOEdgeTests – Pascal fixture FileIOEdgeTests
[PASS] pascal_FileOfRecordRoundTrip – Pascal fixture FileOfRecordRoundTrip
[PASS] pascal_FormattingTestSuite – Pascal fixture FormattingTestSuite
[PASS] pascal_FuncPtrIndirectCall – Pascal fixture FuncPtrIndirectCall
[SKIP] pascal_GetScreenSizeTest – Pascal fixture GetScreenSizeTest
Graphics builtins unavailable
[PASS] pascal_HttpAsyncFileURL – Pascal fixture HttpAsyncFileURL
[PASS] pascal_HttpFetchDataURL – Pascal fixture HttpFetchDataURL
[PASS] pascal_HttpHeadersFileURL – Pascal fixture HttpHeadersFileURL
[PASS] pascal_HttpRequestToFileFileURL – Pascal fixture HttpRequestToFileFileURL
[PASS] pascal_IfdefExtendedTest – Pascal fixture IfdefExtendedTest
[PASS] pascal_InvalidArrayBounds – Pascal fixture InvalidArrayBounds
[PASS] pascal_LowHighCharTest – Pascal fixture LowHighCharTest
[PASS] pascal_LowHighDynamicArrayTest – Pascal fixture LowHighDynamicArrayTest
[PASS] pascal_MalformedUses – Pascal fixture MalformedUses
[PASS] pascal_MathLibTest – Pascal fixture MathLibTest
[PASS] pascal_MathSuite01 – Pascal fixture MathSuite01
[PASS] pascal_MultiDimArrayTest – Pascal fixture MultiDimArrayTest
[PASS] pascal_MutexReuseTest – Pascal fixture MutexReuseTest
[PASS] pascal_NestedRoutineAccessTest – Pascal fixture NestedRoutineAccessTest
[PASS] pascal_NestedRoutineSuite – Pascal fixture NestedRoutineSuite
[PASS] pascal_NestedVarArray – Pascal fixture NestedVarArray
[PASS] pascal_OpenArrayBaseTypeMismatch – Pascal fixture OpenArrayBaseTypeMismatch
[PASS] pascal_OpenArrayParam – Pascal fixture OpenArrayParam
[PASS] pascal_PointerTortureTest – Pascal fixture PointerTortureTest
[PASS] pascal_PrimitiveArgCall – Pascal fixture PrimitiveArgCall
[PASS] pascal_PrintfFloatLenPascal – Pascal fixture PrintfFloatLenPascal
[PASS] pascal_PrintfIntLenPascal – Pascal fixture PrintfIntLenPascal
[PASS] pascal_ProcPtrAssign – Pascal fixture ProcPtrAssign
[PASS] pascal_ProcPtrAssignMismatch – Pascal fixture ProcPtrAssignMismatch
[PASS] pascal_ProcPtrIndirectCall – Pascal fixture ProcPtrIndirectCall
[PASS] pascal_ProcPtrParamBad – Pascal fixture ProcPtrParamBad
[PASS] pascal_ProcPtrParamOK – Pascal fixture ProcPtrParamOK
[PASS] pascal_ProcVarLocalScope – Pascal fixture ProcVarLocalScope
[PASS] pascal_ProgramFileList – Pascal fixture ProgramFileList
[PASS] pascal_ReadlnString – Pascal fixture ReadlnString
[PASS] pascal_RealExponentTest – Pascal fixture RealExponentTest
[PASS] pascal_RealIntConversionTest – Pascal fixture RealIntConversionTest
[PASS] pascal_RealIntOverflowTest – Pascal fixture RealIntOverflowTest
[PASS] pascal_RealTimeClockExtBuiltinTest – Pascal fixture RealTimeClockExtBuiltinTest
[PASS] pascal_RecordFieldIndexTest – Pascal fixture RecordFieldIndexTest
[PASS] pascal_RecordsArray – Pascal fixture RecordsArray
[PASS] pascal_RecursiveMutexTest – Pascal fixture RecursiveMutexTest
[PASS] pascal_ReverseStringTest – Pascal fixture ReverseStringTest
[SKIP] pascal_SDLFeaturesTest – Pascal fixture SDLFeaturesTest
Graphics builtins unavailable
[PASS] pascal_ShortCircuitTest – Pascal fixture ShortCircuitTest
[SKIP] pascal_SocketSendReceiveTest – Pascal fixture SocketSendReceiveTest
Network tests disabled (set RUN_NET_TESTS=1)
[PASS] pascal_SqliteSmokeTest – Pascal fixture SqliteSmokeTest
[PASS] pascal_StrBuiltinTest – Pascal fixture StrBuiltinTest
[PASS] pascal_StringTruncationTest – Pascal fixture StringTruncationTest
[PASS] pascal_SuccEnumTest – Pascal fixture SuccEnumTest
[PASS] pascal_SwapArrayElements – Pascal fixture SwapArrayElements
[PASS] pascal_TestFileOperations – Pascal fixture TestFileOperations
[PASS] pascal_TestIncDecWithVerify – Pascal fixture TestIncDecWithVerify
[PASS] pascal_TestSuite7 – Pascal fixture TestSuite7
[PASS] pascal_TestVarParam – Pascal fixture TestVarParam
[PASS] pascal_ThreadMutexTest – Pascal fixture ThreadMutexTest
[PASS] pascal_ThreadSpawnJoinTest – Pascal fixture ThreadSpawnJoinTest
[PASS] pascal_ThreadSpawnNestedCaptureTest – Pascal fixture ThreadSpawnNestedCaptureTest
[PASS] pascal_TrimLeftRightTest – Pascal fixture TrimLeftRightTest
[PASS] pascal_TypeTestSuite – Pascal fixture TypeTestSuite
[PASS] pascal_UpperCaseTest – Pascal fixture UpperCaseTest
[PASS] pascal_UserProcCallTest – Pascal fixture UserProcCallTest
[PASS] pascal_VMVersionQueryTest – Pascal fixture VMVersionQueryTest
[PASS] pascal_VarInitTest – Pascal fixture VarInitTest
[PASS] pascal_XorExprSuite – Pascal fixture XorExprSuite
[PASS] pascal_YyjsonBuiltinTest – Pascal fixture YyjsonBuiltinTest
[PASS] pascal_cache_staleness – Cache invalidation honours matching timestamps

Ran 100 Pascal test(s); 3 failure(s); 3 skipped

Start 2: clike_tests

2/4 Test #2: clike_tests ...................... Passed 12.77 sec
Start 3: rea_tests
3/4 Test #3: rea_tests ........................***Failed 6.72 sec
[PASS] rea_ext_builtin_dump – rea --dump-ext-builtins validates structure
[PASS] rea_cli_version – rea -v reports latest tag
[PASS] rea_cli_strict_dump – rea --strict --dump-bytecode-only prints banner
[PASS] rea_cli_no_run – rea --no-run compiles without executing
[PASS] rea_cli_vm_trace – rea --vm-trace-head produces trace
[PASS] rea_array_decl – Rea fixture array_decl
[PASS] rea_array_element_read – Rea fixture array_element_read
[PASS] rea_array_literal_init – Rea fixture array_literal_init
[PASS] rea_assign_expr – Rea fixture assign_expr
[PASS] rea_balls3d_builtin_compare – Rea fixture balls3d_builtin_compare
[PASS] rea_balls3d_demo_regression – Rea fixture balls3d_demo_regression
[PASS] rea_block_decl – Rea fixture block_decl
[PASS] rea_bytecode_dump – Rea fixture bytecode_dump
[PASS] rea_bytecode_exec – Rea fixture bytecode_exec
[PASS] rea_casts – Rea fixture casts
[PASS] rea_class_decl – Rea fixture class_decl
[PASS] rea_class_instantiation – Rea fixture class_instantiation
[PASS] rea_constructor_default – Rea fixture constructor_default
[SKIP] rea_constructor_init – Rea fixture constructor_init
Skipped via REA_SKIP_TESTS
[PASS] rea_continue_for – Rea fixture continue_for
[PASS] rea_continue_repeat – Rea fixture continue_repeat
[PASS] rea_continue_while – Rea fixture continue_while
[PASS] rea_expr – Rea fixture expr
[PASS] rea_ext_builtin_enumeration – Rea fixture ext_builtin_enumeration
[SKIP] rea_field_access_assign – Rea fixture field_access_assign
Skipped via REA_SKIP_TESTS
[SKIP] rea_field_access_read – Rea fixture field_access_read
Skipped via REA_SKIP_TESTS
[PASS] rea_for_local_decl – Rea fixture for_local_decl
[PASS] rea_hex_number – Rea fixture hex_number
[PASS] rea_if – Rea fixture if
[PASS] rea_ifdef_extended – Rea fixture ifdef_extended
[PASS] rea_int_division_and_comments – Rea fixture int_division_and_comments
[PASS] rea_mandelbrotrow – Rea fixture mandelbrotrow
[SKIP] rea_method_this_assign – Rea fixture method_this_assign
Skipped via REA_SKIP_TESTS
[PASS] rea_modulo – Rea fixture modulo
[PASS] rea_myself_keyword – Rea fixture myself_keyword
[SKIP] rea_new_alloc – Rea fixture new_alloc
Skipped via REA_SKIP_TESTS
[SKIP] rea_new_assign_ptr – Rea fixture new_assign_ptr
Skipped via REA_SKIP_TESTS
[PASS] rea_new_local_vtable – Rea fixture new_local_vtable
[FAIL] rea_printf_format – Rea fixture printf_format
stderr mismatch:\n--- /home/runner/work/pscal/pscal/Tests/rea/printf_format.err 2025-10-29 09:35:59.416263478 +0000
+++ /tmp/tmp.G2zAGyfgQY 2025-10-29 09:38:37.238623840 +0000
@@ -8,7 +8,7 @@
0009 | CONSTANT 4 '7'
0011 | CONSTANT 5 '1.234560'
0013 | CONSTANT 6 '3'
-0015 | CALL_HOST 3 (ID: 3)
+0015 | CALL_HOST 4 (ID: 4)
0017 | POP
0018 0 HALT
== End Disassembly: rea/printf_format.rea ==
[PASS] rea_realtimeclock – Rea fixture realtimeclock
[PASS] rea_scientific_number – Rea fixture scientific_number
[PASS] rea_short_circuit – Rea fixture short_circuit
[PASS] rea_sqlite_smoke – Rea fixture sqlite_smoke
[PASS] rea_super_constructor – Rea fixture super_constructor
[PASS] rea_super_method – Rea fixture super_method
[PASS] rea_switch_stmt – Rea fixture switch_stmt
[PASS] rea_thread_spawn_join – Rea fixture thread_spawn_join
[PASS] rea_thread_spawn_join_decl – Rea fixture thread_spawn_join_decl
[PASS] rea_write_sugar – Rea fixture write_sugar
[PASS] rea_writeln_format – Rea fixture writeln_format
[PASS] rea_xor_expr – Rea fixture xor_expr
[PASS] rea_yyjson_builtin – Rea fixture yyjson_builtin
[PASS] rea_hangman_example – Hangman example emits vtable before constructor
[PASS] rea_cache_reuse – Cache reuse surfaces bytecode reuse notice
[PASS] rea_cache_binary_staleness – Binary timestamp invalidates cached bytecode

Ran 55 Rea test(s); 1 failure(s); 6 skipped

Start 4: json2bc_tests

4/4 Test #4: json2bc_tests .................... Passed 0.09 sec

50% tests passed, 2 tests failed out of 4

Total Test time (real) = 72.06 sec

The following tests FAILED:
1 - pascal_tests (Failed)
3 - rea_tests (Failed)
Errors while running CTest
Error: Process completed with exit code 8.

@chatgpt-codex-connector
Copy link

Summary

Testing

  • cmake -S . -B build
  • cmake --build build --target clike
  • cmake --build build --target pascal
  • cmake --build build --target rea
  • build/bin/pascal Tests/Pascal/CreateThreadSmoke
  • build/bin/pascal Tests/Pascal/CreateThreadWithArg
  • build/bin/rea --dump-bytecode-only Tests/rea/printf_format.rea

View task →

@emkey1
Copy link
Owner Author

emkey1 commented Oct 29, 2025

@codex please fix...
[FAIL] closure_return_function_runtime – Capturing function can be returned
Reason: expected exit 0 but got 1
stderr:
L18: Compiler Error: Function 'add' expects 1 arguments, got 0.
Compilation failed with errors.
[FAIL] closure_store_runtime – Stored closure updates captured locals
Reason: expected exit 0 but got 1
stderr:
Type error: '@step' does not name a known procedure or function.
Type error: expected '@proc' on right-hand side of proc pointer assignment.
Compilation failed with errors.

@chatgpt-codex-connector
Copy link

I’m sorry, but I wasn’t able to complete these changes.

View task →

@emkey1
Copy link
Owner Author

emkey1 commented Oct 29, 2025

@codex please fix...
[FAIL] closure_return_function_runtime – Capturing function can be returned
Reason: expected exit 0 but got 1
stderr:
L18: Compiler Error: Function 'add' expects 1 arguments, got 0.
Compilation failed with errors.
[FAIL] closure_store_runtime – Stored closure updates captured locals
Reason: expected exit 0 but got 1
stderr:
Type error: '@step' does not name a known procedure or function.
Type error: expected '@proc' on right-hand side of proc pointer assignment.
Compilation failed with errors.

@chatgpt-codex-connector
Copy link

Summary

Testing

  • cmake --build build --target pascal
  • python3 Tests/scope_verify/pascal/pascal_scope_test_harness.py --only closure_return_function_runtime
  • python3 Tests/scope_verify/pascal/pascal_scope_test_harness.py --only closure_store_runtime

View task →

@emkey1 emkey1 merged commit 0a9c334 into devel Oct 29, 2025
2 of 3 checks passed
@emkey1 emkey1 deleted the codex/introduce-closure-support-in-compiler branch October 31, 2025 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants