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

derive: Add base for builtin derives and #[derive(Clone)] #2216

Merged
merged 2 commits into from Jun 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions gcc/rust/Make-lang.in
Expand Up @@ -87,6 +87,9 @@ GRS_OBJS = \
rust/rust-macro-expand.o \
rust/rust-cfg-strip.o \
rust/rust-expand-visitor.o \
rust/rust-ast-builder.o \
rust/rust-derive.o \
rust/rust-derive-clone.o \
rust/rust-macro-invoc-lexer.o \
rust/rust-macro-substitute-ctx.o \
rust/rust-macro-builtins.o \
Expand Down
122 changes: 122 additions & 0 deletions gcc/rust/ast/rust-ast-builder.cc
@@ -0,0 +1,122 @@
// Copyright (C) 2020-2023 Free Software Foundation, Inc.

// This file is part of GCC.

// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.

// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.

// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.

#include "rust-ast-builder.h"
#include "rust-ast-full.h"

namespace Rust {
namespace AST {

std::unique_ptr<Expr>
AstBuilder::call (std::unique_ptr<Expr> &&path,
std::vector<std::unique_ptr<Expr>> &&args)
{
return std::unique_ptr<Expr> (
new CallExpr (std::move (path), std::move (args), {}, loc));
}

std::unique_ptr<Expr>
AstBuilder::identifier (std::string name)
{
return std::unique_ptr<Expr> (new IdentifierExpr (name, {}, loc));
}

std::unique_ptr<Expr>
AstBuilder::tuple_idx (std::string receiver, int idx)
{
return std::unique_ptr<Expr> (
new TupleIndexExpr (identifier (receiver), idx, {}, loc));
}

FunctionQualifiers
AstBuilder::fn_qualifiers ()
{
return FunctionQualifiers (loc, AsyncConstStatus::NONE, false);
}

PathExprSegment
AstBuilder::path_segment (std::string seg)
{
return PathExprSegment (PathIdentSegment (seg, loc), loc);
}

std::unique_ptr<TypePathSegment>
AstBuilder::type_path_segment (std::string seg)
{
return std::unique_ptr<TypePathSegment> (
new TypePathSegment (seg, false, loc));
}

std::unique_ptr<Type>
AstBuilder::single_type_path (std::string type)
{
auto segments = std::vector<std::unique_ptr<TypePathSegment>> ();
segments.emplace_back (type_path_segment (type));

return std::unique_ptr<Type> (new TypePath (std::move (segments), loc));
}

PathInExpression
AstBuilder::path_in_expression (std::vector<std::string> &&segments)
{
auto path_segments = std::vector<PathExprSegment> ();
for (auto &seg : segments)
path_segments.emplace_back (path_segment (seg));

return PathInExpression (std::move (path_segments), {}, loc);
}

std::unique_ptr<Expr>
AstBuilder::struct_expr_struct (std::string struct_name)
{
return std::unique_ptr<Expr> (
new StructExprStruct (path_in_expression ({struct_name}), {}, {}, loc));
}

std::unique_ptr<Expr>
AstBuilder::block (std::vector<std::unique_ptr<Stmt>> &&stmts,
std::unique_ptr<Expr> &&tail_expr)
{
return std::unique_ptr<Expr> (
new BlockExpr (std::move (stmts), std::move (tail_expr), {}, {}, loc, loc));
}

std::unique_ptr<Stmt>
AstBuilder::let (std::unique_ptr<Pattern> pattern, std::unique_ptr<Type> type,
std::unique_ptr<Expr> init)
{
return std::unique_ptr<Stmt> (
new LetStmt (/* needs a pattern here, not just a name */ nullptr,
std::move (init), std::move (type), {}, loc));
}

std::unique_ptr<Expr>
AstBuilder::ref (std::unique_ptr<Expr> &&of, bool mut)
{
return std::unique_ptr<Expr> (
new BorrowExpr (std::move (of), mut, /* is double */ false, {}, loc));
}

std::unique_ptr<Expr>
AstBuilder::deref (std::unique_ptr<Expr> &&of)
{
return std::unique_ptr<Expr> (new DereferenceExpr (std::move (of), {}, loc));
}

} // namespace AST
} // namespace Rust
98 changes: 98 additions & 0 deletions gcc/rust/ast/rust-ast-builder.h
@@ -0,0 +1,98 @@
// Copyright (C) 2020-2023 Free Software Foundation, Inc.

// This file is part of GCC.

// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.

// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.

// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.

#ifndef AST_BUILDER_H
#define AST_BUILDER_H

#include "rust-ast-full.h"

namespace Rust {
namespace AST {

// TODO: Use this builder when expanding regular macros
/* Builder class with helper methods to create AST nodes. This builder is
* tailored towards generating multiple AST nodes from a single location, and
* may not be suitable to other purposes */
class AstBuilder
{
public:
AstBuilder (Location loc) : loc (loc) {}

/* Create an identifier expression (`variable`) */
std::unique_ptr<Expr> identifier (std::string name);

/* Create a tuple index expression (`receiver.0`) */
std::unique_ptr<Expr> tuple_idx (std::string receiver, int idx);

/* Create a reference to an expression (`&of`) */
std::unique_ptr<Expr> ref (std::unique_ptr<Expr> &&of, bool mut = false);

/* Create a dereference of an expression (`*of`) */
std::unique_ptr<Expr> deref (std::unique_ptr<Expr> &&of);

/* Create a block with an optional tail expression */
std::unique_ptr<Expr> block (std::vector<std::unique_ptr<Stmt>> &&stmts,
std::unique_ptr<Expr> &&tail_expr = nullptr);

/* Create a let binding with an optional type and initializer (`let <name> :
* <type> = <init>`) */
std::unique_ptr<Stmt> let (std::unique_ptr<Pattern> pattern,
std::unique_ptr<Type> type = nullptr,
std::unique_ptr<Expr> init = nullptr);

/**
* Create a call expression to a function, struct or enum variant, given its
* arguments (`path(arg0, arg1, arg2)`)
*/
std::unique_ptr<Expr> call (std::unique_ptr<Expr> &&path,
std::vector<std::unique_ptr<Expr>> &&args);

/* Empty function qualifiers, with no specific qualifiers */
FunctionQualifiers fn_qualifiers ();

/* Create a single path segment from one string */
PathExprSegment path_segment (std::string seg);

/* And similarly for type path segments */
std::unique_ptr<TypePathSegment> type_path_segment (std::string seg);

/* Create a Type from a single string - the most basic kind of type in our AST
*/
std::unique_ptr<Type> single_type_path (std::string type);

/**
* Create a path in expression from multiple segments (`Clone::clone`). You
* do not need to separate the segments using `::`, you can simply provide a
* vector of strings to the functions which will get turned into path segments
*/
PathInExpression path_in_expression (std::vector<std::string> &&segments);

/* Create a struct expression for unit structs (`S`) */
std::unique_ptr<Expr> struct_expr_struct (std::string struct_name);

private:
/**
* Location of the generated AST nodes
*/
Location loc;
};

} // namespace AST
} // namespace Rust

#endif // AST_BUILDER_H
152 changes: 152 additions & 0 deletions gcc/rust/expand/rust-derive-clone.cc
@@ -0,0 +1,152 @@
// Copyright (C) 2020-2023 Free Software Foundation, Inc.

// This file is part of GCC.

// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.

// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.

// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.

#include "rust-derive-clone.h"

namespace Rust {
namespace AST {

std::unique_ptr<Expr>
DeriveClone::clone_call (std::unique_ptr<Expr> &&to_clone)
{
// $crate::core::clone::Clone::clone for the fully qualified path - we don't
// link with `core` yet so that might be an issue. Use `Clone::clone` for now?
// TODO: Factor this function inside the DeriveAccumulator
auto path = std::unique_ptr<Expr> (
new PathInExpression (builder.path_in_expression ({"Clone", "clone"})));

auto args = std::vector<std::unique_ptr<Expr>> ();
args.emplace_back (std::move (to_clone));

return builder.call (std::move (path), std::move (args));
}

/**
* Create the actual "clone" function of the implementation, so
*
* fn clone(&self) -> Self { <clone_expr> }
*
*/
std::unique_ptr<TraitImplItem>
DeriveClone::clone_fn (std::unique_ptr<Expr> &&clone_expr)
{
auto block = std::unique_ptr<BlockExpr> (
new BlockExpr ({}, std::move (clone_expr), {}, {}, loc, loc));
auto big_self_type = builder.single_type_path ("Self");

return std::unique_ptr<TraitImplItem> (
new Method ("clone", builder.fn_qualifiers (), /* generics */ {},
SelfParam (Lifetime::error (), /* is_mut */ false, loc),
/* function params */ {}, std::move (big_self_type),
WhereClause::create_empty (), std::move (block),
Visibility::create_private (), {}, loc));
}

/**
* Create the Clone trait implementation for a type
*
* impl Clone for <type> {
* <clone_fn>
* }
*
*/
std::unique_ptr<Item>
DeriveClone::clone_impl (std::unique_ptr<TraitImplItem> &&clone_fn,
std::string name)
{
// should that be `$crate::core::clone::Clone` instead?
auto segments = std::vector<std::unique_ptr<TypePathSegment>> ();
segments.emplace_back (builder.type_path_segment ("Clone"));
auto clone = TypePath (std::move (segments), loc);

auto trait_items = std::vector<std::unique_ptr<TraitImplItem>> ();
trait_items.emplace_back (std::move (clone_fn));

return std::unique_ptr<Item> (
new TraitImpl (clone, /* unsafe */ false,
/* exclam */ false, std::move (trait_items),
/* generics */ {}, builder.single_type_path (name),
WhereClause::create_empty (), Visibility::create_private (),
{}, {}, loc));
}

// TODO: Create new `make_qualified_call` helper function

DeriveClone::DeriveClone (Location loc)
: loc (loc), expanded (nullptr), builder (AstBuilder (loc))
{}

std::unique_ptr<AST::Item>
DeriveClone::go (Item &item)
{
item.accept_vis (*this);

rust_assert (expanded);

return std::move (expanded);
}

void
DeriveClone::visit_tuple (TupleStruct &item)
{
auto cloned_fields = std::vector<std::unique_ptr<Expr>> ();

for (size_t idx = 0; idx < item.get_fields ().size (); idx++)
cloned_fields.emplace_back (
clone_call (builder.ref (builder.tuple_idx ("self", idx))));

auto path = std::unique_ptr<Expr> (new PathInExpression (
builder.path_in_expression ({item.get_identifier ()})));
auto constructor = builder.call (std::move (path), std::move (cloned_fields));

expanded
= clone_impl (clone_fn (std::move (constructor)), item.get_identifier ());
}

void
DeriveClone::visit_struct (StructStruct &item)
{
if (item.is_unit_struct ())
{
auto unit_ctor = builder.struct_expr_struct (item.get_struct_name ());
expanded = clone_impl (clone_fn (std::move (unit_ctor)),
item.get_struct_name ());
}
else
{
rust_sorry_at (item.get_locus (), "cannot derive %qs for these items yet",
"Clone");
}
}

void
DeriveClone::visit_enum (Enum &item)
{
rust_sorry_at (item.get_locus (), "cannot derive %qs for these items yet",
"Clone");
}

void
DeriveClone::visit_union (Union &item)
{
rust_sorry_at (item.get_locus (), "cannot derive %qs for these items yet",
"Clone");
}

} // namespace AST
} // namespace Rust