mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:40:10 +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:
+16
-2
@@ -39,8 +39,8 @@ cc_library(
|
||||
srcs = [
|
||||
"bindings.cpp",
|
||||
"declaration.cpp",
|
||||
"element.cpp",
|
||||
"expression.cpp",
|
||||
"member.cpp",
|
||||
"pattern.cpp",
|
||||
"statement.cpp",
|
||||
],
|
||||
@@ -48,9 +48,9 @@ cc_library(
|
||||
"ast.h",
|
||||
"bindings.h",
|
||||
"declaration.h",
|
||||
"element.h",
|
||||
"expression.h",
|
||||
"impl_binding.h",
|
||||
"member.h",
|
||||
"pattern.h",
|
||||
"return_term.h",
|
||||
"statement.h",
|
||||
@@ -128,6 +128,20 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "element_test",
|
||||
srcs = ["element_test.cpp"],
|
||||
deps = [
|
||||
":ast",
|
||||
":paren_contents",
|
||||
"//common:gtest_main",
|
||||
"//explorer/common:arena",
|
||||
"//explorer/interpreter:action_and_value",
|
||||
"@com_google_googletest//:gtest",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "pattern_test",
|
||||
srcs = ["pattern_test.cpp"],
|
||||
|
||||
@@ -82,3 +82,7 @@ abstract class WhereClause : AstNode;
|
||||
class IsWhereClause : WhereClause;
|
||||
class EqualsWhereClause : WhereClause;
|
||||
class RewriteWhereClause : WhereClause;
|
||||
root class Element;
|
||||
class NamedElement : Element;
|
||||
class PositionalElement : Element;
|
||||
class BaseElement : Element;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "explorer/ast/element.h"
|
||||
|
||||
#include "common/check.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
|
||||
namespace Carbon {
|
||||
NamedElement::NamedElement(Nonnull<const Declaration*> declaration)
|
||||
: Element(ElementKind::NamedElement), element_(declaration) {}
|
||||
|
||||
NamedElement::NamedElement(Nonnull<const NamedValue*> struct_member)
|
||||
: Element(ElementKind::NamedElement), element_(struct_member) {}
|
||||
|
||||
auto NamedElement::IsNamed(std::string_view name) const -> bool {
|
||||
return this->name() == name;
|
||||
}
|
||||
|
||||
auto NamedElement::name() const -> std::string_view {
|
||||
if (const auto* decl = element_.dyn_cast<const Declaration*>()) {
|
||||
return GetName(*decl).value();
|
||||
} else {
|
||||
const auto* named_value = element_.dyn_cast<const NamedValue*>();
|
||||
return named_value->name;
|
||||
}
|
||||
}
|
||||
|
||||
auto NamedElement::type() const -> const Value& {
|
||||
if (const auto* decl = element_.dyn_cast<const Declaration*>()) {
|
||||
return decl->static_type();
|
||||
} else {
|
||||
const auto* named_value = element_.dyn_cast<const NamedValue*>();
|
||||
return *named_value->value;
|
||||
}
|
||||
}
|
||||
|
||||
auto NamedElement::declaration() const
|
||||
-> std::optional<Nonnull<const Declaration*>> {
|
||||
if (const auto* decl = element_.dyn_cast<const Declaration*>()) {
|
||||
return decl;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void NamedElement::Print(llvm::raw_ostream& out) const { out << name(); }
|
||||
|
||||
// Prints the Element
|
||||
void PositionalElement::Print(llvm::raw_ostream& out) const {
|
||||
out << "element #" << index_;
|
||||
}
|
||||
|
||||
// Return whether the element's name matches `name`.
|
||||
auto PositionalElement::IsNamed(std::string_view /*name*/) const -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
void BaseElement::Print(llvm::raw_ostream& out) const { out << "base class"; }
|
||||
|
||||
// Return whether the element's name matches `name`.
|
||||
auto BaseElement::IsNamed(std::string_view /*name*/) const -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
@@ -0,0 +1,138 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef CARBON_EXPLORER_AST_ELEMENT_H_
|
||||
#define CARBON_EXPLORER_AST_ELEMENT_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "explorer/ast/ast_rtti.h"
|
||||
#include "explorer/common/nonnull.h"
|
||||
#include "llvm/ADT/PointerUnion.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
class Declaration;
|
||||
class Value;
|
||||
|
||||
// A NamedValue represents a value with a name, such as a single struct field.
|
||||
struct NamedValue {
|
||||
// The field name.
|
||||
std::string name;
|
||||
|
||||
// The field's value.
|
||||
Nonnull<const Value*> value;
|
||||
};
|
||||
|
||||
// A generic member of a type.
|
||||
//
|
||||
// This is can be a named, positional or other type of member.
|
||||
class Element {
|
||||
protected:
|
||||
explicit Element(ElementKind kind) : kind_(kind) {}
|
||||
|
||||
public:
|
||||
virtual ~Element() = default;
|
||||
|
||||
// Prints the Member
|
||||
virtual void Print(llvm::raw_ostream& out) const = 0;
|
||||
|
||||
// Return whether the member's name matches `name`.
|
||||
virtual auto IsNamed(std::string_view name) const -> bool = 0;
|
||||
|
||||
// Returns the enumerator corresponding to the most-derived type of this
|
||||
// object.
|
||||
auto kind() const -> ElementKind { return kind_; }
|
||||
|
||||
// The declared type of the member, which might include type variables.
|
||||
virtual auto type() const -> const Value& = 0;
|
||||
|
||||
private:
|
||||
const ElementKind kind_;
|
||||
};
|
||||
|
||||
// A named element of a type.
|
||||
//
|
||||
// This is either a declared member of a class, interface, or similar, or a
|
||||
// member of a struct with no declaration.
|
||||
class NamedElement : public Element {
|
||||
public:
|
||||
explicit NamedElement(Nonnull<const Declaration*> declaration);
|
||||
explicit NamedElement(Nonnull<const NamedValue*> struct_member);
|
||||
|
||||
// Prints the element's name
|
||||
void Print(llvm::raw_ostream& out) const override;
|
||||
|
||||
auto IsNamed(std::string_view name) const -> bool override;
|
||||
|
||||
static auto classof(const Element* member) -> bool {
|
||||
return InheritsFromNamedElement(member->kind());
|
||||
}
|
||||
|
||||
auto type() const -> const Value& override;
|
||||
// The name of the member.
|
||||
auto name() const -> std::string_view;
|
||||
// A declaration of the member, if any exists.
|
||||
auto declaration() const -> std::optional<Nonnull<const Declaration*>>;
|
||||
|
||||
private:
|
||||
const llvm::PointerUnion<Nonnull<const Declaration*>,
|
||||
Nonnull<const NamedValue*>>
|
||||
element_;
|
||||
};
|
||||
|
||||
// A positional element of a type.
|
||||
//
|
||||
// This is a positional tuple element, or other index-based value.
|
||||
class PositionalElement : public Element {
|
||||
public:
|
||||
explicit PositionalElement(int index, Nonnull<const Value*> type)
|
||||
: Element(ElementKind::PositionalElement), index_(index), type_(type) {}
|
||||
|
||||
// Prints the element
|
||||
void Print(llvm::raw_ostream& out) const override;
|
||||
|
||||
// Return whether the member's name matches `name`.
|
||||
auto IsNamed(std::string_view name) const -> bool override;
|
||||
|
||||
static auto classof(const Element* member) -> bool {
|
||||
return InheritsFromPositionalElement(member->kind());
|
||||
}
|
||||
|
||||
auto index() const -> int { return index_; }
|
||||
auto type() const -> const Value& override { return *type_; }
|
||||
|
||||
private:
|
||||
const int index_;
|
||||
const Nonnull<const Value*> type_;
|
||||
};
|
||||
|
||||
// A base class object.
|
||||
//
|
||||
// This is the base class object of a class value.
|
||||
class BaseElement : public Element {
|
||||
public:
|
||||
explicit BaseElement(Nonnull<const Value*> type)
|
||||
: Element(ElementKind::BaseElement), type_(type) {}
|
||||
|
||||
// Prints the Member
|
||||
void Print(llvm::raw_ostream& out) const override;
|
||||
|
||||
// Return whether the member's name matches `name`.
|
||||
auto IsNamed(std::string_view name) const -> bool override;
|
||||
|
||||
static auto classof(const Element* member) -> bool {
|
||||
return InheritsFromBaseElement(member->kind());
|
||||
}
|
||||
|
||||
auto type() const -> const Value& override { return *type_; }
|
||||
|
||||
private:
|
||||
const Nonnull<const Value*> type_;
|
||||
};
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // CARBON_EXPLORER_AST_ELEMENT_H_
|
||||
@@ -0,0 +1,96 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "explorer/ast/element.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "explorer/ast/bindings.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/ast/expression.h"
|
||||
#include "explorer/common/arena.h"
|
||||
#include "explorer/interpreter/value.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
|
||||
namespace Carbon::Testing {
|
||||
namespace {
|
||||
|
||||
static auto FakeSourceLoc(int line_num) -> SourceLocation {
|
||||
return SourceLocation("<test>", line_num);
|
||||
}
|
||||
|
||||
class ElementTest : public ::testing::Test {
|
||||
protected:
|
||||
Arena arena;
|
||||
};
|
||||
|
||||
TEST_F(ElementTest, NamedElementType) {
|
||||
const auto src_loc = FakeSourceLoc(1);
|
||||
VariableDeclaration decl{
|
||||
src_loc,
|
||||
arena.New<BindingPattern>(src_loc, "valuename",
|
||||
arena.New<AutoPattern>(src_loc),
|
||||
ValueCategory::Var),
|
||||
std::nullopt, ValueCategory::Var};
|
||||
const auto* static_type = arena.New<IntValue>(1);
|
||||
decl.set_static_type(static_type);
|
||||
NamedElement element_decl(&decl);
|
||||
|
||||
EXPECT_EQ(&element_decl.type(), static_type);
|
||||
|
||||
NamedElement named_val(
|
||||
arena.New<NamedValue>(NamedValue{"valuename", static_type}));
|
||||
EXPECT_EQ(&named_val.type(), static_type);
|
||||
}
|
||||
|
||||
TEST_F(ElementTest, NamedElementDeclaration) {
|
||||
const auto src_loc = FakeSourceLoc(1);
|
||||
VariableDeclaration decl{
|
||||
src_loc,
|
||||
arena.New<BindingPattern>(src_loc, "valuename",
|
||||
arena.New<AutoPattern>(src_loc),
|
||||
ValueCategory::Var),
|
||||
std::nullopt, ValueCategory::Var};
|
||||
const auto* static_type = arena.New<IntValue>(1);
|
||||
NamedElement element_decl(&decl);
|
||||
|
||||
EXPECT_TRUE(element_decl.declaration());
|
||||
|
||||
NamedElement named_val(
|
||||
arena.New<NamedValue>(NamedValue{"valuename", static_type}));
|
||||
EXPECT_FALSE(named_val.declaration());
|
||||
}
|
||||
|
||||
TEST_F(ElementTest, NamedElementIsNamed) {
|
||||
const auto src_loc = FakeSourceLoc(1);
|
||||
VariableDeclaration decl{
|
||||
src_loc,
|
||||
arena.New<BindingPattern>(src_loc, "valuename",
|
||||
arena.New<AutoPattern>(src_loc),
|
||||
ValueCategory::Var),
|
||||
std::nullopt, ValueCategory::Var};
|
||||
NamedElement member_decl(&decl);
|
||||
EXPECT_TRUE(member_decl.IsNamed("valuename"));
|
||||
EXPECT_FALSE(member_decl.IsNamed("anything"));
|
||||
|
||||
NamedElement named_val(
|
||||
arena.New<NamedValue>(NamedValue{"valuename", arena.New<IntValue>(1)}));
|
||||
EXPECT_TRUE(named_val.IsNamed("valuename"));
|
||||
EXPECT_FALSE(named_val.IsNamed("anything"));
|
||||
}
|
||||
|
||||
TEST_F(ElementTest, PositionalElementIsNamed) {
|
||||
PositionalElement element(1, arena.New<IntValue>(1));
|
||||
EXPECT_FALSE(element.IsNamed("anything"));
|
||||
}
|
||||
|
||||
TEST_F(ElementTest, BaseElementIsNamed) {
|
||||
BaseElement element(arena.New<IntValue>(1));
|
||||
EXPECT_FALSE(element.IsNamed("anything"));
|
||||
}
|
||||
} // namespace
|
||||
} // namespace Carbon::Testing
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "common/ostream.h"
|
||||
#include "explorer/ast/ast_node.h"
|
||||
#include "explorer/ast/bindings.h"
|
||||
#include "explorer/ast/member.h"
|
||||
#include "explorer/ast/element.h"
|
||||
#include "explorer/ast/paren_contents.h"
|
||||
#include "explorer/ast/static_scope.h"
|
||||
#include "explorer/ast/value_category.h"
|
||||
@@ -319,18 +319,15 @@ class SimpleMemberAccessExpression : public MemberAccessExpression {
|
||||
|
||||
auto member_name() const -> const std::string& { return member_name_; }
|
||||
|
||||
// Returns the `Member` that the member name resolved to.
|
||||
// Returns the `NamedElement` that the member name resolved to.
|
||||
// Should not be called before typechecking.
|
||||
auto member() const -> const Member& {
|
||||
auto member() const -> const NamedElement& {
|
||||
CARBON_CHECK(member_.has_value());
|
||||
return *member_;
|
||||
return *member_.value();
|
||||
}
|
||||
|
||||
// Can only be called once, during typechecking.
|
||||
void set_member(Member member) {
|
||||
CARBON_CHECK(!member_.has_value());
|
||||
member_ = member;
|
||||
}
|
||||
void set_member(Nonnull<const NamedElement*> member) { member_ = member; }
|
||||
|
||||
// If `object` is a constrained type parameter and `member` was found in an
|
||||
// interface, returns that interface. Should not be called before
|
||||
@@ -348,7 +345,7 @@ class SimpleMemberAccessExpression : public MemberAccessExpression {
|
||||
|
||||
private:
|
||||
std::string member_name_;
|
||||
std::optional<Member> member_;
|
||||
std::optional<Nonnull<const NamedElement*>> member_;
|
||||
std::optional<Nonnull<const InterfaceType*>> found_in_interface_;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "explorer/ast/member.h"
|
||||
|
||||
#include "common/check.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
Member::Member(Nonnull<const Declaration*> declaration)
|
||||
: member_(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 {
|
||||
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 IndexedValue*>()->value;
|
||||
}
|
||||
}
|
||||
|
||||
auto Member::declaration() const -> std::optional<Nonnull<const Declaration*>> {
|
||||
if (const auto* decl = member_.dyn_cast<const Declaration*>()) {
|
||||
return decl;
|
||||
}
|
||||
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
|
||||
@@ -1,76 +0,0 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef CARBON_EXPLORER_AST_MEMBER_H_
|
||||
#define CARBON_EXPLORER_AST_MEMBER_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "explorer/common/nonnull.h"
|
||||
#include "llvm/ADT/PointerUnion.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
class Declaration;
|
||||
class Value;
|
||||
|
||||
// A NamedValue represents a value with a name, such as a single struct field.
|
||||
struct NamedValue {
|
||||
// The field name.
|
||||
std::string name;
|
||||
|
||||
// The field's value.
|
||||
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
|
||||
// member of a struct with no declaration.
|
||||
class Member {
|
||||
public:
|
||||
explicit Member(Nonnull<const Declaration*> declaration);
|
||||
explicit Member(Nonnull<const NamedValue*> struct_member);
|
||||
explicit Member(Nonnull<const IndexedValue*> tuple_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*>,
|
||||
Nonnull<const IndexedValue*>>
|
||||
member_;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // CARBON_EXPLORER_AST_MEMBER_H_
|
||||
@@ -16,10 +16,12 @@ cc_library(
|
||||
"action.h",
|
||||
"value.h",
|
||||
],
|
||||
# Exposed to resolve `member_test` dependencies.
|
||||
visibility = ["//explorer/ast:__pkg__"],
|
||||
deps = [
|
||||
":address",
|
||||
":dictionary",
|
||||
":field_path",
|
||||
":element_path",
|
||||
":heap_allocation_interface",
|
||||
":stack",
|
||||
"//common:check",
|
||||
@@ -51,7 +53,7 @@ cc_library(
|
||||
name = "address",
|
||||
hdrs = ["address.h"],
|
||||
deps = [
|
||||
":field_path",
|
||||
":element_path",
|
||||
"//common:ostream",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
@@ -86,8 +88,8 @@ cc_library(
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "field_path",
|
||||
hdrs = ["field_path.h"],
|
||||
name = "element_path",
|
||||
hdrs = ["element_path.h"],
|
||||
deps = [
|
||||
"//common:ostream",
|
||||
"//explorer/ast",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "explorer/interpreter/field_path.h"
|
||||
#include "explorer/interpreter/element_path.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
namespace Carbon {
|
||||
@@ -57,18 +57,18 @@ class Address {
|
||||
// Prints a human-readable representation of `a` to `out`.
|
||||
//
|
||||
// Currently, that representation consists of an AllocationId followed by an
|
||||
// optional FieldPath specifying a particular field within that allocation.
|
||||
// optional ElementPath specifying a particular field within that allocation.
|
||||
void Print(llvm::raw_ostream& out) const {
|
||||
out << allocation_ << field_path_;
|
||||
out << allocation_ << element_path_;
|
||||
}
|
||||
|
||||
LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
|
||||
|
||||
// If *this represents the address of an object with a field named
|
||||
// `field_name`, this method returns the address of that field.
|
||||
auto SubobjectAddress(Member member) const -> Address {
|
||||
auto ElementAddress(Nonnull<const Element*> element) const -> Address {
|
||||
Address result = *this;
|
||||
result.field_path_.Append(member);
|
||||
result.element_path_.Append(element);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class Address {
|
||||
friend class Heap;
|
||||
|
||||
AllocationId allocation_;
|
||||
FieldPath field_path_;
|
||||
ElementPath element_path_;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef CARBON_EXPLORER_INTERPRETER_FIELD_PATH_H_
|
||||
#define CARBON_EXPLORER_INTERPRETER_FIELD_PATH_H_
|
||||
#ifndef CARBON_EXPLORER_INTERPRETER_ELEMENT_PATH_H_
|
||||
#define CARBON_EXPLORER_INTERPRETER_ELEMENT_PATH_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "explorer/ast/member.h"
|
||||
#include "explorer/ast/element.h"
|
||||
#include "explorer/ast/static_scope.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
@@ -20,38 +20,38 @@ namespace Carbon {
|
||||
class InterfaceType;
|
||||
class Witness;
|
||||
|
||||
// Given some initial Value, a FieldPath identifies a sub-Value within it,
|
||||
// Given some initial Value, a ElementPath identifies a sub-Value within it,
|
||||
// in much the same way that a file path identifies a file within some
|
||||
// directory. FieldPaths are relative rather than absolute: the initial
|
||||
// Value is specified by the context in which the FieldPath is used, not
|
||||
// by the FieldPath itself.
|
||||
// Value is specified by the context in which the ElementPath is used, not
|
||||
// by the ElementPath itself.
|
||||
//
|
||||
// A FieldPath consists of a series of steps, which specify how to
|
||||
// A ElementPath consists of a series of steps, which specify how to
|
||||
// incrementally navigate from a Value to one of its fields. Currently
|
||||
// there is only one kind of step, a string specifying a child field by name,
|
||||
// but that may change as Carbon develops. Note that an empty FieldPath
|
||||
// but that may change as Carbon develops. Note that an empty ElementPath
|
||||
// refers to the initial Value itself.
|
||||
class FieldPath {
|
||||
class ElementPath {
|
||||
public:
|
||||
// Constructs an empty FieldPath.
|
||||
FieldPath() = default;
|
||||
// Constructs an empty ElementPath.
|
||||
ElementPath() = default;
|
||||
|
||||
// A single component of the FieldPath, which is typically the name
|
||||
// A single component of the ElementPath, which is typically the name
|
||||
// of a field. However, inside a generic, when there is a field
|
||||
// access on something of a generic type, e.g., `T`, then we also
|
||||
// need `witness`, a pointer to the witness table containing that field.
|
||||
class Component {
|
||||
public:
|
||||
explicit Component(Member member) : member_(member) {}
|
||||
Component(Member member,
|
||||
explicit Component(Nonnull<const Element*> element) : element_(element) {}
|
||||
Component(Nonnull<const Element*> element,
|
||||
std::optional<Nonnull<const InterfaceType*>> interface,
|
||||
std::optional<Nonnull<const Witness*>> witness)
|
||||
: member_(member), interface_(interface), witness_(witness) {}
|
||||
: element_(element), interface_(interface), witness_(witness) {}
|
||||
|
||||
auto member() const -> Member { return member_; }
|
||||
auto element() const -> Nonnull<const Element*> { return element_; }
|
||||
|
||||
auto IsNamed(std::string_view name) const -> bool {
|
||||
return member_.IsNamed(name);
|
||||
return element_->IsNamed(name);
|
||||
}
|
||||
|
||||
auto interface() const -> std::optional<Nonnull<const InterfaceType*>> {
|
||||
@@ -62,29 +62,30 @@ class FieldPath {
|
||||
return witness_;
|
||||
}
|
||||
|
||||
void Print(llvm::raw_ostream& out) const { return member_.Print(out); }
|
||||
void Print(llvm::raw_ostream& out) const { return element_->Print(out); }
|
||||
|
||||
private:
|
||||
Member member_;
|
||||
Nonnull<const Element*> element_;
|
||||
std::optional<Nonnull<const InterfaceType*>> interface_;
|
||||
std::optional<Nonnull<const Witness*>> witness_;
|
||||
};
|
||||
|
||||
// Constructs a FieldPath consisting of a single step.
|
||||
explicit FieldPath(Member member) : components_({Component(member)}) {}
|
||||
explicit FieldPath(const Component& f) : components_({f}) {}
|
||||
// Constructs a ElementPath consisting of a single step.
|
||||
explicit ElementPath(Nonnull<const Element*> element)
|
||||
: components_({Component(element)}) {}
|
||||
explicit ElementPath(const Component& f) : components_({f}) {}
|
||||
|
||||
FieldPath(const FieldPath&) = default;
|
||||
FieldPath(FieldPath&&) = default;
|
||||
auto operator=(const FieldPath&) -> FieldPath& = default;
|
||||
auto operator=(FieldPath&&) -> FieldPath& = default;
|
||||
ElementPath(const ElementPath&) = default;
|
||||
ElementPath(ElementPath&&) = default;
|
||||
auto operator=(const ElementPath&) -> ElementPath& = default;
|
||||
auto operator=(ElementPath&&) -> ElementPath& = default;
|
||||
|
||||
// Returns whether *this is empty.
|
||||
auto IsEmpty() const -> bool { return components_.empty(); }
|
||||
|
||||
// Appends `member` to the end of *this.
|
||||
auto Append(Member member) -> void {
|
||||
components_.push_back(Component(member));
|
||||
// Appends `element` to the end of *this.
|
||||
auto Append(Nonnull<const Element*> element) -> void {
|
||||
components_.push_back(Component(element));
|
||||
}
|
||||
|
||||
void Print(llvm::raw_ostream& out) const {
|
||||
@@ -96,7 +97,7 @@ class FieldPath {
|
||||
LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
|
||||
|
||||
private:
|
||||
// The representation of FieldPath describes how to locate a Value within
|
||||
// The representation of ElementPath describes how to locate a Value within
|
||||
// another Value, so its implementation details are tied to the implementation
|
||||
// details of Value.
|
||||
friend class Value;
|
||||
@@ -105,4 +106,4 @@ class FieldPath {
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // CARBON_EXPLORER_INTERPRETER_FIELD_PATH_H_
|
||||
#endif // CARBON_EXPLORER_INTERPRETER_ELEMENT_PATH_H_
|
||||
@@ -31,7 +31,7 @@ auto Heap::Read(const Address& a, SourceLocation source_loc) const
|
||||
CARBON_RETURN_IF_ERROR(this->CheckInit(a.allocation_, source_loc));
|
||||
CARBON_RETURN_IF_ERROR(this->CheckAlive(a.allocation_, source_loc));
|
||||
Nonnull<const Value*> value = values_[a.allocation_.index_];
|
||||
return value->GetMember(arena_, a.field_path_, source_loc, value);
|
||||
return value->GetElement(arena_, a.element_path_, source_loc, value);
|
||||
}
|
||||
|
||||
auto Heap::Write(const Address& a, Nonnull<const Value*> v,
|
||||
@@ -42,7 +42,7 @@ auto Heap::Write(const Address& a, Nonnull<const Value*> v,
|
||||
}
|
||||
CARBON_ASSIGN_OR_RETURN(values_[a.allocation_.index_],
|
||||
values_[a.allocation_.index_]->SetField(
|
||||
arena_, a.field_path_, v, source_loc));
|
||||
arena_, a.element_path_, v, source_loc));
|
||||
return Success();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "common/check.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/ast/element.h"
|
||||
#include "explorer/ast/expression.h"
|
||||
#include "explorer/common/arena.h"
|
||||
#include "explorer/common/error_builders.h"
|
||||
@@ -423,7 +424,7 @@ auto Interpreter::StepLvalue() -> ErrorOr<Success> {
|
||||
// { v :: [].f :: C, E, F} :: S, H}
|
||||
// -> { { &v.f :: C, E, F} :: S, H }
|
||||
Address object = cast<LValue>(*act.results()[0]).address();
|
||||
Address member = object.SubobjectAddress(access.member());
|
||||
Address member = object.ElementAddress(&access.member());
|
||||
return todo_.FinishAction(arena_->New<LValue>(member));
|
||||
}
|
||||
}
|
||||
@@ -445,7 +446,7 @@ auto Interpreter::StepLvalue() -> ErrorOr<Success> {
|
||||
Convert(act.results()[0], *access.member().base_type(),
|
||||
exp.source_loc()));
|
||||
Address object = cast<LValue>(*val).address();
|
||||
Address field = object.SubobjectAddress(access.member().member());
|
||||
Address field = object.ElementAddress(&access.member().member());
|
||||
return todo_.FinishAction(arena_->New<LValue>(field));
|
||||
}
|
||||
}
|
||||
@@ -464,9 +465,8 @@ auto Interpreter::StepLvalue() -> ErrorOr<Success> {
|
||||
// -> { { &v[i] :: C, E, F} :: S, H }
|
||||
Address object = cast<LValue>(*act.results()[0]).address();
|
||||
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));
|
||||
Address field = object.ElementAddress(
|
||||
arena_->New<PositionalElement>(index, &exp.static_type()));
|
||||
return todo_.FinishAction(arena_->New<LValue>(field));
|
||||
}
|
||||
}
|
||||
@@ -1131,8 +1131,8 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
if (access.impl().has_value()) {
|
||||
witness = cast<Witness>(act.results()[1]);
|
||||
}
|
||||
FieldPath::Component member(access.member(), found_in_interface,
|
||||
witness);
|
||||
ElementPath::Component member(&access.member(), found_in_interface,
|
||||
witness);
|
||||
const Value* aggregate;
|
||||
if (access.is_type_access()) {
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
@@ -1147,8 +1147,8 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
}
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> member_value,
|
||||
aggregate->GetMember(arena_, FieldPath(member), exp.source_loc(),
|
||||
act.results()[0]));
|
||||
aggregate->GetElement(arena_, ElementPath(member),
|
||||
exp.source_loc(), act.results()[0]));
|
||||
return todo_.FinishAction(member_value);
|
||||
}
|
||||
}
|
||||
@@ -1216,11 +1216,11 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
object, Convert(object, *access.member().base_type(),
|
||||
exp.source_loc()));
|
||||
}
|
||||
FieldPath::Component field(access.member().member(),
|
||||
found_in_interface, witness);
|
||||
ElementPath::Component field(&access.member().member(),
|
||||
found_in_interface, witness);
|
||||
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> member,
|
||||
object->GetMember(arena_, FieldPath(field),
|
||||
exp.source_loc(), object));
|
||||
object->GetElement(arena_, ElementPath(field),
|
||||
exp.source_loc(), object));
|
||||
return todo_.FinishAction(member);
|
||||
}
|
||||
}
|
||||
@@ -2038,7 +2038,7 @@ auto Interpreter::StepDestroy() -> ErrorOr<Success> {
|
||||
const auto& member = class_dec.members()[index];
|
||||
if (const auto* var = dyn_cast<VariableDeclaration>(member)) {
|
||||
Address object = destroy_act.lvalue()->address();
|
||||
Address mem = object.SubobjectAddress(Member(var));
|
||||
Address mem = object.ElementAddress(arena_->New<NamedElement>(var));
|
||||
SourceLocation source_loc("destructor", 1);
|
||||
auto v = heap_.Read(mem, source_loc);
|
||||
return todo_.Spawn(
|
||||
|
||||
@@ -2408,19 +2408,26 @@ static void RewriteMemberAccess(Nonnull<MemberAccessExpression*> access,
|
||||
}
|
||||
|
||||
// Determine whether the given member declaration declares an instance member.
|
||||
static auto IsInstanceMember(Member member) {
|
||||
if (!member.declaration()) {
|
||||
// This is a struct field.
|
||||
return true;
|
||||
}
|
||||
Nonnull<const Declaration*> declaration = *member.declaration();
|
||||
switch (declaration->kind()) {
|
||||
case DeclarationKind::FunctionDeclaration:
|
||||
return cast<FunctionDeclaration>(declaration)->is_method();
|
||||
case DeclarationKind::VariableDeclaration:
|
||||
static auto IsInstanceMember(Nonnull<const Element*> element) {
|
||||
switch (element->kind()) {
|
||||
case ElementKind::BaseElement:
|
||||
case ElementKind::PositionalElement:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
case ElementKind::NamedElement:
|
||||
const auto nom_element = cast<NamedElement>(element);
|
||||
if (!nom_element->declaration()) {
|
||||
// This is a struct field.
|
||||
return true;
|
||||
}
|
||||
Nonnull<const Declaration*> declaration = *nom_element->declaration();
|
||||
switch (declaration->kind()) {
|
||||
case DeclarationKind::FunctionDeclaration:
|
||||
return cast<FunctionDeclaration>(declaration)->is_method();
|
||||
case DeclarationKind::VariableDeclaration:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2555,7 +2562,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
const auto& struct_type = cast<StructType>(object_type);
|
||||
for (const auto& field : struct_type.fields()) {
|
||||
if (access.member_name() == field.name) {
|
||||
access.set_member(Member(&field));
|
||||
access.set_member(arena_->New<NamedElement>(&field));
|
||||
access.set_static_type(field.value);
|
||||
access.set_value_category(access.object().value_category());
|
||||
return Success();
|
||||
@@ -2574,9 +2581,9 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
auto [member_type, member, member_t_class] = res.value();
|
||||
Nonnull<const Value*> field_type =
|
||||
Substitute(member_t_class->bindings(), member_type);
|
||||
access.set_member(Member(member));
|
||||
access.set_member(arena_->New<NamedElement>(member));
|
||||
access.set_static_type(field_type);
|
||||
access.set_is_type_access(!IsInstanceMember(access.member()));
|
||||
access.set_is_type_access(!IsInstanceMember(&access.member()));
|
||||
switch (member->kind()) {
|
||||
case DeclarationKind::VariableDeclaration:
|
||||
access.set_value_category(access.object().value_category());
|
||||
@@ -2647,9 +2654,9 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
const Value& member_type = result.member->static_type();
|
||||
Nonnull<const Value*> inst_member_type =
|
||||
Substitute(bindings, &member_type);
|
||||
access.set_member(Member(result.member));
|
||||
access.set_member(arena_->New<NamedElement>(result.member));
|
||||
access.set_found_in_interface(result.interface);
|
||||
access.set_is_type_access(!IsInstanceMember(access.member()));
|
||||
access.set_is_type_access(!IsInstanceMember(&access.member()));
|
||||
access.set_static_type(inst_member_type);
|
||||
|
||||
if (auto* func_decl = dyn_cast<FunctionDeclaration>(result.member)) {
|
||||
@@ -2700,17 +2707,17 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
CARBON_ASSIGN_OR_RETURN(Nonnull<const Witness*> impl,
|
||||
impl_scope.Resolve(result.interface, type,
|
||||
e->source_loc(), *this));
|
||||
access.set_member(Member(result.member));
|
||||
access.set_member(arena_->New<NamedElement>(result.member));
|
||||
access.set_impl(impl);
|
||||
access.set_found_in_interface(result.interface);
|
||||
|
||||
if (IsInstanceMember(access.member())) {
|
||||
if (IsInstanceMember(&access.member())) {
|
||||
// This is a member name denoting an instance member.
|
||||
// TODO: Consider setting the static type of all instance member
|
||||
// declarations to be member name types, rather than special-casing
|
||||
// member accesses that name them.
|
||||
access.set_static_type(
|
||||
arena_->New<TypeOfMemberName>(Member(result.member)));
|
||||
arena_->New<TypeOfMemberName>(NamedElement(result.member)));
|
||||
access.set_value_category(ValueCategory::Let);
|
||||
} else {
|
||||
// This is a non-instance member whose value is found directly via
|
||||
@@ -2738,9 +2745,9 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
case Value::Kind::StructType: {
|
||||
for (const auto& field : cast<StructType>(type)->fields()) {
|
||||
if (access.member_name() == field.name) {
|
||||
access.set_member(Member(&field));
|
||||
access.set_member(arena_->New<NamedElement>(&field));
|
||||
access.set_static_type(
|
||||
arena_->New<TypeOfMemberName>(Member(&field)));
|
||||
arena_->New<TypeOfMemberName>(NamedElement(&field)));
|
||||
access.set_value_category(ValueCategory::Let);
|
||||
return Success();
|
||||
}
|
||||
@@ -2769,8 +2776,9 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
substituted_parameter_type, &choice);
|
||||
// TODO: Should there be a Declaration corresponding to each
|
||||
// choice type alternative?
|
||||
access.set_member(Member(arena_->New<NamedValue>(
|
||||
NamedValue{access.member_name(), type})));
|
||||
access.set_member(
|
||||
arena_->New<NamedElement>(arena_->New<NamedValue>(
|
||||
NamedValue{access.member_name(), type})));
|
||||
access.set_static_type(type);
|
||||
access.set_value_category(ValueCategory::Let);
|
||||
return Success();
|
||||
@@ -2784,7 +2792,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
&class_type));
|
||||
if (type_member.has_value()) {
|
||||
auto [member_type, member] = type_member.value();
|
||||
access.set_member(Member(member));
|
||||
access.set_member(arena_->New<NamedElement>(member));
|
||||
switch (member->kind()) {
|
||||
case DeclarationKind::FunctionDeclaration: {
|
||||
const auto& func = cast<FunctionDeclaration>(*member);
|
||||
@@ -2801,7 +2809,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
break;
|
||||
}
|
||||
access.set_static_type(
|
||||
arena_->New<TypeOfMemberName>(Member(member)));
|
||||
arena_->New<TypeOfMemberName>(NamedElement(member)));
|
||||
access.set_value_category(ValueCategory::Let);
|
||||
return Success();
|
||||
} else {
|
||||
@@ -2817,10 +2825,10 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
ConstraintLookupResult result,
|
||||
LookupInConstraint(e->source_loc(), "member access", type,
|
||||
access.member_name()));
|
||||
access.set_member(Member(result.member));
|
||||
access.set_member(arena_->New<NamedElement>(result.member));
|
||||
access.set_found_in_interface(result.interface);
|
||||
access.set_static_type(
|
||||
arena_->New<TypeOfMemberName>(Member(result.member)));
|
||||
arena_->New<TypeOfMemberName>(NamedElement(result.member)));
|
||||
access.set_value_category(ValueCategory::Let);
|
||||
return Success();
|
||||
}
|
||||
@@ -2853,7 +2861,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
InterpExp(&access.path(), arena_, trace_stream_));
|
||||
const auto& member_name = cast<MemberName>(*member_name_value);
|
||||
access.set_member(&member_name);
|
||||
bool is_instance_member = IsInstanceMember(member_name.member());
|
||||
bool is_instance_member = IsInstanceMember(&member_name.member());
|
||||
|
||||
bool has_instance = true;
|
||||
std::optional<Nonnull<const Value*>> base_type = member_name.base_type();
|
||||
|
||||
@@ -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*>> {
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
#include "common/ostream.h"
|
||||
#include "explorer/ast/bindings.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/ast/member.h"
|
||||
#include "explorer/ast/element.h"
|
||||
#include "explorer/ast/statement.h"
|
||||
#include "explorer/common/nonnull.h"
|
||||
#include "explorer/interpreter/address.h"
|
||||
#include "explorer/interpreter/field_path.h"
|
||||
#include "explorer/interpreter/element_path.h"
|
||||
#include "explorer/interpreter/stack.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
@@ -90,18 +90,18 @@ class Value {
|
||||
void Print(llvm::raw_ostream& out) const;
|
||||
LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
|
||||
|
||||
// Returns the sub-Value specified by `path`, which must be a valid field
|
||||
// Returns the sub-Value specified by `path`, which must be a valid element
|
||||
// path for *this. If the sub-Value is a method and its self_pattern is an
|
||||
// AddrPattern, then pass the LValue representing the receiver as `me_value`,
|
||||
// otherwise pass `*this`.
|
||||
auto GetMember(Nonnull<Arena*> arena, const FieldPath& path,
|
||||
SourceLocation source_loc,
|
||||
Nonnull<const Value*> me_value) const
|
||||
auto GetElement(Nonnull<Arena*> arena, const ElementPath& path,
|
||||
SourceLocation source_loc,
|
||||
Nonnull<const Value*> me_value) const
|
||||
-> ErrorOr<Nonnull<const Value*>>;
|
||||
|
||||
// Returns a copy of *this, but with the sub-Value specified by `path`
|
||||
// set to `field_value`. `path` must be a valid field path for *this.
|
||||
auto SetField(Nonnull<Arena*> arena, const FieldPath& path,
|
||||
auto SetField(Nonnull<Arena*> arena, const ElementPath& path,
|
||||
Nonnull<const Value*> field_value,
|
||||
SourceLocation source_loc) const
|
||||
-> ErrorOr<Nonnull<const Value*>>;
|
||||
@@ -1139,14 +1139,13 @@ class MemberName : public Value {
|
||||
public:
|
||||
MemberName(std::optional<Nonnull<const Value*>> base_type,
|
||||
std::optional<Nonnull<const InterfaceType*>> interface,
|
||||
Member member)
|
||||
NamedElement member)
|
||||
: Value(Kind::MemberName),
|
||||
base_type_(base_type),
|
||||
interface_(interface),
|
||||
member_(member) {
|
||||
member_(std::move(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 {
|
||||
@@ -1165,14 +1164,14 @@ class MemberName : public Value {
|
||||
return interface_;
|
||||
}
|
||||
// The member.
|
||||
auto member() const -> Member { return member_; }
|
||||
auto member() const -> const NamedElement& { return member_; }
|
||||
// The name of the member.
|
||||
auto name() const -> std::string_view { return member().name(); }
|
||||
|
||||
private:
|
||||
std::optional<Nonnull<const Value*>> base_type_;
|
||||
std::optional<Nonnull<const InterfaceType*>> interface_;
|
||||
Member member_;
|
||||
NamedElement member_;
|
||||
};
|
||||
|
||||
// A symbolic value representing an associated constant.
|
||||
@@ -1340,8 +1339,8 @@ class TypeOfParameterizedEntityName : public Value {
|
||||
// as the member name in a compound member access.
|
||||
class TypeOfMemberName : public Value {
|
||||
public:
|
||||
explicit TypeOfMemberName(Member member)
|
||||
: Value(Kind::TypeOfMemberName), member_(member) {}
|
||||
explicit TypeOfMemberName(NamedElement member)
|
||||
: Value(Kind::TypeOfMemberName), member_(std::move(member)) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::TypeOfMemberName;
|
||||
@@ -1349,10 +1348,10 @@ class TypeOfMemberName : public Value {
|
||||
|
||||
// TODO: consider removing this or moving it elsewhere in the AST,
|
||||
// since it's arguably part of the expression value rather than its type.
|
||||
auto member() const -> Member { return member_; }
|
||||
auto member() const -> NamedElement { return member_; }
|
||||
|
||||
private:
|
||||
Member member_;
|
||||
NamedElement member_;
|
||||
};
|
||||
|
||||
// The type of a statically-sized array.
|
||||
|
||||
Reference in New Issue
Block a user