Files
carbon-lang/explorer/ast/element_path.h
T
Jon Ross-Perkins 53af8f04b2 Provide a Printable CRTP parent to replace HasPrintable templates. (#3166)
With the toolchain splitting namespaces, ostream.h's `operator<<`
templates aren't reliably found with name lookup, likely due to the loss
of associated namespaces (zygoloid commented on this at
https://github.com/carbon-language/carbon-lang/pull/3161#discussion_r1307941999).
This is especially a barrier to moving the lex files into `Carbon::Lex`;
versus other parts of the toolchain, they contain more printable types
which are used cross-namespace, including `Carbon::Testing`. As a
consequence, I'm looking at migrating ostream.h to a more reliable
approach that doesn't rely as much on everything being in the `Carbon`
namespace.
2023-08-30 21:32:19 +00:00

143 lines
5.1 KiB
C++

// 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_PATH_H_
#define CARBON_EXPLORER_AST_ELEMENT_PATH_H_
#include <algorithm>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "common/check.h"
#include "common/ostream.h"
#include "explorer/ast/element.h"
#include "explorer/ast/value_node.h"
#include "llvm/Support/Compiler.h"
namespace Carbon {
class InterfaceType;
class Witness;
// 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 ElementPath is used, not
// by the ElementPath itself.
//
// 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 ElementPath
// refers to the initial Value itself.
class ElementPath : public Printable<ElementPath> {
public:
// Constructs an empty ElementPath.
ElementPath() = default;
// 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 Printable<Component> {
public:
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)
: element_(element), interface_(interface), witness_(witness) {}
inline friend auto operator==(const Component& lhs, const Component& rhs)
-> bool {
return lhs.element_ == rhs.element_ && lhs.interface_ == rhs.interface_ &&
lhs.witness_ == rhs.witness_;
}
inline friend auto hash_value(const Component& component)
-> llvm::hash_code {
return llvm::hash_combine(component.element_, component.interface_,
component.witness_);
}
auto element() const -> Nonnull<const Element*> { return element_; }
auto IsNamed(std::string_view name) const -> bool {
return element_->IsNamed(name);
}
auto interface() const -> std::optional<Nonnull<const InterfaceType*>> {
return interface_;
}
auto witness() const -> std::optional<Nonnull<const Witness*>> {
return witness_;
}
void Print(llvm::raw_ostream& out) const { return element_->Print(out); }
private:
Nonnull<const Element*> element_;
std::optional<Nonnull<const InterfaceType*>> interface_;
std::optional<Nonnull<const Witness*>> witness_;
};
// Constructs a ElementPath consisting of a single step.
explicit ElementPath(Nonnull<const Element*> element)
: components_({Component(element)}) {}
explicit ElementPath(const Component& f) : components_({f}) {}
ElementPath(const ElementPath&) = default;
ElementPath(ElementPath&&) = default;
auto operator=(const ElementPath&) -> ElementPath& = default;
auto operator=(ElementPath&&) -> ElementPath& = default;
inline friend auto operator==(const ElementPath& lhs, const ElementPath& rhs)
-> bool {
return lhs.components_ == rhs.components_;
}
inline friend auto hash_value(const ElementPath& path) -> llvm::hash_code {
return llvm::hash_combine_range(path.components_.begin(),
path.components_.end());
}
// Returns whether *this is empty.
auto IsEmpty() const -> bool { return components_.empty(); }
// Appends `element` to the end of *this.
auto Append(Nonnull<const Element*> element) -> void {
components_.push_back(Component(element));
}
// Removes all trailing `BaseElement`s, errors if there are no base elements.
auto RemoveTrailingBaseElements() -> void {
CARBON_CHECK(!components_.empty() && components_.back().element()->kind() ==
ElementKind::BaseElement)
<< "No base elements to remove.";
const auto r_it = std::find_if(
components_.rbegin(), components_.rend(), [](const Component& c) {
return c.element()->kind() != ElementKind::BaseElement;
});
components_.erase(r_it.base(), components_.end());
}
void Print(llvm::raw_ostream& out) const {
for (const Component& component : components_) {
out << "." << component;
}
}
private:
// 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;
friend class Heap;
std::vector<Component> components_;
};
} // namespace Carbon
#endif // CARBON_EXPLORER_AST_ELEMENT_PATH_H_