Skip to content

Raise the exception a program named - #100

Merged
tamnd merged 1 commit into
mainfrom
raise
Aug 29, 2026
Merged

Raise the exception a program named#100
tamnd merged 1 commit into
mainfrom
raise

Conversation

@tamnd

@tamnd tamnd commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Part of #7, the first of two changes for exceptions. This one is the objects and the raise that makes them; try is the next one.

What works now

Every builtin exception class is a value bound in builtins, all 66 of them, verified against a live BaseException.__subclasses__() walk on CPython 3.14.7. They can be named, called, held in a variable, printed and raised:

e = ValueError('boom')
print(e)          # boom
print([e])        # [ValueError('boom')]
raise e from KeyError('missing')

raise works in every form the grammar has: a class, an instance, an instance held in a variable, and any of those followed by from. An uncaught one prints the cause chain oldest first with CPython's wording in between, then the last line of the traceback.

The hierarchy is one table

A hierarchy! macro declares Kind, Kind::name, Kind::base and Kind::ALL from a single indented table in kohebi-core::error. A name and the class it derives from come out of the same row, so they cannot drift apart, and adding a class is one line. Kind::derives_from walks it, which is the question except will ask in the next PR and the one isinstance asks after that.

The raised object comes along

Error now carries the object that was raised, boxed. except ValueError as e has to hand back the very instance that raise e threw, and there is no way back to it from a kind and a message. It is a Box because Error sits inside every Result the runtime returns: the case being paid for is a program raising something, and the case that must not get slower is dividing by zero. Option<Box<Object>> is 8 bytes, so nothing on the hot path widened.

raise e from e is a ring, so the chain walk seeds its seen list with the exception being printed, which is what makes it print once rather than twice. That is what CPython does too.

SystemExit

The one class that asks for something rather than reporting something. It sets the process status and prints nothing, and a status is a byte, so 256 exits 0 and -1 exits 255. All 13 argument shapes were diffed against CPython on both output and exit code.

Two documented differences

Attributes are missing, so e.args and e.__cause__ are not readable from Python yet, and the classes that take a specific signature in CPython (OSError, UnicodeDecodeError) take anything positionally here. Both wait on attribute access.

raise int still says NameError rather than TypeError because int is not a builtin yet, and raise ValueError(*args) still refuses because starred call arguments do not lower. Both are pre-existing gaps with their own work.

Checked

31 raise programs diffed against CPython 3.14.7, byte-identical apart from the two gaps above. cargo fmt, the whole workspace test suite, clippy with --all-targets --all-features and rustdoc with -D warnings are all clean locally.

Every builtin exception class is a value now, all 66 of them, and
`raise` works in every form: a class, an instance, an instance held in
a variable, and any of those after a `from`. The classes are one
indented table in `kohebi-core::error`, so a name and the class it
derives from come out of the same row and cannot drift apart, and the
hierarchy is there for the `except` that will read it.

An `Error` carries the object that was raised as well as its name and
its message, boxed, because the case it pays for is a program raising
something and the case it must not slow down is division by zero. That
is what will make `except ValueError as e` hand back the very instance
a `raise e` threw.

`SystemExit` is the one class that asks for something rather than
reporting something, so it sets the process status and says nothing,
and a status is a byte the way the operating system has it.

`try` is still missing, so an exception leaves the program rather than
being caught by it.
@tamnd
tamnd merged commit 56c23be into main Aug 29, 2026
9 checks passed
@tamnd
tamnd deleted the raise branch August 29, 2026 12:17
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.

1 participant