mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Explorer: support virtual class methods (#2462)
Features: * Add `virtual` virtual override keyword for functions * Support `virtual` class methods using dynamic dispatch Changes: * Add `vtable` in `NominalClassType`, * Add `NominalClassValue**` in class values pointing to descendant-most class * Resolve virtual methods during member lookup Limitations: * Does not include yet `impl`, `abstract` virtual override keywords, or the complete logic for virtual function declaration Depends on #2460 Relates to #1881 Relates to #2493
This commit is contained in:
@@ -38,6 +38,19 @@ auto StructValue::FindField(std::string_view name) const
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
NominalClassValue::NominalClassValue(
|
||||
Nonnull<const Value*> type, Nonnull<const Value*> inits,
|
||||
std::optional<Nonnull<const NominalClassValue*>> base,
|
||||
Nonnull<const NominalClassValue** const> class_value_ptr)
|
||||
: Value(Kind::NominalClassValue),
|
||||
type_(type),
|
||||
inits_(inits),
|
||||
base_(base),
|
||||
class_value_ptr_(class_value_ptr) {
|
||||
// Update ancestors's class value to point to latest child.
|
||||
*class_value_ptr_ = this;
|
||||
}
|
||||
|
||||
static auto FindClassField(Nonnull<const NominalClassValue*> object,
|
||||
std::string_view name)
|
||||
-> std::optional<Nonnull<const Value*>> {
|
||||
@@ -148,8 +161,30 @@ static auto GetNamedElement(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
} else if ((*func)->declaration().is_method()) {
|
||||
// Found a method. Turn it into a bound method.
|
||||
const auto& m = cast<FunctionValue>(**func);
|
||||
return arena->New<BoundMethodValue>(&m.declaration(), me_value,
|
||||
&class_type.bindings());
|
||||
if (!m.declaration().is_virtual()) {
|
||||
return arena->New<BoundMethodValue>(&m.declaration(), me_value,
|
||||
&class_type.bindings());
|
||||
}
|
||||
// Method is virtual, get child-most class value and perform vtable
|
||||
// lookup.
|
||||
const auto& last_child_value = **object.class_value_ptr();
|
||||
const auto& last_child_type =
|
||||
cast<NominalClassType>(last_child_value.type());
|
||||
const auto res = last_child_type.vtable().find(f);
|
||||
CARBON_CHECK(res != last_child_type.vtable().end());
|
||||
const auto [virtual_method, level] = res->second;
|
||||
const auto level_diff = last_child_type.hierarchy_level() - level;
|
||||
const auto* m_class_value = &last_child_value;
|
||||
// Get class value matching the virtual method, and turn it into a
|
||||
// bound method.
|
||||
for (int i = 0; i < level_diff; ++i) {
|
||||
CARBON_CHECK(m_class_value->base())
|
||||
<< "Error trying to access function class value";
|
||||
m_class_value = *m_class_value->base();
|
||||
}
|
||||
return arena->New<BoundMethodValue>(
|
||||
cast<FunctionDeclaration>(virtual_method), m_class_value,
|
||||
&class_type.bindings());
|
||||
} else {
|
||||
// Found a class function
|
||||
// TODO: This should not be reachable.
|
||||
@@ -255,15 +290,15 @@ static auto SetFieldImpl(
|
||||
if (auto inits = SetFieldImpl(arena, &object.inits(), path_begin,
|
||||
path_end, field_value, source_loc);
|
||||
inits.ok()) {
|
||||
return arena->New<NominalClassValue>(&object.type(), *inits,
|
||||
object.base());
|
||||
return arena->New<NominalClassValue>(
|
||||
&object.type(), *inits, object.base(), object.class_value_ptr());
|
||||
} else if (object.base().has_value()) {
|
||||
auto new_base = SetFieldImpl(arena, object.base().value(), path_begin,
|
||||
path_end, field_value, source_loc);
|
||||
if (new_base.ok()) {
|
||||
return arena->New<NominalClassValue>(
|
||||
&object.type(), &object.inits(),
|
||||
cast<NominalClassValue>(*new_base));
|
||||
cast<NominalClassValue>(*new_base), object.class_value_ptr());
|
||||
}
|
||||
}
|
||||
// Failed to match, show full object content
|
||||
|
||||
Reference in New Issue
Block a user