ChaiScript/unittests/class_inheritance.chai
leftibot f59eff9b2f
Fix #201: Suggestion: class Inheritance (#641)
* Fix #201: Add class inheritance support with Derived : Base syntax

Classes can now inherit methods and attributes from a base class using
C++-style syntax: `class Derived : Base { ... }`. Base class methods and
attributes are automatically available on derived objects. Derived classes
can override base methods by defining a method with the same name.
Inheritance relationships are tracked to support proper type matching
in the dispatch system.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address review: use implicit derived-to-base matching instead of copying base class functions

Instead of copying all base class methods/attributes into derived classes,
make the type matching system recognize inheritance relationships. Base class
methods now naturally match derived objects through dynamic_object_typename_match,
and dispatch ordering ensures derived overrides are preferred over base methods.

This is simpler (net -25 lines) and avoids duplicating function registrations.

Requested by @lefticus in PR #641 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add tests for passing derived objects to functions expecting Base

Tests cover: free functions calling base methods on derived objects,
polymorphic dispatch through containers, base attribute access on
derived objects, and multi-level inheritance (GrandChild : Derived : Base).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add typed parameter tests for class inheritance

Use typed function signatures (e.g., `def call_do_something(Base obj)`)
instead of untyped parameters to test that derived objects are accepted
by functions expecting a base type, with correct polymorphic dispatch.

Requested by @lefticus in PR #641 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: leftibot <leftibot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 19:12:06 -06:00

138 lines
2.6 KiB
ChaiScript

class Base
{
attr x
def Base()
{
this.x = 10
}
def do_something()
{
return this.x * 2
}
}
class Derived : Base
{
attr y
def Derived()
{
this.x = 10
this.y = 20
}
def do_other()
{
return this.y * 3
}
}
// Test basic inheritance - derived can call base methods
auto d = Derived()
assert_equal(10, d.x)
assert_equal(20, d.y)
assert_equal(20, d.do_something())
assert_equal(60, d.do_other())
// Test base class still works independently
auto b = Base()
assert_equal(10, b.x)
assert_equal(20, b.do_something())
// Test method override
class Derived2 : Base
{
def Derived2()
{
this.x = 5
}
def do_something()
{
return this.x * 100
}
}
auto d2 = Derived2()
assert_equal(500, d2.do_something())
// Test passing a derived object to an untyped free function
def call_do_something_untyped(obj) {
return obj.do_something()
}
auto d3 = Derived()
assert_equal(20, call_do_something_untyped(d3))
assert_equal(20, call_do_something_untyped(Base()))
// Test typed functions: parameter declared as Base, accepts derived objects
def call_do_something(Base obj) {
return obj.do_something()
}
assert_equal(20, call_do_something(Base()))
assert_equal(20, call_do_something(d3))
// Test typed function accessing base attributes on a derived object
def get_x(Base obj) {
return obj.x
}
assert_equal(10, get_x(d3))
assert_equal(10, get_x(Base()))
// Test polymorphic dispatch through typed function: derived override is called
auto d4 = Derived2()
assert_equal(500, call_do_something(d4))
// Test mixing base and derived in a container, calling base methods
var objects = [Base(), Derived(), Derived2()]
assert_equal(20, objects[0].do_something())
assert_equal(20, objects[1].do_something())
assert_equal(500, objects[2].do_something())
// Test that derived objects still report correct type
auto d5 = Derived()
assert_true(d5.is_type("Derived"))
// Test multi-level inheritance
class GrandChild : Derived
{
attr z
def GrandChild()
{
this.x = 1
this.y = 2
this.z = 3
}
def do_grandchild()
{
return this.z * 4
}
}
auto gc = GrandChild()
assert_equal(1, gc.x)
assert_equal(2, gc.y)
assert_equal(3, gc.z)
assert_equal(2, gc.do_something()) // Base method
assert_equal(6, gc.do_other()) // Derived method
assert_equal(12, gc.do_grandchild()) // Own method
// Test passing grandchild to typed Base function (multi-level inheritance)
assert_equal(2, call_do_something(gc))
assert_equal(1, get_x(gc))
// Test typed function expecting mid-level type
def call_do_other(Derived obj) {
return obj.do_other()
}
assert_equal(6, call_do_other(gc))
assert_equal(60, call_do_other(Derived()))