Skip to content

Commit

Permalink
expansion: Add base for deriving builtin macros (Clone, Copy...)
Browse files Browse the repository at this point in the history
gcc/rust/ChangeLog:

	* Make-lang.in: Add new object files to Makefile.
	* expand/rust-expand-visitor.cc (is_derive): Move function.
	(is_builtin): Likewise.
	(get_traits_to_derive): New function.
	(derive_item): Likewise.
	(ExpandVisitor::visit): Visit item/statements containers.
	(ExpandVisitor::visit_outer_attrs): Take a reference on the attribute
	instead of a copy.
	(ExpandVisitor::is_derive): Deleted function.
	(ExpandVisitor::is_builtin): Likewise.
	* expand/rust-expand-visitor.h (RUST_EXPAND_VISITOR_H): Add missing #ifdef
	guards.
	(is_derive): Declare function.
	(is_builtin): Likewise.
	* expand/rust-macro-builtins.cc (builtin_macro_from_string): Use new
	MacroBuiltin::builtins map.
	(make_macro_path_str): Likewise.
	* expand/rust-macro-builtins.h (enum class): Add builtin derive macros.
	* expand/rust-derive-clone.cc: New file.
	* expand/rust-derive-clone.h: New file.
	* expand/rust-derive.cc: New file.
	* expand/rust-derive.h: New file.

gcc/testsuite/ChangeLog:

	* rust/compile/macro43.rs: Fix test with new derive macros.
	* rust/compile/derive_macro1.rs: New test.
	* rust/compile/derive_macro3.rs: New test.
	* rust/execute/torture/derive_macro1.rs: New test.
	* rust/execute/torture/derive_macro3.rs: New test.
  • Loading branch information
CohenArthur committed May 25, 2023
1 parent 65b7f4f commit e5079a7
Show file tree
Hide file tree
Showing 14 changed files with 828 additions and 58 deletions.
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
153 changes: 153 additions & 0 deletions gcc/rust/expand/rust-derive-clone.cc
@@ -0,0 +1,153 @@
// 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: `has_opening_scope_resolution`? Does it have it?
// 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
77 changes: 77 additions & 0 deletions gcc/rust/expand/rust-derive-clone.h
@@ -0,0 +1,77 @@
// 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 RUST_DERIVE_CLONE_H
#define RUST_DERIVE_CLONE_H

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

namespace Rust {
namespace AST {

class DeriveClone : DeriveVisitor
{
public:
DeriveClone (Location loc);

std::unique_ptr<AST::Item> go (Item &item);

private:
Location loc;
std::unique_ptr<AST::Item> expanded;
AstBuilder builder;

/**
* Create a call to "clone". For now, this creates a call to
* `Clone::clone`, but should ultimately call into
* `::core::clone::Clone::clone`
*
* Clone::clone(<to_clone>)
*/
std::unique_ptr<Expr> clone_call (std::unique_ptr<Expr> &&to_clone);

/**
* Create the actual "clone" function of the implementation, so
*
* fn clone(&self) -> Self { <clone_expr> }
*
*/
std::unique_ptr<TraitImplItem> clone_fn (std::unique_ptr<Expr> &&clone_expr);

/**
* Create the Clone trait implementation for a type
*
* impl Clone for <type> {
* <clone_fn>
* }
*
*/
std::unique_ptr<Item> clone_impl (std::unique_ptr<TraitImplItem> &&clone_fn,
std::string name);

virtual void visit_struct (StructStruct &item);
virtual void visit_tuple (TupleStruct &item);
virtual void visit_enum (Enum &item);
virtual void visit_union (Union &item);
};

} // namespace AST
} // namespace Rust

#endif // ! RUST_DERIVE_CLONE_H
48 changes: 48 additions & 0 deletions gcc/rust/expand/rust-derive.cc
@@ -0,0 +1,48 @@
// 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.h"
#include "rust-derive-clone.h"

namespace Rust {
namespace AST {

std::unique_ptr<Item>
DeriveVisitor::derive (Item &item, const Attribute &attr,
BuiltinMacro to_derive)
{
switch (to_derive)
{
case BuiltinMacro::Clone:
return DeriveClone (attr.get_locus ()).go (item);
case BuiltinMacro::Copy:
case BuiltinMacro::Debug:
case BuiltinMacro::Default:
case BuiltinMacro::Eq:
case BuiltinMacro::PartialEq:
case BuiltinMacro::Ord:
case BuiltinMacro::PartialOrd:
case BuiltinMacro::Hash:
default:
rust_sorry_at (attr.get_locus (), "uninmplemented builtin derive macro");
return nullptr;
};
}

} // namespace AST
} // namespace Rust

0 comments on commit e5079a7

Please sign in to comment.