Performance Innovations
Luwu objects take less memory than tables, and the compiler reaches their fields and methods without the lookups a table needs. Most of these optimizations depend on what the compiler can prove about an object's class.
Inline fields
An object stores its fields inline in an order its class fixes up front instead of in a hash table that grows as keys are added. That makes objects smaller too: an object with two number fields takes 88 bytes and a table holding the same fields takes 128.
Constant-offset field access
Because the order is fixed, the compiler turns a field access into a constant offset. Inside a method, self.health compiles to a GETOBJECTMEMBER instruction (SETOBJECTMEMBER for writes) that goes straight to the field's slot without a key lookup or a cache check. With native code generation, a field read takes about 0.9 nanoseconds against 2.0 for a table, and a write about 0.3 against 4.5. When code reads the same field twice in a row, constant propagation reuses the first read.
Proving what self is
The compiler only takes these shortcuts when it can prove what self is. Every method starts with a self check that raises an error when it's called on the wrong kind of value. Past that check, self is proven to be an object of the method's class when the method never reassigns it. Type annotations deliberately don't count as proof: a wrong one would read the wrong memory instead of failing with an error.
Method inlining
At optimization level 2 (--!optimize 2), Luwu also inlines method calls. The compiler copies the body of a small method into its caller and removes the call entirely. This is where most of the speed of classes comes from. Inlining follows the same rule of proof: a call inlines when the compiler can prove the receiver's class. That covers self:method() inside a class, an object the same code just constructed, and a value checked with class.isinstance in an if or an assert. An inlined body still runs its self check and fails loudly on the wrong kind of object. Methods only inline within a module, and hot code runs fastest in the same module as the classes it uses.
Trusting type annotations
Proving every receiver gets tedious once objects hold other objects. The --!trust directive lets a file trust its type annotations instead: a parameter, local or field annotated with a class is then enough to pick the method to inline. Trust never lets wrong code run quietly: the inlined self check turns a lying annotation into a runtime error. --!trust is still experimental and isn't turned on by default yet.
Construction and type tests
Building and testing objects is cheap too. Constructing an object compiles to a single NEWOBJECT instruction with no temporary table and no function call. class.isinstance compiles to one fused test-and-branch instruction that native code drops entirely when the class is already known.