diff --git a/explorer/ast/BUILD b/explorer/ast/BUILD index 10f5250c794d..3b6687c252a5 100644 --- a/explorer/ast/BUILD +++ b/explorer/ast/BUILD @@ -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"], diff --git a/explorer/ast/ast_rtti.txt b/explorer/ast/ast_rtti.txt index 26f250f6eeae..43a21a7354dd 100644 --- a/explorer/ast/ast_rtti.txt +++ b/explorer/ast/ast_rtti.txt @@ -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; diff --git a/explorer/ast/element.cpp b/explorer/ast/element.cpp new file mode 100644 index 000000000000..1a4952e3cf98 --- /dev/null +++ b/explorer/ast/element.cpp @@ -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 declaration) + : Element(ElementKind::NamedElement), element_(declaration) {} + +NamedElement::NamedElement(Nonnull 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()) { + return GetName(*decl).value(); + } else { + const auto* named_value = element_.dyn_cast(); + return named_value->name; + } +} + +auto NamedElement::type() const -> const Value& { + if (const auto* decl = element_.dyn_cast()) { + return decl->static_type(); + } else { + const auto* named_value = element_.dyn_cast(); + return *named_value->value; + } +} + +auto NamedElement::declaration() const + -> std::optional> { + if (const auto* decl = element_.dyn_cast()) { + 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 diff --git a/explorer/ast/element.h b/explorer/ast/element.h new file mode 100644 index 000000000000..ef005bf051d2 --- /dev/null +++ b/explorer/ast/element.h @@ -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 +#include +#include + +#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 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 declaration); + explicit NamedElement(Nonnull 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>; + + private: + const llvm::PointerUnion, + Nonnull> + 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 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 type_; +}; + +// A base class object. +// +// This is the base class object of a class value. +class BaseElement : public Element { + public: + explicit BaseElement(Nonnull 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 type_; +}; +} // namespace Carbon + +#endif // CARBON_EXPLORER_AST_ELEMENT_H_ diff --git a/explorer/ast/element_test.cpp b/explorer/ast/element_test.cpp new file mode 100644 index 000000000000..57f8eb47adcd --- /dev/null +++ b/explorer/ast/element_test.cpp @@ -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 +#include + +#include + +#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("", 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(src_loc, "valuename", + arena.New(src_loc), + ValueCategory::Var), + std::nullopt, ValueCategory::Var}; + const auto* static_type = arena.New(1); + decl.set_static_type(static_type); + NamedElement element_decl(&decl); + + EXPECT_EQ(&element_decl.type(), static_type); + + NamedElement named_val( + arena.New(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(src_loc, "valuename", + arena.New(src_loc), + ValueCategory::Var), + std::nullopt, ValueCategory::Var}; + const auto* static_type = arena.New(1); + NamedElement element_decl(&decl); + + EXPECT_TRUE(element_decl.declaration()); + + NamedElement named_val( + arena.New(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(src_loc, "valuename", + arena.New(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{"valuename", arena.New(1)})); + EXPECT_TRUE(named_val.IsNamed("valuename")); + EXPECT_FALSE(named_val.IsNamed("anything")); +} + +TEST_F(ElementTest, PositionalElementIsNamed) { + PositionalElement element(1, arena.New(1)); + EXPECT_FALSE(element.IsNamed("anything")); +} + +TEST_F(ElementTest, BaseElementIsNamed) { + BaseElement element(arena.New(1)); + EXPECT_FALSE(element.IsNamed("anything")); +} +} // namespace +} // namespace Carbon::Testing diff --git a/explorer/ast/expression.h b/explorer/ast/expression.h index 402ac935dbba..56000b9aec5a 100644 --- a/explorer/ast/expression.h +++ b/explorer/ast/expression.h @@ -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 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_; + std::optional> member_; std::optional> found_in_interface_; }; diff --git a/explorer/ast/member.cpp b/explorer/ast/member.cpp deleted file mode 100644 index cb5e82a1aaba..000000000000 --- a/explorer/ast/member.cpp +++ /dev/null @@ -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 declaration) - : member_(declaration) {} - -Member::Member(Nonnull struct_member) - : member_(struct_member) {} - -Member::Member(Nonnull 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()) { - return GetName(*decl).value(); - } else if (const auto* named_valued = member_.dyn_cast()) { - return named_valued->name; - } else { - CARBON_FATAL() << "Unreachable"; - } -} - -auto Member::HasPosition() const -> bool { - return member_.dyn_cast() != 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()->index; -} - -auto Member::type() const -> const Value& { - if (const auto* decl = member_.dyn_cast()) { - return decl->static_type(); - } else if (const auto* named_valued = member_.dyn_cast()) { - return *named_valued->value; - } else { - return *member_.get()->value; - } -} - -auto Member::declaration() const -> std::optional> { - if (const auto* decl = member_.dyn_cast()) { - 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()) { - out << "element #" << member_.get()->index; - } else { - CARBON_FATAL() << "Unhandled member type"; - } -} - -} // namespace Carbon diff --git a/explorer/ast/member.h b/explorer/ast/member.h deleted file mode 100644 index 85272a4fa7d4..000000000000 --- a/explorer/ast/member.h +++ /dev/null @@ -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 -#include -#include - -#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 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 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 declaration); - explicit Member(Nonnull struct_member); - explicit Member(Nonnull 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>; - - private: - llvm::PointerUnion, Nonnull, - Nonnull> - member_; -}; - -} // namespace Carbon - -#endif // CARBON_EXPLORER_AST_MEMBER_H_ diff --git a/explorer/interpreter/BUILD b/explorer/interpreter/BUILD index cbacea5419b7..0363b3587d56 100644 --- a/explorer/interpreter/BUILD +++ b/explorer/interpreter/BUILD @@ -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", diff --git a/explorer/interpreter/address.h b/explorer/interpreter/address.h index b3d193c12152..af99496ee5be 100644 --- a/explorer/interpreter/address.h +++ b/explorer/interpreter/address.h @@ -10,7 +10,7 @@ #include #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 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 diff --git a/explorer/interpreter/field_path.h b/explorer/interpreter/element_path.h similarity index 54% rename from explorer/interpreter/field_path.h rename to explorer/interpreter/element_path.h index ccc484d89130..4f823b7693f1 100644 --- a/explorer/interpreter/field_path.h +++ b/explorer/interpreter/element_path.h @@ -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 #include @@ -11,7 +11,7 @@ #include #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 element) : element_(element) {} + Component(Nonnull element, std::optional> interface, std::optional> witness) - : member_(member), interface_(interface), witness_(witness) {} + : element_(element), interface_(interface), witness_(witness) {} - auto member() const -> Member { return member_; } + auto element() const -> Nonnull { return element_; } auto IsNamed(std::string_view name) const -> bool { - return member_.IsNamed(name); + return element_->IsNamed(name); } auto interface() const -> std::optional> { @@ -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 element_; std::optional> interface_; std::optional> 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 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 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_ diff --git a/explorer/interpreter/heap.cpp b/explorer/interpreter/heap.cpp index 717f0b03e53f..3c9b01dfac57 100644 --- a/explorer/interpreter/heap.cpp +++ b/explorer/interpreter/heap.cpp @@ -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 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 v, @@ -42,7 +42,7 @@ auto Heap::Write(const Address& a, Nonnull 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(); } diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index e8a700065697..6d8610deb783 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -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 { // { v :: [].f :: C, E, F} :: S, H} // -> { { &v.f :: C, E, F} :: S, H } Address object = cast(*act.results()[0]).address(); - Address member = object.SubobjectAddress(access.member()); + Address member = object.ElementAddress(&access.member()); return todo_.FinishAction(arena_->New(member)); } } @@ -445,7 +446,7 @@ auto Interpreter::StepLvalue() -> ErrorOr { Convert(act.results()[0], *access.member().base_type(), exp.source_loc())); Address object = cast(*val).address(); - Address field = object.SubobjectAddress(access.member().member()); + Address field = object.ElementAddress(&access.member().member()); return todo_.FinishAction(arena_->New(field)); } } @@ -464,9 +465,8 @@ auto Interpreter::StepLvalue() -> ErrorOr { // -> { { &v[i] :: C, E, F} :: S, H } Address object = cast(*act.results()[0]).address(); const auto index = cast(*act.results()[1]).value(); - auto* tuple_field = - arena_->New(IndexedValue{index, &exp.static_type()}); - Address field = object.SubobjectAddress(Member(tuple_field)); + Address field = object.ElementAddress( + arena_->New(index, &exp.static_type())); return todo_.FinishAction(arena_->New(field)); } } @@ -1131,8 +1131,8 @@ auto Interpreter::StepExp() -> ErrorOr { if (access.impl().has_value()) { witness = cast(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 { } CARBON_ASSIGN_OR_RETURN( Nonnull 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 { 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 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 { const auto& member = class_dec.members()[index]; if (const auto* var = dyn_cast(member)) { Address object = destroy_act.lvalue()->address(); - Address mem = object.SubobjectAddress(Member(var)); + Address mem = object.ElementAddress(arena_->New(var)); SourceLocation source_loc("destructor", 1); auto v = heap_.Read(mem, source_loc); return todo_.Spawn( diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 8f81b2756269..073c733a6b67 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -2408,19 +2408,26 @@ static void RewriteMemberAccess(Nonnull 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 declaration = *member.declaration(); - switch (declaration->kind()) { - case DeclarationKind::FunctionDeclaration: - return cast(declaration)->is_method(); - case DeclarationKind::VariableDeclaration: +static auto IsInstanceMember(Nonnull element) { + switch (element->kind()) { + case ElementKind::BaseElement: + case ElementKind::PositionalElement: return true; - default: - return false; + case ElementKind::NamedElement: + const auto nom_element = cast(element); + if (!nom_element->declaration()) { + // This is a struct field. + return true; + } + Nonnull declaration = *nom_element->declaration(); + switch (declaration->kind()) { + case DeclarationKind::FunctionDeclaration: + return cast(declaration)->is_method(); + case DeclarationKind::VariableDeclaration: + return true; + default: + return false; + } } } @@ -2555,7 +2562,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, const auto& struct_type = cast(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(&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 e, auto [member_type, member, member_t_class] = res.value(); Nonnull field_type = Substitute(member_t_class->bindings(), member_type); - access.set_member(Member(member)); + access.set_member(arena_->New(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 e, const Value& member_type = result.member->static_type(); Nonnull inst_member_type = Substitute(bindings, &member_type); - access.set_member(Member(result.member)); + access.set_member(arena_->New(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(result.member)) { @@ -2700,17 +2707,17 @@ auto TypeChecker::TypeCheckExp(Nonnull e, CARBON_ASSIGN_OR_RETURN(Nonnull impl, impl_scope.Resolve(result.interface, type, e->source_loc(), *this)); - access.set_member(Member(result.member)); + access.set_member(arena_->New(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(Member(result.member))); + arena_->New(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 e, case Value::Kind::StructType: { for (const auto& field : cast(type)->fields()) { if (access.member_name() == field.name) { - access.set_member(Member(&field)); + access.set_member(arena_->New(&field)); access.set_static_type( - arena_->New(Member(&field))); + arena_->New(NamedElement(&field))); access.set_value_category(ValueCategory::Let); return Success(); } @@ -2769,8 +2776,9 @@ auto TypeChecker::TypeCheckExp(Nonnull e, substituted_parameter_type, &choice); // TODO: Should there be a Declaration corresponding to each // choice type alternative? - access.set_member(Member(arena_->New( - NamedValue{access.member_name(), type}))); + access.set_member( + arena_->New(arena_->New( + 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 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(member)); switch (member->kind()) { case DeclarationKind::FunctionDeclaration: { const auto& func = cast(*member); @@ -2801,7 +2809,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, break; } access.set_static_type( - arena_->New(Member(member))); + arena_->New(NamedElement(member))); access.set_value_category(ValueCategory::Let); return Success(); } else { @@ -2817,10 +2825,10 @@ auto TypeChecker::TypeCheckExp(Nonnull e, ConstraintLookupResult result, LookupInConstraint(e->source_loc(), "member access", type, access.member_name())); - access.set_member(Member(result.member)); + access.set_member(arena_->New(result.member)); access.set_found_in_interface(result.interface); access.set_static_type( - arena_->New(Member(result.member))); + arena_->New(NamedElement(result.member))); access.set_value_category(ValueCategory::Let); return Success(); } @@ -2853,7 +2861,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, InterpExp(&access.path(), arena_, trace_stream_)); const auto& member_name = cast(*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> base_type = member_name.base_type(); diff --git a/explorer/interpreter/value.cpp b/explorer/interpreter/value.cpp index a26126271ccf..1773a625f7e5 100644 --- a/explorer/interpreter/value.cpp +++ b/explorer/interpreter/value.cpp @@ -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 object, return std::nullopt; } -static auto GetPositionalMember(Nonnull v, - const FieldPath::Component& field, - SourceLocation source_loc) +static auto GetBaseElement(Nonnull class_value, + SourceLocation source_loc) -> ErrorOr> { - switch (v->kind()) { - case Value::Kind::TupleValue: { - const auto& tuple = cast(*v); - const auto index = field.member().index(); - if (index < 0 || index >= static_cast(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(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, Nonnull v, - const FieldPath::Component& field, - SourceLocation source_loc, - Nonnull me_value) +static auto GetPositionalElement(Nonnull tuple, + const ElementPath::Component& path_comp, + SourceLocation source_loc) -> ErrorOr> { - const auto f = field.member().name(); + CARBON_CHECK(path_comp.element()->kind() == ElementKind::PositionalElement) + << "Invalid non-tuple member"; + const auto* tuple_element = cast(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, Nonnull v, + const ElementPath::Component& field, + SourceLocation source_loc, + Nonnull me_value) + -> ErrorOr> { + CARBON_CHECK(field.element()->kind() == ElementKind::NamedElement) + << "Invalid element, expecting NamedElement"; + const auto* member = cast(field.element()); + const auto f = member->name(); if (field.witness().has_value()) { const auto* witness = cast(*field.witness()); // Associated constants. if (const auto* assoc_const = dyn_cast_or_null( - 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(v, *field.interface(), assoc_const, @@ -169,35 +179,50 @@ static auto GetNamedMember(Nonnull arena, Nonnull 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, Nonnull v, - const FieldPath::Component& field, - SourceLocation source_loc, Nonnull me_value) +static auto GetElement(Nonnull arena, Nonnull v, + const ElementPath::Component& path_comp, + SourceLocation source_loc, + Nonnull me_value) -> ErrorOr> { - 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(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(v)) { + return GetBaseElement(class_value, source_loc); + } else { + CARBON_FATAL() << "Invalid value for base element"; + } + } } -auto Value::GetMember(Nonnull arena, const FieldPath& path, - SourceLocation source_loc, - Nonnull me_value) const +auto Value::GetElement(Nonnull arena, const ElementPath& path, + SourceLocation source_loc, + Nonnull me_value) const -> ErrorOr> { Nonnull 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, Nonnull value, - std::vector::const_iterator path_begin, - std::vector::const_iterator path_end, + std::vector::const_iterator path_begin, + std::vector::const_iterator path_end, Nonnull field_value, SourceLocation source_loc) -> ErrorOr> { 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> elements = cast(*value).elements(); - const auto index = (*path_begin).member().index(); - if (index < 0 || index >= static_cast(elements.size())) { + const size_t index = + cast((*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, const FieldPath& path, +auto Value::SetField(Nonnull arena, const ElementPath& path, Nonnull field_value, SourceLocation source_loc) const -> ErrorOr> { diff --git a/explorer/interpreter/value.h b/explorer/interpreter/value.h index e4463947a579..4a145d2e6d51 100644 --- a/explorer/interpreter/value.h +++ b/explorer/interpreter/value.h @@ -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, const FieldPath& path, - SourceLocation source_loc, - Nonnull me_value) const + auto GetElement(Nonnull arena, const ElementPath& path, + SourceLocation source_loc, + Nonnull me_value) const -> ErrorOr>; // 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, const FieldPath& path, + auto SetField(Nonnull arena, const ElementPath& path, Nonnull field_value, SourceLocation source_loc) const -> ErrorOr>; @@ -1139,14 +1139,13 @@ class MemberName : public Value { public: MemberName(std::optional> base_type, std::optional> 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> base_type_; std::optional> 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.