Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AST cloning mechanism #2699

Merged
merged 3 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions common/fuzzing/carbon.proto
Original file line number Diff line number Diff line change
Expand Up @@ -430,9 +430,10 @@ message ImplDeclaration {
}

optional ImplKind kind = 1;
optional Expression impl_type = 2;
optional Expression interface = 3;
repeated Declaration members = 4;
repeated GenericBinding deduced_parameters = 2;
optional Expression impl_type = 3;
optional Expression interface = 4;
repeated Declaration members = 5;
}

message MatchFirstDeclaration {
Expand Down
9 changes: 9 additions & 0 deletions common/fuzzing/proto_to_carbon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,15 @@ static auto DeclarationToCarbon(const Fuzzing::Declaration& declaration,
out << "external ";
}
out << "impl ";
if (!impl.deduced_parameters().empty()) {
out << "forall [";
llvm::ListSeparator sep;
for (const Fuzzing::GenericBinding& p : impl.deduced_parameters()) {
out << sep;
GenericBindingToCarbon(p, out);
}
out << "]";
}
ExpressionToCarbon(impl.impl_type(), out);
out << " as ";
ExpressionToCarbon(impl.interface(), out);
Expand Down
61 changes: 26 additions & 35 deletions explorer/ast/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,60 @@

package(default_visibility = ["//explorer:__subpackages__"])

AST_HDRS = [
"address.h",
"ast.h",
"ast_node.h",
"bindings.h",
"clone_context.h",
"declaration.h",
"element.h",
"element_path.h",
"expression.h",
"impl_binding.h",
"pattern.h",
"return_term.h",
"statement.h",
"value.h",
"value_node.h",
"value_transform.h",
]

genrule(
name = "ast_rtti",
srcs = ["ast_rtti.txt"],
srcs = ["ast_rtti.txt"] + AST_HDRS,
outs = [
"ast_rtti.h",
"ast_rtti.cpp",
],
cmd = "./$(location //explorer:gen_rtti)" +
" $(location ast_rtti.txt)" +
" $(location ast_rtti.h) $(location ast_rtti.cpp)" +
" $(rootpath ast_rtti.h)",
" $(rootpath ast_rtti.h)" +
"".join([" $(rootpath " + f + ")" for f in AST_HDRS]),
tools = ["//explorer:gen_rtti"],
)

cc_library(
name = "ast_node",
name = "ast",
srcs = [
"ast_node.cpp",
"ast_rtti.cpp",
],
hdrs = [
"ast_node.h",
"ast_rtti.h",
],
deps = [
"//explorer/common:source_location",
"@llvm-project//llvm:Support",
],
)

cc_library(
name = "ast",
srcs = [
"bindings.cpp",
"clone_context.cpp",
"declaration.cpp",
"element.cpp",
"expression.cpp",
"impl_binding.cpp",
"pattern.cpp",
"statement.cpp",
"value.cpp",
],
hdrs = [
"address.h",
"ast.h",
"bindings.h",
"declaration.h",
"element.h",
"element_path.h",
"expression.h",
"impl_binding.h",
"pattern.h",
"return_term.h",
"statement.h",
"value.h",
"value_node.h",
"value_transform.h",
],
hdrs = AST_HDRS + ["ast_rtti.h"],
textual_hdrs = [
"value_kinds.def",
],
deps = [
":ast_node",
":library_name",
":paren_contents",
":value_category",
Expand All @@ -91,7 +83,6 @@ cc_library(
hdrs = ["ast_test_matchers.h"],
deps = [
":ast",
":ast_node",
"@com_google_googletest//:gtest",
"@llvm-project//llvm:Support",
],
Expand Down
14 changes: 14 additions & 0 deletions explorer/ast/ast_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Carbon {

class CloneContext;

// Base class for all nodes in the AST.
//
// Every class derived from this class must be listed in ast_rtti.txt. See
Expand All @@ -35,6 +37,14 @@ namespace Carbon {
// The definitions of `InheritsFromFoo` and `FooKind` are generated from
// ast_rtti.txt, and are implicitly provided by this header.
//
// Every AST node is expected to provide a cloning constructor:
//
// explicit MyAstNode(CloneContext& context, const MyAstNode& other);
//
// The cloning constructor should behave like a copy constructor, but pointers
// to other AST nodes should be passed through context.Clone to clone the
// referenced object.
//
// TODO: To support generic traversal, add children() method, and ensure that
// all AstNodes are reachable from a root AstNode.
class AstNode {
Expand Down Expand Up @@ -65,6 +75,10 @@ class AstNode {
explicit AstNode(AstNodeKind kind, SourceLocation source_loc)
: kind_(kind), source_loc_(source_loc) {}

// Clone this AstNode.
explicit AstNode(CloneContext& /*context*/, const AstNode& other)
: kind_(other.kind_), source_loc_(other.source_loc_) {}

// Equivalent to kind(), but will not be hidden by `kind()` methods of
// derived classes.
auto root_kind() const -> AstNodeKind { return kind_; }
Expand Down
10 changes: 10 additions & 0 deletions explorer/ast/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,19 @@
#include "common/error.h"
#include "explorer/ast/impl_binding.h"
#include "explorer/ast/pattern.h"
#include "explorer/ast/value.h"

namespace Carbon {

Bindings::Bindings(CloneContext& context, const Bindings& other) {
for (auto [binding, value] : other.args_) {
args_.insert({context.Remap(binding), context.Clone(value)});
}
for (auto [binding, value] : other.witnesses_) {
witnesses_.insert({context.Remap(binding), context.Clone(value)});
}
}

void Bindings::Add(Nonnull<const GenericBinding*> binding,
Nonnull<const Value*> value,
std::optional<Nonnull<const Value*>> witness) {
Expand Down
7 changes: 5 additions & 2 deletions explorer/ast/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <map>
#include <utility>

#include "explorer/ast/clone_context.h"
#include "explorer/common/nonnull.h"
#include "llvm/ADT/ArrayRef.h"

Expand Down Expand Up @@ -45,16 +46,18 @@ class Bindings {

// Create an instantiated set of bindings for use during evaluation,
// containing both arguments and witnesses.
Bindings(BindingMap args, ImplWitnessMap witnesses)
explicit Bindings(BindingMap args, ImplWitnessMap witnesses)
: args_(std::move(args)), witnesses_(std::move(witnesses)) {}

enum NoWitnessesTag { NoWitnesses };

// Create a set of bindings for use during type-checking, containing only the
// arguments but not the corresponding witnesses.
Bindings(BindingMap args, NoWitnessesTag /*unused*/)
explicit Bindings(BindingMap args, NoWitnessesTag /*unused*/)
: args_(std::move(args)) {}

explicit Bindings(CloneContext& context, const Bindings& other);

template <typename F>
auto Decompose(F f) const {
return f(args_, witnesses_);
Expand Down
92 changes: 92 additions & 0 deletions explorer/ast/clone_context.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// 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/clone_context.h"

#include "explorer/ast/ast_node.h"
#include "explorer/ast/value_transform.h"

namespace Carbon {

auto CloneContext::Clone(Nonnull<const AstNode*> node) -> Nonnull<AstNode*> {
auto [it, added] = nodes_.insert({node, nullptr});
CARBON_CHECK(added) << (it->second
? "node was cloned multiple times: "
: "node was remapped before it was cloned: ")
<< *node;

// The implementation is generated in ast_rtti.cpp.
CloneImpl(*arena_, *this, *node, &it->second);

// Cloning may have invalidated our iterator; redo lookup.
auto* result = nodes_[node];
CARBON_CHECK(result) << "CloneImpl didn't set the result pointer";
return result;
}

namespace {
class CloneValueTransform
: public ValueTransform<CloneValueTransform, NoOpUnwrapper> {
public:
CloneValueTransform(Nonnull<CloneContext*> context, Nonnull<Arena*> arena)
: ValueTransform(arena), context_(context) {}

using ValueTransform::operator();

// Transforming a pointer to an AstNode should remap the node. Values do not
// own the nodes they point to, apart from the exceptions handled below.
template <typename NodeT>
auto operator()(Nonnull<const NodeT*> node, int /*unused*/ = 0)
-> std::enable_if_t<std::is_base_of_v<AstNode, NodeT>,
Nonnull<const NodeT*>> {
return context_->Remap(node);
}

// Transforming a value node view should clone it. The value node view does
// not itself own the node it points to, so this is a shallow clone.
auto operator()(ValueNodeView value_node) -> ValueNodeView {
return context_->Clone(value_node);
}

// A FunctionType may or may not own its bindings.
auto operator()(Nonnull<const FunctionType*> fn_type)
-> Nonnull<const FunctionType*> {
for (auto* binding : fn_type->deduced_bindings()) {
context_->MaybeClone(binding);
}
for (auto [index, binding] : fn_type->generic_parameters()) {
context_->MaybeClone(binding);
}
return ValueTransform::operator()(fn_type);
}

// A ConstraintType owns its self binding, so we need to clone it.
auto operator()(Nonnull<const ConstraintType*> constraint)
-> Nonnull<const Value*> {
context_->Clone(constraint->self_binding());
return ValueTransform::operator()(constraint);
}

private:
Nonnull<CloneContext*> context_;
};
} // namespace

auto CloneContext::Clone(Nonnull<const Value*> value) -> Nonnull<Value*> {
return const_cast<Value*>(CloneValueTransform(this, arena_).Transform(value));
}

auto CloneContext::Clone(Nonnull<const Element*> elem) -> Nonnull<Element*> {
return const_cast<Element*>(
CloneValueTransform(this, arena_).Transform(elem));
}

void CloneContext::MaybeClone(Nonnull<const AstNode*> node) {
auto it = nodes_.find(node);
if (it == nodes_.end()) {
Clone(node);
}
}

} // namespace Carbon
Loading