mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Explorer: split Member into dedicated classes (#2421)
Features: * Split `Member` class into 3 dedicated classes covering named, positional, and a new "base class" element Changes: * Rename `Member` to `Element` to better reflect the variants covered * Add `NamedElement`, `PositionalElement`, and `BaseElement` child classes for `Element` * Split `GetMember` into 3 function variants based on available attributes (index, name, nothing). * Add some unit tests to provide coverage of core features Motivation: This changeset splits Member into (currently 2) classes, as we see the need for more Member variants (base class access needed for #2378, possibly unnamed mixins, ...), which in addition to the current ones, also have significantly different attributes. This will allow supporting more Member types in the future cleanly. Alternatives considered: The alternative solution, "one class for positional, named & base class access", would expose unused or unavailable attributes depending on the Member actual type (`index()` only for positional, `name()` only for named, and neither for base class access).
This commit is contained in:
@@ -11,10 +11,11 @@
|
||||
#include "common/check.h"
|
||||
#include "common/error.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/ast/element.h"
|
||||
#include "explorer/common/arena.h"
|
||||
#include "explorer/common/error_builders.h"
|
||||
#include "explorer/interpreter/action.h"
|
||||
#include "explorer/interpreter/field_path.h"
|
||||
#include "explorer/interpreter/element_path.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
@@ -49,39 +50,48 @@ static auto FindClassField(Nonnull<const NominalClassValue*> object,
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static auto GetPositionalMember(Nonnull<const Value*> v,
|
||||
const FieldPath::Component& field,
|
||||
SourceLocation source_loc)
|
||||
static auto GetBaseElement(Nonnull<const NominalClassValue*> class_value,
|
||||
SourceLocation source_loc)
|
||||
-> ErrorOr<Nonnull<const Value*>> {
|
||||
switch (v->kind()) {
|
||||
case Value::Kind::TupleValue: {
|
||||
const auto& tuple = cast<TupleValue>(*v);
|
||||
const auto index = field.member().index();
|
||||
if (index < 0 || index >= static_cast<int>(tuple.elements().size())) {
|
||||
return ProgramError(source_loc)
|
||||
<< "index " << index << " out of range for " << *v;
|
||||
}
|
||||
return tuple.elements()[index];
|
||||
}
|
||||
default:
|
||||
return ProgramError(source_loc)
|
||||
<< "Invalid positional argument for value " << *v;
|
||||
const auto base = cast<NominalClassValue>(class_value)->base();
|
||||
if (!base.has_value()) {
|
||||
return ProgramError(source_loc)
|
||||
<< "Non-existent base class for " << *class_value;
|
||||
}
|
||||
return base.value();
|
||||
}
|
||||
|
||||
static auto GetNamedMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
const FieldPath::Component& field,
|
||||
SourceLocation source_loc,
|
||||
Nonnull<const Value*> me_value)
|
||||
static auto GetPositionalElement(Nonnull<const TupleValue*> tuple,
|
||||
const ElementPath::Component& path_comp,
|
||||
SourceLocation source_loc)
|
||||
-> ErrorOr<Nonnull<const Value*>> {
|
||||
const auto f = field.member().name();
|
||||
CARBON_CHECK(path_comp.element()->kind() == ElementKind::PositionalElement)
|
||||
<< "Invalid non-tuple member";
|
||||
const auto* tuple_element = cast<PositionalElement>(path_comp.element());
|
||||
const size_t index = tuple_element->index();
|
||||
if (index < 0 || index >= tuple->elements().size()) {
|
||||
return ProgramError(source_loc)
|
||||
<< "index " << index << " out of range for " << *tuple;
|
||||
}
|
||||
return tuple->elements()[index];
|
||||
}
|
||||
|
||||
static auto GetNamedElement(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
const ElementPath::Component& field,
|
||||
SourceLocation source_loc,
|
||||
Nonnull<const Value*> me_value)
|
||||
-> ErrorOr<Nonnull<const Value*>> {
|
||||
CARBON_CHECK(field.element()->kind() == ElementKind::NamedElement)
|
||||
<< "Invalid element, expecting NamedElement";
|
||||
const auto* member = cast<NamedElement>(field.element());
|
||||
const auto f = member->name();
|
||||
if (field.witness().has_value()) {
|
||||
const auto* witness = cast<Witness>(*field.witness());
|
||||
|
||||
// Associated constants.
|
||||
if (const auto* assoc_const =
|
||||
dyn_cast_or_null<AssociatedConstantDeclaration>(
|
||||
field.member().declaration().value_or(nullptr))) {
|
||||
member->declaration().value_or(nullptr))) {
|
||||
CARBON_CHECK(field.interface()) << "have witness but no interface";
|
||||
// TODO: Use witness to find the value of the constant.
|
||||
return arena->New<AssociatedConstant>(v, *field.interface(), assoc_const,
|
||||
@@ -169,35 +179,50 @@ static auto GetNamedMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
&class_type.bindings());
|
||||
}
|
||||
default:
|
||||
CARBON_FATAL() << "field access not allowed for value " << *v;
|
||||
CARBON_FATAL() << "named element access not supported for value " << *v;
|
||||
}
|
||||
}
|
||||
|
||||
static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
const FieldPath::Component& field,
|
||||
SourceLocation source_loc, Nonnull<const Value*> me_value)
|
||||
static auto GetElement(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
const ElementPath::Component& path_comp,
|
||||
SourceLocation source_loc,
|
||||
Nonnull<const Value*> me_value)
|
||||
-> ErrorOr<Nonnull<const Value*>> {
|
||||
return field.member().HasPosition()
|
||||
? GetPositionalMember(v, field, source_loc)
|
||||
: GetNamedMember(arena, v, field, source_loc, me_value);
|
||||
switch (path_comp.element()->kind()) {
|
||||
case ElementKind::NamedElement:
|
||||
return GetNamedElement(arena, v, path_comp, source_loc, me_value);
|
||||
case ElementKind::PositionalElement: {
|
||||
if (const auto* tuple = dyn_cast<TupleValue>(v)) {
|
||||
return GetPositionalElement(tuple, path_comp, source_loc);
|
||||
} else {
|
||||
CARBON_FATAL() << "Invalid value for positional element";
|
||||
}
|
||||
}
|
||||
case ElementKind::BaseElement:
|
||||
if (const auto* class_value = dyn_cast<NominalClassValue>(v)) {
|
||||
return GetBaseElement(class_value, source_loc);
|
||||
} else {
|
||||
CARBON_FATAL() << "Invalid value for base element";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Value::GetMember(Nonnull<Arena*> arena, const FieldPath& path,
|
||||
SourceLocation source_loc,
|
||||
Nonnull<const Value*> me_value) const
|
||||
auto Value::GetElement(Nonnull<Arena*> arena, const ElementPath& path,
|
||||
SourceLocation source_loc,
|
||||
Nonnull<const Value*> me_value) const
|
||||
-> ErrorOr<Nonnull<const Value*>> {
|
||||
Nonnull<const Value*> value(this);
|
||||
for (const FieldPath::Component& field : path.components_) {
|
||||
for (const ElementPath::Component& field : path.components_) {
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
value, Carbon::GetMember(arena, value, field, source_loc, me_value));
|
||||
value, Carbon::GetElement(arena, value, field, source_loc, me_value));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static auto SetFieldImpl(
|
||||
Nonnull<Arena*> arena, Nonnull<const Value*> value,
|
||||
std::vector<FieldPath::Component>::const_iterator path_begin,
|
||||
std::vector<FieldPath::Component>::const_iterator path_end,
|
||||
std::vector<ElementPath::Component>::const_iterator path_begin,
|
||||
std::vector<ElementPath::Component>::const_iterator path_end,
|
||||
Nonnull<const Value*> field_value, SourceLocation source_loc)
|
||||
-> ErrorOr<Nonnull<const Value*>> {
|
||||
if (path_begin == path_end) {
|
||||
@@ -241,10 +266,14 @@ static auto SetFieldImpl(
|
||||
}
|
||||
case Value::Kind::TupleType:
|
||||
case Value::Kind::TupleValue: {
|
||||
CARBON_CHECK((*path_begin).element()->kind() ==
|
||||
ElementKind::PositionalElement)
|
||||
<< "Invalid non-positional member for tuple";
|
||||
std::vector<Nonnull<const Value*>> elements =
|
||||
cast<TupleValueBase>(*value).elements();
|
||||
const auto index = (*path_begin).member().index();
|
||||
if (index < 0 || index >= static_cast<int>(elements.size())) {
|
||||
const size_t index =
|
||||
cast<PositionalElement>((*path_begin).element())->index();
|
||||
if (index < 0 || index >= elements.size()) {
|
||||
return ProgramError(source_loc)
|
||||
<< "index " << index << " out of range in " << *value;
|
||||
}
|
||||
@@ -262,7 +291,7 @@ static auto SetFieldImpl(
|
||||
}
|
||||
}
|
||||
|
||||
auto Value::SetField(Nonnull<Arena*> arena, const FieldPath& path,
|
||||
auto Value::SetField(Nonnull<Arena*> arena, const ElementPath& path,
|
||||
Nonnull<const Value*> field_value,
|
||||
SourceLocation source_loc) const
|
||||
-> ErrorOr<Nonnull<const Value*>> {
|
||||
|
||||
Reference in New Issue
Block a user