Skip to content

Let Compile fail with its own exception rather than someone else's (#894) - #895

Merged
Rafael-SOWNet merged 3 commits into
masterfrom
fix/compile-exceptions
Aug 11, 2026
Merged

Let Compile fail with its own exception rather than someone else's (#894)#895
Rafael-SOWNet merged 3 commits into
masterfrom
fix/compile-exceptions

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Collaborator

Closes #894. Compile reported the impossible with two exceptions that were not the one documented
for it.

What changed

Nothing about which nodes compile, or about the compiled output. Only what is thrown when
compilation is impossible.

input, compiled as <double, double> was is
floor(x), ceil(x), round(x), phi(x), gamma(x), x!, (x+1)!/x! AngouriBugException: An unary node seems to be not added UncompilableNodeException
not x, x and 2, x or 2, x xor 2, x implies 2, x -> x + 1, (x -> x + 1) applied to 2 InvalidOperationException from System.Linq.Expressions UncompilableNodeException
x provided 2 ArgumentException: Argument must be boolean UncompilableNodeException

Why each is wrong as it stands

AngouriBugException means "an internal error occurred, report it". None of the first row is one:
the converter has no case for the node, which is a gap in coverage. So a caller who compiled
floor(x) was asked to file a bug report — and floor, ceil and round are nodes 2.0 itself
added
(#809) and never taught the compiler, so that is a 2.0 feature asking to be reported as a
defect. #872 moved twelve other known gaps off AngouriBugException for exactly this reason.

Linq's exceptions are not under AngouriMathBaseException.
Docs/Usage/Exceptions.md tells a
caller that catching AngouriMathBaseException catches everything the library throws, and these
escaped that handler entirely. The mismatch itself is real — a boolean node has no double-valued
compiled form — so the fix is to report it as what it is, not to make it compile.

What this deliberately does not do

It does not teach the compiler floor, ceil, round, phi, gamma or the factorial. That is
worth doing and is separate work; conflating the two would mean shipping a coverage change under a
correctness fix. This change makes the failure honest, and the issue says so.

Two small choices worth stating

The unary converter's message now names the node and points at CompilationProtocol, which is the
documented way to add a case: "There is no compiled form for Floorf. Define a CompilationProtocol which overrides ConvertUnaryNode to add one."

The Linq wrap is at the boundary in IntoLinqCompiler.Compile rather than around each converter, and
it is narrow — InvalidOperationException, ArgumentException, NotSupportedException, and
explicitly not anything already deriving from AngouriMathBaseException, so a library exception
raised deeper down still reaches the caller as itself.

Measured on this branch, .NET 10

C# tests 6284 passed, 0 failed, 14 skipped
crashcheck, compile operation 118 cases, 0 unexpected, down from 16
crashcheck, everything 1652 cases, 0 crashed, 0 did not finish, 0 unexpected, 103 declined

14 new regression cases in UserInvalidExceptions, one per expression in the two families above.

How it was found

work/crashcheck, added for P2 of this batch of work: every case runs in a child process of its
own
, because a stack overflow cannot be caught — it kills the process, and a test run that dies
reports nothing about what it had not reached. propcheck on stock master died that way in
IntegrateByPartsPolynomial, so the suite was green and the library was not.

The expressions come from the Entity node types by reflection, so a node added later is covered
without anyone remembering the file — which is what would have caught floor and ceil when they
landed. The harness also proves itself before reporting: --selftest spawns a child whose only job is
to overflow its own stack, and fails loudly if the parent does not report a crash, because "0 crashes"
from a detector that cannot see one is worth nothing.

)

Two ways compilation reported the impossible, neither of them the exception written
down for it.

A node the Linq converter has no case for threw AngouriBugException, which asks the
caller to report a bug -- for a gap in coverage the library already knows about. So
compiling floor(x), ceil(x), round(x), phi(x), gamma(x) or x! told the user to file an
issue, and floor, ceil and round are nodes 2.0 itself added and never taught the
compiler. #872 moved twelve other known gaps off AngouriBugException for this reason;
these are the same thing.

Linq.Expression's own refusals reached the caller unwrapped: an InvalidOperationException
for `not x` compiled as a double, an ArgumentException for a Providedf whose condition is
not boolean. The mismatch is real -- a boolean node has no double-valued compiled form --
but Docs/Usage/Exceptions.md tells a caller to write catch (AngouriMathBaseException)
around a library call, and those are not under it, so they escaped the handler the
documentation asks for.

Both now throw UncompilableNodeException. The node set and the compiled output are
unchanged; only the failure is. Teaching the compiler floor, ceil and round is worth
doing and is not this.

Found by work/crashcheck: on 1652 cases it reports 0 crashes and 0 hangs, and these 16
were every remaining finding. Measured after the change, the same 118 compile cases
produce none.

6284 tests pass, 14 new ones covering both families.
Both changelog sections were inserted at the same anchor, so both are kept: the
arccotan correction from #888 continues the inverse-trigonometric entry above it,
and the Compile entry follows it.
#893's logarithm section went into BREAKING-CHANGES.md at the same anchor this branch
uses, so both are kept -- as with #888 before it. Three of the entries in this file now
sit next to each other because three changes to the same table landed in the same
afternoon, which is a good problem to have.
@Rafael-SOWNet
Rafael-SOWNet merged commit d8bfcd5 into master Aug 11, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compile throws AngouriBugException for nodes it simply has no case for, and leaks Linq's exceptions for type mismatches

1 participant