Replace stack-unwinding error handling with error-flag returns#5002
Replace stack-unwinding error handling with error-flag returns#5002SeanTAllen wants to merge 1 commit into
Conversation
|
This isnt actually ready for review. I switched it to that to get CI to run. |
|
Did an ensemble review of this (security, API, performance, correctness — four independent passes). The core codegen is correct across all paths I traced. A few things to address: Partial FFI declarations are silently accepted. The plan called for a compiler error when users write Brace style in
|
ba3e87a to
858af9a
Compare
99bc832 to
860f182
Compare
860f182 to
6eb3d96
Compare
|
With This PR is itself the statement that FFI partiality goes away. The question I want to raise is whether that change also needs to go through the RFC process on its own. I don't think it does. This is a breaking change, but it's one that falls out of an implementation change rather than a deliberate piece of language design. We didn't set out to remove partial FFI as a feature. We removed stack unwinding, and partial FFI stopped meaning anything as a consequence. If folks want it through an RFC anyway, there's a clean way to split it. We leave it legal to write a meaningless |
I would prefer to remove it, and here's my reasoning: AS we are also removing If there is a case out there with a partial C-FFI function that never had a path to |
|
I agree that we should remove the silent acceptance of Probably the nicest thing we could do here is continue to accept the syntax at the parser/lexer level, but catch it in some early pass and give an informative error about this change. We could leave that in place for some period of time to help people migrate. If that seems like too much trouble, I'm okay with skipping that "nice to have" error. |
6eb3d96 to
32d1106
Compare
|
Note for whoever merges this: open a follow-up issue to make This PR (Approach A) rejects a
This is the AST-shape change the repo's "Known Couplings" warns about, so run the full coupled set ( |
5950967 to
2d3e8ee
Compare
|
@jemc this has the "friendly error warning in place". its ready for review. |
2d3e8ee to
1b106e6
Compare
Pony's `error` keyword propagated errors by unwinding the stack, with
libunwind on POSIX and SEH on Windows doing the work. That forced an
LLVM `invoke` at every partial call site instead of a plain `call`,
which blocks optimization across those sites, and it pulled in a pile
of platform-specific landing pad, personality, and LSDA machinery.
Partial functions now return an error flag instead. A value-returning
partial function returns `{T, i1}`; a void-returning class constructor
returns `i1`. The flag is `false` on a normal return and `true` on an
error. Callers check it and either branch to their handler or
propagate. A `try` block becomes an ordinary branch.
With Pony-to-Pony propagation no longer riding on stack unwinding, the
unwinding infrastructure is dead code, so it goes: `pony_error()`,
`pony_try()`, `ponyint_personality_v0()`, LSDA scanning, the
`except_try_catch.ll` IR, and every landing pad, invoke, and
personality reference in codegen.
Removing `pony_error()` reaches past the compiler internals:
- Bare functions that hit `error` outside a `try` now call `abort()`.
A bare partial lambda can no longer carry an error across a C frame,
because the stack unwind that used to carry it is gone. The
`serialise` package relied on that pattern, and RFC #83 already calls
for its removal as a security footgun, so it and its tests go with
this change. The C-level serialise runtime the compiler uses
internally is untouched.
- `pony_error()` was also the only way a partial FFI function could
signal failure, so a `?` on an FFI declaration or call no longer does
anything. Codegen generates no error path for it. The compiler now
rejects that `?` in the syntax pass with a message explaining the
change, instead of accepting one that does nothing. The verifier's
`check_partial_ffi_call`, which matched declaration partiality against
call-site partiality, is dead and removed too. Dropping the `?` from
the grammar so it becomes a plain syntax error is left for a
follow-up.
- C runtime code that called `pony_error()` on failure needs another
way to report it. The five `PONY_API` socket functions
(`pony_os_writev`, `pony_os_send`, `pony_os_recv`, `pony_os_sendto`,
`pony_os_recvfrom`) now return a three-state result
(`PONY_SOCKET_OK`, `PONY_SOCKET_RETRY`, `PONY_SOCKET_ERROR`) and
write the byte count to a `size_t* count_out` out-parameter. The old
`size_t` return overloaded three meanings onto one value: bytes
moved, would-block, and error. Each channel means one thing now. The
C return type is `uint8_t` rather than a C `enum`, so the FFI return
width is pinned at one byte. `ponyint_formattime` returns `NULL` on a
bad format string, and `PosixDate.format` checks for it.
Two implementation notes. When a non-partial concrete method shares a
vtable index with a partial dispatch method (`Cons.head` and
`List.head`, say), a thin wrapper is generated that calls the concrete
method and wraps its result as `{value, false}`, so the vtable slot
has one return type. And `invoke_target` in `compile_frame_t` becomes
`error_target`, since it is now a branch target for an error-flag
check rather than an invoke unwind destination.
Design: #4997
Closes #4443
Closes #5280
Closes #5325
1b106e6 to
32cfb7c
Compare
|
It occurs to me, when this merges we can probably remove this section: https://www.ponylang.io/use/performance/pony-performance-cheat-sheet/#avoid-error |
yes, i am planning on doing that. |
|
I ran this against tier2 tests and they all passed. |
|
I ran this against tier3 tests and they all passed. Same for the weekly tests. |
|
My expectation is that if #3874 is still reproducible immediately prior to this commit that it won't be after this. |
Pony's
errorkeyword propagated errors by unwinding the stack (libunwind on POSIX, SEH on Windows), which forced an LLVMinvokeat every partial call site and pulled in platform-specific landing pad, personality, and LSDA machinery.Partial functions now return an error flag instead:
{T, i1}for value-returning functions,i1for void-returning class constructors. Callers check the flag and either branch to their handler or propagate.trybecomes an ordinary branch.With Pony-to-Pony propagation no longer riding on stack unwinding, the unwinding infrastructure is dead code and is removed:
pony_error(),pony_try(),ponyint_personality_v0(), LSDA scanning,except_try_catch.ll, and all landing pad / invoke / personality machinery in codegen.Removing
pony_error()has user-facing consequences:erroroutside atrynow callabort(). A bare partial lambda can no longer carry an error across a C frame. Theserialisepackage was the only stdlib user of that pattern and has been removed along with its tests (RFC Debug symbols and LLDB integration #83). The C-level serialise runtime used internally by the compiler is untouched.pony_error()was the only way a partial FFI function could signal failure, so a?on an FFI declaration or call no longer does anything and is now a compile error from the syntax pass. The deadcheck_partial_ffi_callverifier logic is removed. Dropping the?from the grammar entirely is left for a follow-up.pony_error()reports failure through return values now. The fivePONY_APIsocket functions return a three-state result (PONY_SOCKET_OK/RETRY/ERROR) plus asize_t* count_outout-parameter (RFC Check identifier rules are enforced #84).ponyint_formattimereturnsNULLon a bad format string.Closes #4443. Closes #5280. Closes #5325. Design: #4997.