mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:50:14 +01:00
Explorer: support index-based fields for FieldPath and Members (#2417)
Features * Support addressing positional members (i.e. tuple fields) using an index. * Addresses a couple of `TODO`s relative to positional members Changes: * Add a new `IndexedValue` mirroring `NamedValue` * Adds `FieldPath::index() -> int` * Adds a `Member` variant for `IndexedValue`
This commit is contained in:
+40
-2
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "explorer/ast/member.h"
|
||||
|
||||
#include "common/check.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
|
||||
namespace Carbon {
|
||||
@@ -14,19 +15,46 @@ Member::Member(Nonnull<const Declaration*> declaration)
|
||||
Member::Member(Nonnull<const NamedValue*> struct_member)
|
||||
: member_(struct_member) {}
|
||||
|
||||
Member::Member(Nonnull<const IndexedValue*> tuple_member)
|
||||
: member_(tuple_member) {}
|
||||
|
||||
auto Member::IsNamed(std::string_view other_name) const -> bool {
|
||||
return HasName() && name() == other_name;
|
||||
}
|
||||
|
||||
auto Member::name() const -> std::string_view {
|
||||
CARBON_CHECK(HasName()) << "Unnamed member does not have a name()";
|
||||
if (const auto* decl = member_.dyn_cast<const Declaration*>()) {
|
||||
return GetName(*decl).value();
|
||||
} else if (const auto* named_valued = member_.dyn_cast<const NamedValue*>()) {
|
||||
return named_valued->name;
|
||||
} else {
|
||||
return member_.get<const NamedValue*>()->name;
|
||||
CARBON_FATAL() << "Unreachable";
|
||||
}
|
||||
}
|
||||
|
||||
auto Member::HasPosition() const -> bool {
|
||||
return member_.dyn_cast<const IndexedValue*>() != nullptr;
|
||||
}
|
||||
|
||||
auto Member::HasName() const -> bool {
|
||||
// Both are currently mutually exclusive
|
||||
return !HasPosition();
|
||||
}
|
||||
|
||||
auto Member::index() const -> int {
|
||||
CARBON_CHECK(HasPosition())
|
||||
<< "Non-positional member does not have an index()";
|
||||
return member_.dyn_cast<const IndexedValue*>()->index;
|
||||
}
|
||||
|
||||
auto Member::type() const -> const Value& {
|
||||
if (const auto* decl = member_.dyn_cast<const Declaration*>()) {
|
||||
return decl->static_type();
|
||||
} else if (const auto* named_valued = member_.dyn_cast<const NamedValue*>()) {
|
||||
return *named_valued->value;
|
||||
} else {
|
||||
return *member_.get<const NamedValue*>()->value;
|
||||
return *member_.get<const IndexedValue*>()->value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,4 +65,14 @@ auto Member::declaration() const -> std::optional<Nonnull<const Declaration*>> {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Member::Print(llvm::raw_ostream& out) const {
|
||||
if (HasName()) {
|
||||
out << name();
|
||||
} else if (const auto* value = member_.dyn_cast<const IndexedValue*>()) {
|
||||
out << "element #" << member_.get<const IndexedValue*>()->index;
|
||||
} else {
|
||||
CARBON_FATAL() << "Unhandled member type";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
+27
-2
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "explorer/common/nonnull.h"
|
||||
#include "llvm/ADT/PointerUnion.h"
|
||||
@@ -25,6 +26,16 @@ struct NamedValue {
|
||||
Nonnull<const Value*> value;
|
||||
};
|
||||
|
||||
// A IndexedValue represents a value identified by an index, such as a tuple
|
||||
// field.
|
||||
struct IndexedValue {
|
||||
// The field index.
|
||||
int index;
|
||||
|
||||
// The field's value.
|
||||
Nonnull<const Value*> value;
|
||||
};
|
||||
|
||||
// A member of a type.
|
||||
//
|
||||
// This is either a declared member of a class, interface, or similar, or a
|
||||
@@ -33,16 +44,30 @@ class Member {
|
||||
public:
|
||||
explicit Member(Nonnull<const Declaration*> declaration);
|
||||
explicit Member(Nonnull<const NamedValue*> struct_member);
|
||||
explicit Member(Nonnull<const IndexedValue*> tuple_member);
|
||||
|
||||
// The name of the member.
|
||||
// Return whether the member's name matches `name`.
|
||||
auto IsNamed(std::string_view name) const -> bool;
|
||||
// Prints the Member
|
||||
void Print(llvm::raw_ostream& out) const;
|
||||
|
||||
// Return whether the member is positional, i.e. has an index.
|
||||
auto HasPosition() const -> bool;
|
||||
// Return whether the member is named, i.e. has a name.
|
||||
auto HasName() const -> bool;
|
||||
|
||||
// The name of the member. Requires *this to represent a named member.
|
||||
auto name() const -> std::string_view;
|
||||
// The index of the member. Requires *this to represent a positional member.
|
||||
auto index() const -> int;
|
||||
// The declared type of the member, which might include type variables.
|
||||
auto type() const -> const Value&;
|
||||
// A declaration of the member, if any exists.
|
||||
auto declaration() const -> std::optional<Nonnull<const Declaration*>>;
|
||||
|
||||
private:
|
||||
llvm::PointerUnion<Nonnull<const Declaration*>, Nonnull<const NamedValue*>>
|
||||
llvm::PointerUnion<Nonnull<const Declaration*>, Nonnull<const NamedValue*>,
|
||||
Nonnull<const IndexedValue*>>
|
||||
member_;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#ifndef CARBON_EXPLORER_INTERPRETER_FIELD_PATH_H_
|
||||
#define CARBON_EXPLORER_INTERPRETER_FIELD_PATH_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "common/ostream.h"
|
||||
@@ -48,7 +50,9 @@ class FieldPath {
|
||||
|
||||
auto member() const -> Member { return member_; }
|
||||
|
||||
auto name() const -> std::string_view { return member_.name(); }
|
||||
auto IsNamed(std::string_view name) const -> bool {
|
||||
return member_.IsNamed(name);
|
||||
}
|
||||
|
||||
auto interface() const -> std::optional<Nonnull<const InterfaceType*>> {
|
||||
return interface_;
|
||||
@@ -58,7 +62,7 @@ class FieldPath {
|
||||
return witness_;
|
||||
}
|
||||
|
||||
void Print(llvm::raw_ostream& out) const { out << name(); }
|
||||
void Print(llvm::raw_ostream& out) const { return member_.Print(out); }
|
||||
|
||||
private:
|
||||
Member member_;
|
||||
|
||||
@@ -463,14 +463,10 @@ auto Interpreter::StepLvalue() -> ErrorOr<Success> {
|
||||
// { v :: [][i] :: C, E, F} :: S, H}
|
||||
// -> { { &v[i] :: C, E, F} :: S, H }
|
||||
Address object = cast<LValue>(*act.results()[0]).address();
|
||||
// TODO: Add support to `Member` for naming tuple fields rather than
|
||||
// pretending we have struct fields with numerical names.
|
||||
std::string f =
|
||||
std::to_string(cast<IntValue>(*act.results()[1]).value());
|
||||
auto* tuple_field_as_struct_field =
|
||||
arena_->New<NamedValue>(NamedValue{f, &exp.static_type()});
|
||||
Address field =
|
||||
object.SubobjectAddress(Member(tuple_field_as_struct_field));
|
||||
const auto index = cast<IntValue>(*act.results()[1]).value();
|
||||
auto* tuple_field =
|
||||
arena_->New<IndexedValue>(IndexedValue{index, &exp.static_type()});
|
||||
Address field = object.SubobjectAddress(Member(tuple_field));
|
||||
return todo_.FinishAction(arena_->New<LValue>(field));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "explorer/common/arena.h"
|
||||
#include "explorer/common/error_builders.h"
|
||||
#include "explorer/interpreter/action.h"
|
||||
#include "explorer/interpreter/field_path.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
@@ -48,12 +49,32 @@ static auto FindClassField(Nonnull<const NominalClassValue*> object,
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
const FieldPath::Component& field,
|
||||
SourceLocation source_loc, Nonnull<const Value*> me_value)
|
||||
static auto GetPositionalMember(Nonnull<const Value*> v,
|
||||
const FieldPath::Component& field,
|
||||
SourceLocation source_loc)
|
||||
-> ErrorOr<Nonnull<const Value*>> {
|
||||
std::string_view f = field.name();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static auto GetNamedMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
const FieldPath::Component& field,
|
||||
SourceLocation source_loc,
|
||||
Nonnull<const Value*> me_value)
|
||||
-> ErrorOr<Nonnull<const Value*>> {
|
||||
const auto f = field.member().name();
|
||||
if (field.witness().has_value()) {
|
||||
const auto* witness = cast<Witness>(*field.witness());
|
||||
|
||||
@@ -152,6 +173,15 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
}
|
||||
}
|
||||
|
||||
static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
const FieldPath::Component& field,
|
||||
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);
|
||||
}
|
||||
|
||||
auto Value::GetMember(Nonnull<Arena*> arena, const FieldPath& path,
|
||||
SourceLocation source_loc,
|
||||
Nonnull<const Value*> me_value) const
|
||||
@@ -178,11 +208,11 @@ static auto SetFieldImpl(
|
||||
std::vector<NamedValue> elements = cast<StructValue>(*value).elements();
|
||||
auto it =
|
||||
llvm::find_if(elements, [path_begin](const NamedValue& element) {
|
||||
return element.name == (*path_begin).name();
|
||||
return (*path_begin).IsNamed(element.name);
|
||||
});
|
||||
if (it == elements.end()) {
|
||||
return ProgramError(source_loc)
|
||||
<< "field " << (*path_begin).name() << " not in " << *value;
|
||||
<< "field " << *path_begin << " not in " << *value;
|
||||
}
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
it->value, SetFieldImpl(arena, it->value, path_begin + 1, path_end,
|
||||
@@ -207,17 +237,16 @@ static auto SetFieldImpl(
|
||||
}
|
||||
// Failed to match, show full object content
|
||||
return ProgramError(source_loc)
|
||||
<< "field " << (*path_begin).name() << " not in " << *value;
|
||||
<< "field " << *path_begin << " not in " << *value;
|
||||
}
|
||||
case Value::Kind::TupleType:
|
||||
case Value::Kind::TupleValue: {
|
||||
std::vector<Nonnull<const Value*>> elements =
|
||||
cast<TupleValueBase>(*value).elements();
|
||||
// TODO(geoffromer): update FieldPath to hold integers as well as strings.
|
||||
int index = std::stoi(std::string((*path_begin).name()));
|
||||
if (index < 0 || static_cast<size_t>(index) >= elements.size()) {
|
||||
return ProgramError(source_loc) << "index " << (*path_begin).name()
|
||||
<< " out of range in " << *value;
|
||||
const auto index = (*path_begin).member().index();
|
||||
if (index < 0 || index >= static_cast<int>(elements.size())) {
|
||||
return ProgramError(source_loc)
|
||||
<< "index " << index << " out of range in " << *value;
|
||||
}
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
elements[index], SetFieldImpl(arena, elements[index], path_begin + 1,
|
||||
@@ -541,7 +570,7 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
if (member_name.interface().has_value()) {
|
||||
out << *member_name.interface().value();
|
||||
}
|
||||
out << "." << member_name.name();
|
||||
out << "." << member_name;
|
||||
if (member_name.base_type().has_value() &&
|
||||
member_name.interface().has_value()) {
|
||||
out << ")";
|
||||
@@ -587,7 +616,7 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
<< cast<TypeOfParameterizedEntityName>(*this).name();
|
||||
break;
|
||||
case Value::Kind::TypeOfMemberName: {
|
||||
out << "member name " << cast<TypeOfMemberName>(*this).member().name();
|
||||
out << "member name " << cast<TypeOfMemberName>(*this).member();
|
||||
break;
|
||||
}
|
||||
case Value::Kind::StaticArrayType: {
|
||||
|
||||
@@ -1146,12 +1146,16 @@ class MemberName : public Value {
|
||||
member_(member) {
|
||||
CARBON_CHECK(base_type || interface)
|
||||
<< "member name must be in a type, an interface, or both";
|
||||
CARBON_CHECK(member_.HasName()) << "member must have a name";
|
||||
}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::MemberName;
|
||||
}
|
||||
|
||||
// Prints the member name or identifier.
|
||||
void Print(llvm::raw_ostream& out) const { member_.Print(out); }
|
||||
|
||||
// The type for which `name` is a member or a member of an `impl`.
|
||||
auto base_type() const -> std::optional<Nonnull<const Value*>> {
|
||||
return base_type_;
|
||||
|
||||
Reference in New Issue
Block a user