You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 1bfbb2e
Browse filesBrowse the repository at this point in the historyBrowse files
A null class object, null or undefined given to an interface is a null interface whose methods throw (#665)
* A class object or null given to an interface or null becomes an interface
`let opt: I | null = flag ? mk(5) : null` (`C | null` to `I | null`)
was a plain ts.Cast: the class pointer was read as an interface, which
the lowering could not make ("invalid cast from '!llvm.ptr' to
'!llvm.struct<(ptr, ptr, ptr)>'"), under every model. The program
crashed, or called through a wrong vtable into code that allocated
without end.
castToNullableUnionType now converts the class member the way
`let a: I = mk()` does (the vtable lookup is shared,
mlirGenClassInterfaceVTable), and null stays null. Only the vtable is
chosen by a test for null - it may be read through the object - and a
null object is seen through a null vtable, which is what an interface
compares to null by. The ts.NewInterface is built over the object
outside the test, so it stays a view of the union for the ownership
passes. Two of them now look through the class read out of the union:
a global's initializer (`let g: I | null = maybe()` was born pointing
at freed memory under rc and own) and a const's view
(`const o: I | null = c` was folded with no count, and `c = null`
freed what it read).
Tests read a field before any method call or allocation, so a
regression fails an assert instead of running away.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* A null class object given to an interface is a null interface
`C | null` is stored as `C`, so `C | null` to `I | null` is the
class-to-interface cast of `C` to `I`: the union path now only reads
the class out of the union and makes that cast. The null handling moves
into the class-to-interface cast itself, where it was missing for a
plain `C` too (non-strict null): a null class object was paired with
its class's vtable, so the interface was not `=== null`, and for a
class naming the interface, whose vtable is read through the object,
the cast read through the null pointer and crashed. Only the vtable is
chosen by the test for null; the interface is still built over the
object outside it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* -mm=own: an interface over a class object is made with its class's vtable
A class object's vtable for an interface is now chosen by a test for
null (castToInterfaceSpecialCases), so ts.NewInterface's vtable is a
ts.If's result or, by the time the signature pass runs, a block argument
its branches pass it to. The pass took that for a vtable it could not
trace and opened the interface ("reached through an interface, whose
calls are not given its facts"): 17 own tests failed. It now follows
each branch's vtable, skipping a null object's, and counts a class
vtable passed along a branch as one that only makes values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* A null interface is seen through a vtable whose methods throw
A null interface - `I | null` holding null, a null class object seen
through `I`, `null` or `undefined` given to `I` - is an interface over
a null `this`, seen through the interface's null vtable
(mlirGenNullInterfaceVTable, `<I>.null..vtbl`, one per interface and
module). Each of its methods is a stub that throws, as a call of an
abstract method would: `new Error("method is abstract")`, or the
message itself without the default library, which has no `Error`. A
field's slot is offset 0, so reading one reads through the null
`this`, as a null class object's field does; an optional member is
absent (-1); `.instanceOf` answers false.
A null class object was seen through a null vtable, a call of one of
its methods jumped to address 0, and `null` cast to an interface had
no vtable at all.
An interface is now tested for null, and compared, by its `this`
(UnaryBinLogicalOrHelper, and CastLogicHelper's interface-to-boolean),
not its vtable: a null interface has one, and two interfaces over the
same object are the same value (they were compared by vtable, so two
objects of one class were equal).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* The ownership passes see a null interface as null, not as its vtable
The null vtable and its throwing stubs are compiler glue the ownership
passes must not read as a way an interface is implemented:
- A null interface stays a cast of null in the IR the passes see -
`null`/`undefined` given to an interface, and a null class object's
vtable choice - and the cast names the null vtable
(NULL_INTERFACE_VTABLE_ATTR_NAME), which the lowering puts in
(CastLogicHelper::nullInterfaceVTable). An `AddressOf` of it in a
function opened the interface and lost the `reduce` callback's facts
in the own ray tracer.
- The signature pass leaves the null vtable out of the interface
vtables, and a stub's symbol there does not open it; the rc pass
leaves both out of what a call through an interface or a value may
reach - a stub returns nothing, so it does not switch off the
whole-module "every heap result is retained" answer either.
- A stub is a function of its own: its `throw` no longer walks out into
the scopes of the function whose cast made it, and
mlirGenFunctionBody no longer gives a function returning nothing the
return variable of the function being generated around it.
- A discovery run makes no null vtable; one it left without a body is
made again.
And `throw` no longer turns off -mm=own's "a C function of numbers
destroys nothing" (spec 31): the exception tables name the C++
runtime's type_info vtable, a foreign global, which counted as a way
to hand C a function. It is marked (EH_TYPE_INFO_ATTR_NAME) and left
out. Any module with a `throw` hit it (`sqrt` then "may release").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
0 commit comments