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

Conversation

zygoloid
Copy link
Contributor

This is intended to be used for template instantiation, but for now takes no stance as to what it's cloning.

This is tested by parsing and cloning all of our test files, and checking that the result of converting each AST to proto is the same as the result of cloning and then converting each AST to proto.

This is intended to be used for template instantiation, but for now
takes no stance as to what it's cloning.
@zygoloid zygoloid requested a review from jonmeow March 20, 2023 22:15
@github-actions github-actions bot added the explorer Action items related to Carbon explorer code label Mar 20, 2023
@zygoloid zygoloid changed the title AST cloning mechanism. AST cloning mechanism Mar 20, 2023
Comment on lines +321 to +333
print("void CloneImpl(Arena& arena, CloneContext& context,")
print(" const AstNode& node, AstNode** result) {")
print(" switch(node.kind()) {")
for node in classes.values():
if node.kind == Class.Kind.CONCRETE and node.Root().name == "AstNode":
print(f" case AstNodeKind::{node.name}:")
print(
f" return arena.New<{node.name}>("
+ "Arena::WriteAddressTo{result}, context, "
+ f"static_cast<const {node.name}&>(node));"
)
print(" }")
print("}\n")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I admit, reading this, I wonder if it'd be better to emit a .def file with an x-macro, and then have this be a source file. I think it'd avoid the ast_headers dependencies you're adding, and may be a little more readable as an end result. I'll leave that to you though if you want to make that change.

(I've been mulling if gen_rtti could be replaced with x-macros completely, which may be affecting my mindset)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into this as a follow-up.

Comment on lines 34 to 43
// Clone the given node, and remember the mapping from the original to the
// new node for remapping.
auto Clone(Nonnull<const AstNode*> node) -> Nonnull<AstNode*>;

// Clone the given value, replacing references to cloned local declarations
// with references to the copies.
auto Clone(Nonnull<const Value*> value) -> Nonnull<Value*>;

// Clone the given element reference.
auto Clone(Nonnull<const Element*> elem) -> Nonnull<Element*>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be private? I'm wondering, as a change, if you could do something like (requiring renaming these to ClonePointer):

  template <typename T, typename = std::enable_if_t<
    std::is_convertible_v<const T*, const AstNode*> ||
    std::is_convertible_v<const T*, const Value*> ||
    std::is_convertible_v<const T*, const Element*>>>
  auto Clone(Nonnull<const T*> node) -> Nonnull<T*> {
    return static_cast<T*>(ClonePointer(node));
  }

or even:

  template<typename T, typename = std::enable_if_t<
    std::is_invocable_v<decltype(&CloneContext::ClonePointer), CloneContext&, const T*>>> 
  auto Clone(Nonnull<const T*> node) -> Nonnull<T*> {
    return static_cast<T*>(ClonePointer(node));
  }

(may have the syntax a little off)

The main benefit of this that I see is it feels like a simplification of the below Clone body. But I think with everything named Clone, that'd be self-recursive.

Note, that does slip away a little from your current approach of using overloads for everything, so I understand if you don't want it, but the way that you're currently casting pointers to their parent type to get the explicit overload you want seems like it increases the cost of understanding.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done something along these lines.

// that might refer to something being cloned or might refer to the original
// object. The returned node might not be fully constructed and should not be
// inspected.
auto Remap(Nonnull<AstNode*> node) -> Nonnull<AstNode*> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to double-check, where is this used from? I'm wondering if it's necessary versus putting the implementation into Remap(Nonnull<T*> node)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Folded the two functions together.

explorer/ast/clone_context.h Show resolved Hide resolved
Comment on lines 155 to 157
// Clone the given node if it's not already been cloned. This should be used
// very sparingly, in cases where ownership is unclear.
void MaybeClone(Nonnull<const AstNode*> node);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used outside of the cpp file? Maybe CloneValueTransform could be a member class instead of separated (only forward declared in the header), so that this could be private instead of public, preventing external use?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made private.

Copy link
Contributor

@jonmeow jonmeow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@zygoloid zygoloid merged commit 782bd87 into carbon-language:trunk Mar 21, 2023
zygoloid added a commit that referenced this pull request Mar 22, 2023
The strategy that we use for now to support template instantiation is to check the impl declaration as if it were a generic, but to defer all checking of the impl definition until we see a use in which all template parameters have arguments. At that point, we clone the impl definition and type-check the whole thing, with constant values set on the template parameters corresponding to the given arguments.

No caching of template instantiations is performed yet; each time we form a reference to a template instantiation, we instantiate it afresh. We also don't implement the name lookup rule from #949 yet; lookups during template instantiation look only in the actual type and not in the constraint.

Depends on #2699
zygoloid added a commit that referenced this pull request Mar 30, 2023
This keeps all of our C++ code written in C++ source files, and avoids the increasing size of the RTTI generation script we'd started to see in #2699.

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
@zygoloid zygoloid deleted the explorer-clone branch October 4, 2023 23:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
explorer Action items related to Carbon explorer code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants