aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/expand
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/rust/expand')
-rw-r--r--gcc/rust/expand/rust-cfg-strip.cc10
-rw-r--r--gcc/rust/expand/rust-derive-clone.cc320
-rw-r--r--gcc/rust/expand/rust-derive-clone.h17
-rw-r--r--gcc/rust/expand/rust-derive-copy.cc93
-rw-r--r--gcc/rust/expand/rust-derive-debug.cc121
-rw-r--r--gcc/rust/expand/rust-derive-debug.h55
-rw-r--r--gcc/rust/expand/rust-derive-default.cc172
-rw-r--r--gcc/rust/expand/rust-derive-default.h58
-rw-r--r--gcc/rust/expand/rust-derive-eq.cc216
-rw-r--r--gcc/rust/expand/rust-derive-eq.h82
-rw-r--r--gcc/rust/expand/rust-derive-hash.cc293
-rw-r--r--gcc/rust/expand/rust-derive-hash.h61
-rw-r--r--gcc/rust/expand/rust-derive-partial-eq.cc313
-rw-r--r--gcc/rust/expand/rust-derive-partial-eq.h85
-rw-r--r--gcc/rust/expand/rust-derive.cc102
-rw-r--r--gcc/rust/expand/rust-derive.h34
-rw-r--r--gcc/rust/expand/rust-expand-format-args.cc2
-rw-r--r--gcc/rust/expand/rust-expand-visitor.cc38
-rw-r--r--gcc/rust/expand/rust-expand-visitor.h1
-rw-r--r--gcc/rust/expand/rust-macro-builtins-asm.cc268
-rw-r--r--gcc/rust/expand/rust-macro-builtins-asm.h32
-rw-r--r--gcc/rust/expand/rust-macro-builtins-format-args.cc7
-rw-r--r--gcc/rust/expand/rust-macro-builtins-helpers.cc2
-rw-r--r--gcc/rust/expand/rust-macro-builtins-helpers.h1
-rw-r--r--gcc/rust/expand/rust-macro-builtins-include.cc1
-rw-r--r--gcc/rust/expand/rust-macro-builtins-log-debug.cc2
-rw-r--r--gcc/rust/expand/rust-macro-builtins-utility.cc83
-rw-r--r--gcc/rust/expand/rust-macro-builtins.cc17
-rw-r--r--gcc/rust/expand/rust-macro-builtins.h8
-rw-r--r--gcc/rust/expand/rust-macro-expand.cc32
-rw-r--r--gcc/rust/expand/rust-macro-expand.h3
-rw-r--r--gcc/rust/expand/rust-macro-substitute-ctx.cc51
-rw-r--r--gcc/rust/expand/rust-macro-substitute-ctx.h25
-rw-r--r--gcc/rust/expand/rust-token-tree-desugar.cc72
-rw-r--r--gcc/rust/expand/rust-token-tree-desugar.h55
35 files changed, 2495 insertions, 237 deletions
diff --git a/gcc/rust/expand/rust-cfg-strip.cc b/gcc/rust/expand/rust-cfg-strip.cc
index 4e6a8ac..a8c3ca5 100644
--- a/gcc/rust/expand/rust-cfg-strip.cc
+++ b/gcc/rust/expand/rust-cfg-strip.cc
@@ -19,6 +19,7 @@
#include "rust-cfg-strip.h"
#include "rust-ast-full.h"
#include "rust-ast-visitor.h"
+#include "rust-path.h"
#include "rust-session-manager.h"
#include "rust-attribute-values.h"
@@ -434,10 +435,13 @@ CfgStrip::visit (AST::PathInExpression &path)
return;
}
- for (auto &segment : path.get_segments ())
+ if (!path.is_lang_item ())
{
- if (segment.has_generic_args ())
- maybe_strip_generic_args (segment.get_generic_args ());
+ for (auto &segment : path.get_segments ())
+ {
+ if (segment.has_generic_args ())
+ maybe_strip_generic_args (segment.get_generic_args ());
+ }
}
}
diff --git a/gcc/rust/expand/rust-derive-clone.cc b/gcc/rust/expand/rust-derive-clone.cc
index 18436be..321fa00 100644
--- a/gcc/rust/expand/rust-derive-clone.cc
+++ b/gcc/rust/expand/rust-derive-clone.cc
@@ -17,7 +17,12 @@
// <http://www.gnu.org/licenses/>.
#include "rust-derive-clone.h"
+#include "rust-ast.h"
+#include "rust-expr.h"
#include "rust-item.h"
+#include "rust-path.h"
+#include "rust-pattern.h"
+#include "rust-system.h"
namespace Rust {
namespace AST {
@@ -28,6 +33,15 @@ 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
+
+ // Interestingly, later versions of Rust have a `clone_fn` lang item which
+ // corresponds to this. But because we are first targeting 1.49, we cannot use
+ // it yet. Once we target a new, more recent version of the language, we'll
+ // have figured out how to compile and distribute `core`, meaning we'll be
+ // able to directly call `::core::clone::Clone::clone()`
+
+ // Not sure how to call it properly in the meantime...
+
auto path = std::unique_ptr<Expr> (
new PathInExpression (builder.path_in_expression ({"Clone", "clone"})));
@@ -47,11 +61,10 @@ std::unique_ptr<AssociatedItem>
DeriveClone::clone_fn (std::unique_ptr<Expr> &&clone_expr)
{
auto block = std::unique_ptr<BlockExpr> (
- new BlockExpr ({}, std::move (clone_expr), {}, {}, AST::LoopLabel::error (),
- loc, loc));
+ new BlockExpr ({}, std::move (clone_expr), {}, {}, tl::nullopt, loc, loc));
auto big_self_type = builder.single_type_path ("Self");
- std::unique_ptr<SelfParam> self (new SelfParam (Lifetime::error (),
+ std::unique_ptr<SelfParam> self (new SelfParam (tl::nullopt,
/* is_mut */ false, loc));
std::vector<std::unique_ptr<Param>> params;
@@ -77,89 +90,19 @@ DeriveClone::clone_impl (
std::unique_ptr<AssociatedItem> &&clone_fn, std::string name,
const std::vector<std::unique_ptr<GenericParam>> &type_generics)
{
- // 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<AssociatedItem>> ();
- trait_items.emplace_back (std::move (clone_fn));
-
- // we need to build up the generics for this impl block which will be just a
- // clone of the types specified ones
- //
- // for example:
- //
- // #[derive(Clone)]
- // struct Be<T: Clone> { ... }
- //
- // we need to generate the impl block:
- //
- // impl<T: Clone> Clone for Be<T>
-
- std::vector<Lifetime> lifetime_args;
- std::vector<GenericArg> generic_args;
- std::vector<std::unique_ptr<GenericParam>> impl_generics;
- for (const auto &generic : type_generics)
- {
- switch (generic->get_kind ())
- {
- case GenericParam::Kind::Lifetime: {
- LifetimeParam &lifetime_param = (LifetimeParam &) *generic.get ();
-
- Lifetime l = builder.new_lifetime (lifetime_param.get_lifetime ());
- lifetime_args.push_back (std::move (l));
-
- auto impl_lifetime_param
- = builder.new_lifetime_param (lifetime_param);
- impl_generics.push_back (std::move (impl_lifetime_param));
- }
- break;
-
- case GenericParam::Kind::Type: {
- TypeParam &type_param = (TypeParam &) *generic.get ();
+ // we should have two of these, so we don't run into issues with
+ // two paths sharing a node id
+ auto clone_bound = builder.type_path (LangItem::Kind::CLONE);
+ auto clone_trait_path = builder.type_path (LangItem::Kind::CLONE);
- std::unique_ptr<Type> associated_type = builder.single_type_path (
- type_param.get_type_representation ().as_string ());
+ auto trait_items = vec (std::move (clone_fn));
- GenericArg type_arg
- = GenericArg::create_type (std::move (associated_type));
- generic_args.push_back (std::move (type_arg));
-
- auto impl_type_param = builder.new_type_param (type_param);
- impl_generics.push_back (std::move (impl_type_param));
- }
- break;
-
- case GenericParam::Kind::Const: {
- rust_unreachable ();
-
- // TODO
- // const ConstGenericParam *const_param
- // = (const ConstGenericParam *) generic.get ();
- // std::unique_ptr<Expr> const_expr = nullptr;
-
- // GenericArg type_arg
- // = GenericArg::create_const (std::move (const_expr));
- // generic_args.push_back (std::move (type_arg));
- }
- break;
- }
- }
+ auto generics = setup_impl_generics (name, type_generics,
+ builder.trait_bound (clone_bound));
- GenericArgs generic_args_for_self (lifetime_args, generic_args,
- {} /*binding args*/, loc);
- std::unique_ptr<Type> self_type_path
- = impl_generics.empty ()
- ? builder.single_type_path (name)
- : builder.single_generic_type_path (name, generic_args_for_self);
-
- return std::unique_ptr<Item> (
- new TraitImpl (clone, /* unsafe */ false,
- /* exclam */ false, std::move (trait_items),
- std::move (impl_generics), std::move (self_type_path),
- WhereClause::create_empty (), Visibility::create_private (),
- {}, {}, loc));
+ return builder.trait_impl (clone_trait_path, std::move (generics.self_type),
+ std::move (trait_items),
+ std::move (generics.impl));
}
// TODO: Create new `make_qualified_call` helper function
@@ -227,24 +170,222 @@ DeriveClone::visit_struct (StructStruct &item)
item.get_generic_params ());
}
+MatchCase
+DeriveClone::clone_enum_identifier (PathInExpression variant_path,
+ const std::unique_ptr<EnumItem> &variant)
+{
+ auto pattern = std::unique_ptr<Pattern> (new ReferencePattern (
+ std::unique_ptr<Pattern> (new PathInExpression (
+ variant_path.get_segments (), {}, variant_path.get_locus (),
+ variant_path.opening_scope_resolution ())),
+ false, false, loc));
+ auto expr = std::unique_ptr<Expr> (
+ new PathInExpression (variant_path.get_segments (), {},
+ variant_path.get_locus (),
+ variant_path.opening_scope_resolution ()));
+
+ return builder.match_case (std::move (pattern), std::move (expr));
+}
+
+MatchCase
+DeriveClone::clone_enum_tuple (PathInExpression variant_path,
+ const EnumItemTuple &variant)
+{
+ auto patterns = std::vector<std::unique_ptr<Pattern>> ();
+ auto cloned_patterns = std::vector<std::unique_ptr<Expr>> ();
+
+ for (size_t i = 0; i < variant.get_tuple_fields ().size (); i++)
+ {
+ // The pattern we're creating for each field is `self_<i>` where `i` is
+ // the index of the field. It doesn't actually matter what we use, as long
+ // as it's ordered, unique, and that we can reuse it in the match case's
+ // return expression to clone the field.
+ auto pattern_str = "__self_" + std::to_string (i);
+
+ patterns.emplace_back (builder.identifier_pattern (pattern_str));
+
+ // Now, for each tuple's element, we create a new expression calling
+ // `clone` on it for the match case's return expression
+ cloned_patterns.emplace_back (
+ clone_call (builder.ref (builder.identifier (pattern_str))));
+ }
+
+ auto pattern_items = std::unique_ptr<TupleStructItems> (
+ new TupleStructItemsNoRange (std::move (patterns)));
+
+ auto pattern = std::unique_ptr<Pattern> (new ReferencePattern (
+ std::unique_ptr<Pattern> (new TupleStructPattern (
+ PathInExpression (variant_path.get_segments (), {},
+ variant_path.get_locus (),
+ variant_path.opening_scope_resolution ()),
+ std::move (pattern_items))),
+ false, false, loc));
+
+ auto expr = builder.call (std::unique_ptr<Expr> (new PathInExpression (
+ variant_path.get_segments (), {},
+ variant_path.get_locus (),
+ variant_path.opening_scope_resolution ())),
+ std::move (cloned_patterns));
+
+ return builder.match_case (std::move (pattern), std::move (expr));
+}
+
+MatchCase
+DeriveClone::clone_enum_struct (PathInExpression variant_path,
+ const EnumItemStruct &variant)
+{
+ auto field_patterns = std::vector<std::unique_ptr<StructPatternField>> ();
+ auto cloned_fields = std::vector<std::unique_ptr<StructExprField>> ();
+
+#if 0
+ // NOTE: We currently do not support compiling struct patterns where an
+ // identifier is assigned a new pattern, e.g. Bloop { f0: x }
+ // This is the code we should eventually produce as it mimics what rustc does
+ // - which is probably here for a good reason. In the meantime, we can just
+ // use the field's identifier as the pattern: Bloop { f0 }
+ // We can then clone the field directly instead of calling `clone()` on the
+ // new pattern.
+ // TODO: Figure out if that is actually needed and why rustc does it?
+
+ for (size_t i = 0; i < variant.get_struct_fields ().size (); i++)
+ {
+ auto &field = variant.get_struct_fields ()[i];
+
+ // Just like for tuples, the pattern we're creating for each field is
+ // `self_<i>` where `i` is the index of the field. It doesn't actually
+ // matter what we use, as long as it's ordered, unique, and that we can
+ // reuse it in the match case's return expression to clone the field.
+ auto pattern_str = "__self_" + std::to_string (i);
+
+ field_patterns.emplace_back (
+ std::unique_ptr<StructPatternField> (new StructPatternFieldIdentPat (
+ field.get_field_name (), builder.identifier_pattern (pattern_str), {},
+ loc)));
+
+ cloned_fields.emplace_back (
+ std::unique_ptr<StructExprField> (new StructExprFieldIdentifierValue (
+ field.get_field_name (),
+ clone_call (builder.ref (builder.identifier (pattern_str))), {},
+ loc)));
+ }
+#endif
+
+ for (const auto &field : variant.get_struct_fields ())
+ {
+ // We match on the struct's fields, and then recreate an instance of that
+ // struct, cloning each field
+
+ field_patterns.emplace_back (
+ std::unique_ptr<StructPatternField> (new StructPatternFieldIdent (
+ field.get_field_name (), false /* is_ref? true? */, false, {}, loc)));
+
+ cloned_fields.emplace_back (
+ std::unique_ptr<StructExprField> (new StructExprFieldIdentifierValue (
+ field.get_field_name (),
+ clone_call (builder.ref (
+ builder.identifier (field.get_field_name ().as_string ()))),
+ {}, loc)));
+ }
+
+ auto pattern_elts = StructPatternElements (std::move (field_patterns));
+
+ auto pattern = std::unique_ptr<Pattern> (
+ new ReferencePattern (std::unique_ptr<Pattern> (new StructPattern (
+ variant_path, loc, pattern_elts)),
+ false, false, loc));
+
+ PathInExpression new_path (variant_path.get_segments (),
+ variant_path.get_outer_attrs (),
+ variant_path.get_locus (),
+ variant_path.opening_scope_resolution ());
+
+ auto expr = std::unique_ptr<Expr> (
+ new StructExprStructFields (new_path, std::move (cloned_fields), loc));
+
+ return builder.match_case (std::move (pattern), std::move (expr));
+}
+
void
DeriveClone::visit_enum (Enum &item)
{
- rust_sorry_at (item.get_locus (), "cannot derive %qs for these items yet",
- "Clone");
+ // Create an arm for each variant of the enum:
+ // - For enum item variants (simple identifiers), just create the same
+ // variant.
+ // - For struct and tuple variants, destructure the pattern and call clone for
+ // each field.
+
+ auto cases = std::vector<MatchCase> ();
+
+ for (const auto &variant : item.get_variants ())
+ {
+ auto path
+ = builder.variant_path (item.get_identifier ().as_string (),
+ variant->get_identifier ().as_string ());
+
+ switch (variant->get_enum_item_kind ())
+ {
+ // Identifiers and discriminated variants are the same for a clone - we
+ // just return the same variant
+ case EnumItem::Kind::Identifier:
+ case EnumItem::Kind::Discriminant:
+ cases.emplace_back (clone_enum_identifier (path, variant));
+ break;
+ case EnumItem::Kind::Tuple:
+ cases.emplace_back (
+ clone_enum_tuple (path, static_cast<EnumItemTuple &> (*variant)));
+ break;
+ case EnumItem::Kind::Struct:
+ cases.emplace_back (
+ clone_enum_struct (path, static_cast<EnumItemStruct &> (*variant)));
+ break;
+ }
+ }
+
+ // match self { ... }
+ auto match = builder.match (builder.identifier ("self"), std::move (cases));
+
+ expanded = clone_impl (clone_fn (std::move (match)),
+ item.get_identifier ().as_string (),
+ item.get_generic_params ());
}
void
DeriveClone::visit_union (Union &item)
{
// FIXME: Should be $crate::core::clone::AssertParamIsCopy (or similar)
+ // (Rust-GCC#3329)
+
+ auto copy_path = builder.type_path (LangItem::Kind::COPY);
+ auto sized_path = builder.type_path (LangItem::Kind::SIZED);
+
+ auto copy_bound = std::unique_ptr<TypeParamBound> (
+ new TraitBound (copy_path, item.get_locus ()));
+ auto sized_bound = std::unique_ptr<TypeParamBound> (
+ new TraitBound (sized_path, item.get_locus (), false,
+ true /* opening_question_mark */));
+
+ auto bounds = vec (std::move (copy_bound), std::move (sized_bound));
+
+ // struct AssertParamIsCopy<T: Copy + ?Sized> { _t: PhantomData<T> }
+ auto assert_param_is_copy = "AssertParamIsCopy";
+ auto t = std::unique_ptr<GenericParam> (
+ new TypeParam (Identifier ("T"), item.get_locus (), std::move (bounds)));
+ auto assert_param_is_copy_struct = builder.struct_struct (
+ assert_param_is_copy, vec (std::move (t)),
+ {StructField (
+ Identifier ("_t"),
+ builder.single_generic_type_path (
+ LangItem::Kind::PHANTOM_DATA,
+ GenericArgs (
+ {}, {GenericArg::create_type (builder.single_type_path ("T"))}, {})),
+ Visibility::create_private (), item.get_locus ())});
// <Self>
auto arg = GenericArg::create_type (builder.single_type_path ("Self"));
// AssertParamIsCopy::<Self>
auto type = std::unique_ptr<TypePathSegment> (
- new TypePathSegmentGeneric (PathIdentSegment ("AssertParamIsCopy", loc),
+ new TypePathSegmentGeneric (PathIdentSegment (assert_param_is_copy, loc),
false, GenericArgs ({}, {arg}, {}, loc), loc));
auto type_paths = std::vector<std::unique_ptr<TypePathSegment>> ();
type_paths.emplace_back (std::move (type));
@@ -252,11 +393,12 @@ DeriveClone::visit_union (Union &item)
auto full_path
= std::unique_ptr<Type> (new TypePath ({std::move (type_paths)}, loc));
- auto stmts = std::vector<std::unique_ptr<Stmt>> ();
- stmts.emplace_back (
- builder.let (builder.wildcard (), std::move (full_path), nullptr));
auto tail_expr = builder.deref (builder.identifier ("self"));
+ auto stmts
+ = vec (std::move (assert_param_is_copy_struct),
+ builder.let (builder.wildcard (), std::move (full_path), nullptr));
+
auto block = builder.block (std::move (stmts), std::move (tail_expr));
expanded = clone_impl (clone_fn (std::move (block)),
diff --git a/gcc/rust/expand/rust-derive-clone.h b/gcc/rust/expand/rust-derive-clone.h
index 4a43b2a..61224ba 100644
--- a/gcc/rust/expand/rust-derive-clone.h
+++ b/gcc/rust/expand/rust-derive-clone.h
@@ -63,6 +63,23 @@ private:
clone_impl (std::unique_ptr<AssociatedItem> &&clone_fn, std::string name,
const std::vector<std::unique_ptr<GenericParam>> &type_generics);
+ /**
+ * Get the path to use for matching and creating a variant when matching on an
+ * enum. E.g. for the `Option` enum, with the `None` variant, this will create
+ * a path `Option::None`
+ */
+ PathInExpression variant_match_path (Enum &item, const Identifier &variant);
+
+ /**
+ * Implementation of clone for all possible variants of an enum
+ */
+ MatchCase clone_enum_identifier (PathInExpression variant_path,
+ const std::unique_ptr<EnumItem> &variant);
+ MatchCase clone_enum_tuple (PathInExpression variant_path,
+ const EnumItemTuple &variant);
+ MatchCase clone_enum_struct (PathInExpression variant_path,
+ const EnumItemStruct &variant);
+
virtual void visit_struct (StructStruct &item);
virtual void visit_tuple (TupleStruct &item);
virtual void visit_enum (Enum &item);
diff --git a/gcc/rust/expand/rust-derive-copy.cc b/gcc/rust/expand/rust-derive-copy.cc
index 1de7290..b2971ad 100644
--- a/gcc/rust/expand/rust-derive-copy.cc
+++ b/gcc/rust/expand/rust-derive-copy.cc
@@ -17,7 +17,8 @@
// <http://www.gnu.org/licenses/>.
#include "rust-derive-copy.h"
-#include "rust-ast-full.h"
+#include "rust-hir-map.h"
+#include "rust-path.h"
namespace Rust {
namespace AST {
@@ -41,86 +42,16 @@ DeriveCopy::copy_impl (
std::string name,
const std::vector<std::unique_ptr<GenericParam>> &type_generics)
{
- // `$crate::core::marker::Copy` instead
- auto segments = std::vector<std::unique_ptr<TypePathSegment>> ();
- segments.emplace_back (builder.type_path_segment ("Copy"));
- auto copy = TypePath (std::move (segments), loc);
-
- // we need to build up the generics for this impl block which will be just a
- // clone of the types specified ones
- //
- // for example:
- //
- // #[derive(Copy)]
- // struct Be<T: Copy> { ... }
- //
- // we need to generate the impl block:
- //
- // impl<T: Copy> Clone for Be<T>
-
- std::vector<Lifetime> lifetime_args;
- std::vector<GenericArg> generic_args;
- std::vector<std::unique_ptr<GenericParam>> impl_generics;
- for (const auto &generic : type_generics)
- {
- switch (generic->get_kind ())
- {
- case GenericParam::Kind::Lifetime: {
- LifetimeParam &lifetime_param = (LifetimeParam &) *generic.get ();
-
- Lifetime l = builder.new_lifetime (lifetime_param.get_lifetime ());
- lifetime_args.push_back (std::move (l));
-
- auto impl_lifetime_param
- = builder.new_lifetime_param (lifetime_param);
- impl_generics.push_back (std::move (impl_lifetime_param));
- }
- break;
-
- case GenericParam::Kind::Type: {
- TypeParam &type_param = (TypeParam &) *generic.get ();
-
- std::unique_ptr<Type> associated_type = builder.single_type_path (
- type_param.get_type_representation ().as_string ());
-
- GenericArg type_arg
- = GenericArg::create_type (std::move (associated_type));
- generic_args.push_back (std::move (type_arg));
-
- auto impl_type_param = builder.new_type_param (type_param);
- impl_generics.push_back (std::move (impl_type_param));
- }
- break;
-
- case GenericParam::Kind::Const: {
- rust_unreachable ();
-
- // TODO
- // const ConstGenericParam *const_param
- // = (const ConstGenericParam *) generic.get ();
- // std::unique_ptr<Expr> const_expr = nullptr;
-
- // GenericArg type_arg
- // = GenericArg::create_const (std::move (const_expr));
- // generic_args.push_back (std::move (type_arg));
- }
- break;
- }
- }
-
- GenericArgs generic_args_for_self (lifetime_args, generic_args,
- {} /*binding args*/, loc);
- std::unique_ptr<Type> self_type_path
- = impl_generics.empty ()
- ? builder.single_type_path (name)
- : builder.single_generic_type_path (name, generic_args_for_self);
-
- return std::unique_ptr<Item> (
- new TraitImpl (copy, /* unsafe */ false,
- /* exclam */ false, /* trait items */ {},
- std::move (impl_generics), std::move (self_type_path),
- WhereClause::create_empty (), Visibility::create_private (),
- {}, {}, loc));
+ // we should have two of these, so we don't run into issues with
+ // two paths sharing a node id
+ auto copy_bound = builder.type_path (LangItem::Kind::COPY);
+ auto copy_trait_path = builder.type_path (LangItem::Kind::COPY);
+
+ auto generics = setup_impl_generics (name, type_generics,
+ builder.trait_bound (copy_bound));
+
+ return builder.trait_impl (copy_trait_path, std::move (generics.self_type),
+ {}, std::move (generics.impl));
}
void
diff --git a/gcc/rust/expand/rust-derive-debug.cc b/gcc/rust/expand/rust-derive-debug.cc
new file mode 100644
index 0000000..a0bf9d8
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-debug.cc
@@ -0,0 +1,121 @@
+// Copyright (C) 2025 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-debug.h"
+#include "rust-ast.h"
+#include "rust-hir-map.h"
+#include "rust-system.h"
+
+namespace Rust {
+namespace AST {
+
+DeriveDebug::DeriveDebug (location_t loc)
+ : DeriveVisitor (loc), expanded (nullptr)
+{}
+
+std::unique_ptr<Item>
+DeriveDebug::go (Item &item)
+{
+ item.accept_vis (*this);
+
+ rust_assert (expanded);
+
+ return std::move (expanded);
+}
+
+std::unique_ptr<AssociatedItem>
+DeriveDebug::stub_debug_fn ()
+{
+ auto unit_expr = builder.tuple ();
+ auto ok_expr
+ = ptrify (builder.path_in_expression (LangItem::Kind::RESULT_OK));
+
+ auto stub_return = builder.call (std::move (ok_expr), std::move (unit_expr));
+
+ // we can't use builder.block() here as it returns a unique_ptr<Expr> and
+ // Function's constructor expects a unique_ptr<BlockExpr>
+ auto block = std::unique_ptr<BlockExpr> (
+ new BlockExpr ({}, std::move (stub_return), {}, {}, tl::nullopt, loc, loc));
+
+ auto self = builder.self_ref_param ();
+
+ auto return_type
+ = ptrify (builder.type_path ({"core", "fmt", "Result"}, true));
+
+ auto mut_fmt_type_inner
+ = ptrify (builder.type_path ({"core", "fmt", "Formatter"}, true));
+
+ auto mut_fmt_type
+ = builder.reference_type (std::move (mut_fmt_type_inner), true);
+
+ auto fmt = builder.function_param (builder.identifier_pattern ("_fmt"),
+ std::move (mut_fmt_type));
+
+ auto params = vec (std::move (self), std::move (fmt));
+
+ auto function = builder.function ("fmt", std::move (params),
+ std::move (return_type), std::move (block));
+
+ return function;
+}
+
+std::unique_ptr<Item>
+DeriveDebug::stub_derive_impl (
+ std::string name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics)
+{
+ auto trait_items = vec (stub_debug_fn ());
+
+ auto debug = builder.type_path ({"core", "fmt", "Debug"}, true);
+ auto generics
+ = setup_impl_generics (name, type_generics, builder.trait_bound (debug));
+
+ return builder.trait_impl (debug, std::move (generics.self_type),
+ std::move (trait_items),
+ std::move (generics.impl));
+}
+
+void
+DeriveDebug::visit_struct (StructStruct &struct_item)
+{
+ expanded = stub_derive_impl (struct_item.get_identifier ().as_string (),
+ struct_item.get_generic_params ());
+}
+
+void
+DeriveDebug::visit_tuple (TupleStruct &tuple_item)
+{
+ expanded = stub_derive_impl (tuple_item.get_identifier ().as_string (),
+ tuple_item.get_generic_params ());
+}
+
+void
+DeriveDebug::visit_enum (Enum &enum_item)
+{
+ expanded = stub_derive_impl (enum_item.get_identifier ().as_string (),
+ enum_item.get_generic_params ());
+}
+
+void
+DeriveDebug::visit_union (Union &enum_item)
+{
+ rust_error_at (loc, "derive(Debug) cannot be derived for unions");
+}
+
+} // namespace AST
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-derive-debug.h b/gcc/rust/expand/rust-derive-debug.h
new file mode 100644
index 0000000..14af89e
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-debug.h
@@ -0,0 +1,55 @@
+// Copyright (C) 2025 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_DEBUG_H
+#define RUST_DERIVE_DEBUG_H
+
+#include "rust-derive.h"
+#include "rust-ast.h"
+
+namespace Rust {
+namespace AST {
+
+// This derive is currently incomplete and only generate a stub implementation
+// which does not do any debug formatting
+class DeriveDebug : DeriveVisitor
+{
+public:
+ DeriveDebug (location_t loc);
+
+ std::unique_ptr<Item> go (Item &);
+
+private:
+ std::unique_ptr<Item> expanded;
+
+ std::unique_ptr<AssociatedItem> stub_debug_fn ();
+
+ std::unique_ptr<Item> stub_derive_impl (
+ std::string name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics);
+
+ virtual void visit_struct (StructStruct &struct_item) override;
+ virtual void visit_tuple (TupleStruct &tuple_item) override;
+ virtual void visit_enum (Enum &enum_item) override;
+ virtual void visit_union (Union &enum_item) override;
+};
+
+} // namespace AST
+} // namespace Rust
+
+#endif // ! RUST_DERIVE_DEBUG_H
diff --git a/gcc/rust/expand/rust-derive-default.cc b/gcc/rust/expand/rust-derive-default.cc
new file mode 100644
index 0000000..2e8b456
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-default.cc
@@ -0,0 +1,172 @@
+// Copyright (C) 2025 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-default.h"
+#include "rust-ast.h"
+#include "rust-diagnostics.h"
+#include "rust-path.h"
+#include "rust-system.h"
+
+namespace Rust {
+namespace AST {
+
+DeriveDefault::DeriveDefault (location_t loc)
+ : DeriveVisitor (loc), expanded (nullptr)
+{}
+
+std::unique_ptr<Item>
+DeriveDefault::go (Item &item)
+{
+ item.accept_vis (*this);
+
+ rust_assert (expanded);
+
+ return std::move (expanded);
+}
+
+std::unique_ptr<Expr>
+DeriveDefault::default_call (std::unique_ptr<Type> &&type)
+{
+ auto default_trait = builder.type_path ({"core", "default", "Default"}, true);
+
+ auto default_fn
+ = builder.qualified_path_in_expression (std::move (type), default_trait,
+ builder.path_segment ("default"));
+
+ return builder.call (std::move (default_fn));
+}
+
+std::unique_ptr<AssociatedItem>
+DeriveDefault::default_fn (std::unique_ptr<Expr> &&return_expr)
+{
+ auto self_ty
+ = std::unique_ptr<Type> (new TypePath (builder.type_path ("Self")));
+
+ auto block = std::unique_ptr<BlockExpr> (
+ new BlockExpr ({}, std::move (return_expr), {}, {}, tl::nullopt, loc, loc));
+
+ return builder.function ("default", {}, std::move (self_ty),
+ std::move (block));
+}
+
+std::unique_ptr<Item>
+DeriveDefault::default_impl (
+ std::unique_ptr<AssociatedItem> &&default_fn, std::string name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics)
+{
+ auto default_path = builder.type_path ({"core", "default", "Default"}, true);
+
+ auto trait_items = vec (std::move (default_fn));
+
+ auto generics = setup_impl_generics (name, type_generics,
+ builder.trait_bound (default_path));
+
+ return builder.trait_impl (default_path, std::move (generics.self_type),
+ std::move (trait_items),
+ std::move (generics.impl));
+}
+
+void
+DeriveDefault::visit_struct (StructStruct &item)
+{
+ if (item.is_unit_struct ())
+ {
+ auto unit_ctor
+ = builder.struct_expr_struct (item.get_struct_name ().as_string ());
+ expanded = default_impl (default_fn (std::move (unit_ctor)),
+ item.get_struct_name ().as_string (),
+ item.get_generic_params ());
+ return;
+ }
+
+ auto cloned_fields = std::vector<std::unique_ptr<StructExprField>> ();
+ for (auto &field : item.get_fields ())
+ {
+ auto name = field.get_field_name ().as_string ();
+ auto expr = default_call (field.get_field_type ().clone_type ());
+
+ cloned_fields.emplace_back (
+ builder.struct_expr_field (std::move (name), std::move (expr)));
+ }
+
+ auto ctor = builder.struct_expr (item.get_struct_name ().as_string (),
+ std::move (cloned_fields));
+
+ expanded = default_impl (default_fn (std::move (ctor)),
+ item.get_struct_name ().as_string (),
+ item.get_generic_params ());
+}
+
+void
+DeriveDefault::visit_tuple (TupleStruct &tuple_item)
+{
+ auto defaulted_fields = std::vector<std::unique_ptr<Expr>> ();
+
+ for (auto &field : tuple_item.get_fields ())
+ {
+ auto type = field.get_field_type ().clone_type ();
+
+ defaulted_fields.emplace_back (default_call (std::move (type)));
+ }
+
+ auto return_expr
+ = builder.call (builder.identifier (
+ tuple_item.get_struct_name ().as_string ()),
+ std::move (defaulted_fields));
+
+ expanded = default_impl (default_fn (std::move (return_expr)),
+ tuple_item.get_struct_name ().as_string (),
+ tuple_item.get_generic_params ());
+}
+
+void
+DeriveDefault::visit_enum (Enum &enum_item)
+{
+ // This is no longer the case in later Rust versions where you can choose a
+ // default variant to emit using the `#[default]` attribute:
+ //
+ // ```rust
+ // #[derive(Default)]
+ // enum Baz {
+ // #[default]
+ // A,
+ // B(i32),
+ // C { a: i32 }
+ // }
+ // ```
+ //
+ // will emit the following impl
+ //
+ // ```rust
+ // impl ::core::default::Default for Baz {
+ // #[inline]
+ // fn default() -> Baz { Self::A }
+ // }
+ // ```
+ rust_error_at (loc, ErrorCode::E0665,
+ "%<Default%> cannot be derived for enums, only structs");
+}
+
+void
+DeriveDefault::visit_union (Union &enum_item)
+{
+ rust_error_at (loc, "derive(Default) cannot be used on unions");
+}
+
+} // namespace AST
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-derive-default.h b/gcc/rust/expand/rust-derive-default.h
new file mode 100644
index 0000000..eae9e85
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-default.h
@@ -0,0 +1,58 @@
+// Copyright (C) 2025 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_DEFAULT_H
+#define RUST_DERIVE_DEFAULT_H
+
+#include "rust-derive.h"
+#include "rust-ast.h"
+
+namespace Rust {
+namespace AST {
+
+// This derive is currently incomplete and only generate a stub implementation
+// which does not do any debug formatting
+class DeriveDefault : DeriveVisitor
+{
+public:
+ DeriveDefault (location_t loc);
+
+ std::unique_ptr<Item> go (Item &);
+
+private:
+ std::unique_ptr<Item> expanded;
+
+ std::unique_ptr<Expr> default_call (std::unique_ptr<Type> &&type);
+
+ std::unique_ptr<AssociatedItem>
+ default_fn (std::unique_ptr<Expr> &&return_expr);
+
+ std::unique_ptr<Item> default_impl (
+ std::unique_ptr<AssociatedItem> &&default_fn, std::string name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics);
+
+ virtual void visit_struct (StructStruct &struct_item) override;
+ virtual void visit_tuple (TupleStruct &tuple_item) override;
+ virtual void visit_enum (Enum &enum_item) override;
+ virtual void visit_union (Union &enum_item) override;
+};
+
+} // namespace AST
+} // namespace Rust
+
+#endif // ! RUST_DERIVE_DEFAULT_H
diff --git a/gcc/rust/expand/rust-derive-eq.cc b/gcc/rust/expand/rust-derive-eq.cc
new file mode 100644
index 0000000..5e7a894
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-eq.cc
@@ -0,0 +1,216 @@
+// Copyright (C) 2025 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-eq.h"
+#include "rust-ast.h"
+#include "rust-expr.h"
+#include "rust-item.h"
+#include "rust-path.h"
+#include "rust-pattern.h"
+#include "rust-system.h"
+
+namespace Rust {
+namespace AST {
+
+DeriveEq::DeriveEq (location_t loc) : DeriveVisitor (loc) {}
+
+std::vector<std::unique_ptr<AST::Item>>
+DeriveEq::go (Item &item)
+{
+ item.accept_vis (*this);
+
+ return std::move (expanded);
+}
+
+std::unique_ptr<AssociatedItem>
+DeriveEq::assert_receiver_is_total_eq_fn (
+ std::vector<std::unique_ptr<Type>> &&types)
+{
+ auto stmts = std::vector<std::unique_ptr<Stmt>> ();
+
+ stmts.emplace_back (assert_param_is_eq ());
+
+ for (auto &&type : types)
+ stmts.emplace_back (assert_type_is_eq (std::move (type)));
+
+ auto block = std::unique_ptr<BlockExpr> (
+ new BlockExpr (std::move (stmts), nullptr, {}, {}, tl::nullopt, loc, loc));
+
+ auto self = builder.self_ref_param ();
+
+ return builder.function ("assert_receiver_is_total_eq",
+ vec (std::move (self)), {}, std::move (block));
+}
+
+std::unique_ptr<Stmt>
+DeriveEq::assert_param_is_eq ()
+{
+ auto eq_bound = std::unique_ptr<TypeParamBound> (
+ new TraitBound (builder.type_path ({"core", "cmp", "Eq"}, true), loc));
+
+ auto sized_bound = std::unique_ptr<TypeParamBound> (
+ new TraitBound (builder.type_path (LangItem::Kind::SIZED), loc, false,
+ true /* opening_question_mark */));
+
+ auto bounds = vec (std::move (eq_bound), std::move (sized_bound));
+
+ auto assert_param_is_eq = "AssertParamIsEq";
+
+ auto t = std::unique_ptr<GenericParam> (
+ new TypeParam (Identifier ("T"), loc, std::move (bounds)));
+
+ return builder.struct_struct (
+ assert_param_is_eq, vec (std::move (t)),
+ {StructField (
+ Identifier ("_t"),
+ builder.single_generic_type_path (
+ LangItem::Kind::PHANTOM_DATA,
+ GenericArgs (
+ {}, {GenericArg::create_type (builder.single_type_path ("T"))}, {})),
+ Visibility::create_private (), loc)});
+}
+
+std::unique_ptr<Stmt>
+DeriveEq::assert_type_is_eq (std::unique_ptr<Type> &&type)
+{
+ auto assert_param_is_eq = "AssertParamIsEq";
+
+ // AssertParamIsCopy::<Self>
+ auto assert_param_is_eq_ty
+ = std::unique_ptr<TypePathSegment> (new TypePathSegmentGeneric (
+ PathIdentSegment (assert_param_is_eq, loc), false,
+ GenericArgs ({}, {GenericArg::create_type (std::move (type))}, {}, loc),
+ loc));
+
+ // TODO: Improve this, it's really ugly
+ auto type_paths = std::vector<std::unique_ptr<TypePathSegment>> ();
+ type_paths.emplace_back (std::move (assert_param_is_eq_ty));
+
+ auto full_path
+ = std::unique_ptr<Type> (new TypePath ({std::move (type_paths)}, loc));
+
+ return builder.let (builder.wildcard (), std::move (full_path));
+}
+
+std::vector<std::unique_ptr<Item>>
+DeriveEq::eq_impls (
+ std::unique_ptr<AssociatedItem> &&fn, std::string name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics)
+{
+ // We create two copies of the type-path to avoid duplicate NodeIds
+ auto eq = builder.type_path ({"core", "cmp", "Eq"}, true);
+ auto eq_bound
+ = builder.trait_bound (builder.type_path ({"core", "cmp", "Eq"}, true));
+
+ auto steq = builder.type_path (LangItem::Kind::STRUCTURAL_TEQ);
+
+ auto trait_items = vec (std::move (fn));
+
+ auto eq_generics
+ = setup_impl_generics (name, type_generics, std::move (eq_bound));
+ auto steq_generics = setup_impl_generics (name, type_generics);
+
+ auto eq_impl = builder.trait_impl (eq, std::move (eq_generics.self_type),
+ std::move (trait_items),
+ std::move (eq_generics.impl));
+ auto steq_impl
+ = builder.trait_impl (steq, std::move (steq_generics.self_type),
+ std::move (trait_items),
+ std::move (steq_generics.impl));
+
+ return vec (std::move (eq_impl), std::move (steq_impl));
+}
+
+void
+DeriveEq::visit_tuple (TupleStruct &item)
+{
+ auto types = std::vector<std::unique_ptr<Type>> ();
+
+ for (auto &field : item.get_fields ())
+ types.emplace_back (field.get_field_type ().clone_type ());
+
+ expanded = eq_impls (assert_receiver_is_total_eq_fn (std::move (types)),
+ item.get_identifier ().as_string (),
+ item.get_generic_params ());
+}
+
+void
+DeriveEq::visit_struct (StructStruct &item)
+{
+ auto types = std::vector<std::unique_ptr<Type>> ();
+
+ for (auto &field : item.get_fields ())
+ types.emplace_back (field.get_field_type ().clone_type ());
+
+ expanded = eq_impls (assert_receiver_is_total_eq_fn (std::move (types)),
+ item.get_identifier ().as_string (),
+ item.get_generic_params ());
+}
+
+void
+DeriveEq::visit_enum (Enum &item)
+{
+ auto types = std::vector<std::unique_ptr<Type>> ();
+
+ for (auto &variant : item.get_variants ())
+ {
+ switch (variant->get_enum_item_kind ())
+ {
+ case EnumItem::Kind::Identifier:
+ case EnumItem::Kind::Discriminant:
+ // nothing to do as they contain no inner types
+ continue;
+ case EnumItem::Kind::Tuple: {
+ auto &tuple = static_cast<EnumItemTuple &> (*variant);
+
+ for (auto &field : tuple.get_tuple_fields ())
+ types.emplace_back (field.get_field_type ().clone_type ());
+
+ break;
+ }
+ case EnumItem::Kind::Struct: {
+ auto &tuple = static_cast<EnumItemStruct &> (*variant);
+
+ for (auto &field : tuple.get_struct_fields ())
+ types.emplace_back (field.get_field_type ().clone_type ());
+
+ break;
+ }
+ }
+ }
+
+ expanded = eq_impls (assert_receiver_is_total_eq_fn (std::move (types)),
+ item.get_identifier ().as_string (),
+ item.get_generic_params ());
+}
+
+void
+DeriveEq::visit_union (Union &item)
+{
+ auto types = std::vector<std::unique_ptr<Type>> ();
+
+ for (auto &field : item.get_variants ())
+ types.emplace_back (field.get_field_type ().clone_type ());
+
+ expanded = eq_impls (assert_receiver_is_total_eq_fn (std::move (types)),
+ item.get_identifier ().as_string (),
+ item.get_generic_params ());
+}
+
+} // namespace AST
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-derive-eq.h b/gcc/rust/expand/rust-derive-eq.h
new file mode 100644
index 0000000..17af526
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-eq.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2025 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_EQ_H
+#define RUST_DERIVE_EQ_H
+
+#include "rust-derive.h"
+
+namespace Rust {
+namespace AST {
+
+// FIXME: Need to figure out structuraleq marker trait
+
+class DeriveEq : DeriveVisitor
+{
+public:
+ DeriveEq (location_t loc);
+
+ std::vector<std::unique_ptr<AST::Item>> go (Item &item);
+
+private:
+ std::vector<std::unique_ptr<Item>> expanded;
+
+ /**
+ * Create the actual `assert_receiver_is_total_eq` function of the
+ * implementation, which asserts that every type contained within our targeted
+ * type also implements `Eq`.
+ */
+ std::unique_ptr<AssociatedItem>
+ assert_receiver_is_total_eq_fn (std::vector<std::unique_ptr<Type>> &&types);
+
+ /**
+ * Create the Eq trait implementation for a type
+ *
+ * impl Eq for <type> {
+ * <assert_receiver_is_total_eq>
+ * }
+ *
+ */
+ std::vector<std::unique_ptr<Item>>
+ eq_impls (std::unique_ptr<AssociatedItem> &&fn, std::string name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics);
+
+ /**
+ * Generate the following structure definition
+ *
+ * struct AssertParamIsEq<T: Eq + ?Sized> { _t: PhantomData<T> }
+ */
+ std::unique_ptr<Stmt> assert_param_is_eq ();
+
+ /**
+ * Generate a let statement to assert a type implements `Eq`
+ *
+ * let _: AssertParamIsEq<type>;
+ */
+ std::unique_ptr<Stmt> assert_type_is_eq (std::unique_ptr<Type> &&type);
+
+ 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_EQ_H
diff --git a/gcc/rust/expand/rust-derive-hash.cc b/gcc/rust/expand/rust-derive-hash.cc
new file mode 100644
index 0000000..0c9b0f7
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-hash.cc
@@ -0,0 +1,293 @@
+// Copyright (C) 2025 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-hash.h"
+#include "rust-ast.h"
+#include "rust-expr.h"
+#include "rust-item.h"
+#include "rust-path.h"
+#include "rust-pattern.h"
+#include "rust-stmt.h"
+#include "rust-system.h"
+
+namespace Rust {
+namespace AST {
+
+DeriveHash::DeriveHash (location_t loc) : DeriveVisitor (loc) {}
+
+std::unique_ptr<AST::Item>
+DeriveHash::go (Item &item)
+{
+ item.accept_vis (*this);
+
+ return std::move (expanded);
+}
+
+std::unique_ptr<Expr>
+DeriveHash::hash_call (std::unique_ptr<Expr> &&value)
+{
+ auto hash
+ = builder.path_in_expression ({"core", "hash", "Hash", "hash"}, true);
+
+ return builder.call (ptrify (hash),
+ vec (std::move (value),
+ builder.identifier (DeriveHash::state)));
+}
+
+std::unique_ptr<AssociatedItem>
+DeriveHash::hash_fn (std::unique_ptr<BlockExpr> &&block)
+{
+ auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
+
+ auto state_type = std::unique_ptr<TypeNoBounds> (
+ new TypePath (builder.type_path (DeriveHash::state_type)));
+ auto state_param =
+
+ builder.function_param (builder.identifier_pattern (DeriveHash::state),
+ builder.reference_type (std::move (state_type),
+ true));
+
+ auto params = vec (builder.self_ref_param (), std::move (state_param));
+ auto bounds = vec (
+ builder.trait_bound (builder.type_path ({"core", "hash", "Hasher"}, true)));
+ auto generics = vec (
+ builder.generic_type_param (DeriveHash::state_type, std::move (bounds)));
+
+ return builder.function ("hash", std::move (params), nullptr,
+ std::move (block), std::move (generics));
+}
+
+std::unique_ptr<Item>
+DeriveHash::hash_impl (
+ std::unique_ptr<AssociatedItem> &&hash_fn, std::string name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics)
+{
+ auto hash_path = builder.type_path ({"core", "hash", "Hash"}, true);
+
+ auto trait_items = vec (std::move (hash_fn));
+
+ auto generics = setup_impl_generics (name, type_generics,
+ builder.trait_bound (hash_path));
+
+ return builder.trait_impl (hash_path, std::move (generics.self_type),
+ std::move (trait_items),
+ std::move (generics.impl));
+}
+
+void
+DeriveHash::visit_struct (StructStruct &item)
+{
+ auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
+
+ for (auto &field : item.get_fields ())
+ {
+ auto value = builder.ref (
+ builder.field_access (builder.identifier ("self"),
+ field.get_field_name ().as_string ()));
+
+ auto stmt = builder.statementify (hash_call (std::move (value)));
+
+ hash_calls.emplace_back (std::move (stmt));
+ }
+
+ auto block = builder.block (std::move (hash_calls));
+
+ expanded = hash_impl (hash_fn (std::move (block)),
+ item.get_identifier ().as_string (),
+ item.get_generic_params ());
+}
+
+void
+DeriveHash::visit_tuple (TupleStruct &item)
+{
+ auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
+
+ for (size_t idx = 0; idx < item.get_fields ().size (); idx++)
+ {
+ auto value = builder.ref (builder.tuple_idx ("self", idx));
+
+ auto stmt = builder.statementify (hash_call (std::move (value)));
+
+ hash_calls.emplace_back (std::move (stmt));
+ }
+
+ auto block = builder.block (std::move (hash_calls));
+
+ expanded = hash_impl (hash_fn (std::move (block)),
+ item.get_identifier ().as_string (),
+ item.get_generic_params ());
+}
+
+MatchCase
+DeriveHash::match_enum_tuple (PathInExpression variant_path,
+ const EnumItemTuple &variant)
+{
+ auto self_patterns = std::vector<std::unique_ptr<Pattern>> ();
+ auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
+
+ for (size_t i = 0; i < variant.get_tuple_fields ().size (); i++)
+ {
+ auto pattern = "__self_" + std::to_string (i);
+
+ auto call = hash_call (builder.ref (builder.identifier (pattern)));
+
+ self_patterns.emplace_back (builder.identifier_pattern (pattern));
+ hash_calls.emplace_back (builder.statementify (std::move (call)));
+ }
+
+ auto patterns_elts = std::unique_ptr<TupleStructItems> (
+ new TupleStructItemsNoRange (std::move (self_patterns)));
+ auto pattern = std::unique_ptr<Pattern> (
+ new ReferencePattern (std::unique_ptr<Pattern> (new TupleStructPattern (
+ variant_path, std::move (patterns_elts))),
+ false, false, loc));
+
+ auto block = builder.block (std::move (hash_calls));
+
+ return builder.match_case (std::move (pattern), std::move (block));
+}
+
+MatchCase
+DeriveHash::match_enum_struct (PathInExpression variant_path,
+ const EnumItemStruct &variant)
+{
+ auto field_patterns = std::vector<std::unique_ptr<StructPatternField>> ();
+ auto hash_calls = std::vector<std::unique_ptr<Stmt>> ();
+
+ for (const auto &field : variant.get_struct_fields ())
+ {
+ auto call = hash_call (builder.ref (
+ builder.identifier (field.get_field_name ().as_string ())));
+
+ field_patterns.emplace_back (
+ std::unique_ptr<StructPatternField> (new StructPatternFieldIdent (
+ field.get_field_name (), false /* is_ref? true? */, false, {}, loc)));
+
+ hash_calls.emplace_back (builder.statementify (std::move (call)));
+ }
+
+ auto pattern_elts = StructPatternElements (std::move (field_patterns));
+ auto pattern = std::unique_ptr<Pattern> (
+ new ReferencePattern (std::unique_ptr<Pattern> (new StructPattern (
+ variant_path, loc, pattern_elts)),
+ false, false, loc));
+
+ auto block = builder.block (std::move (hash_calls));
+ return builder.match_case (std::move (pattern), std::move (block));
+}
+
+void
+DeriveHash::visit_enum (Enum &item)
+{
+ // Enums are a bit different: We start by hashing the discriminant value of
+ // the enum instance, and then hash all of the data contained in each of the
+ // enum's variants. For data-less variants, we don't have any data to hash, so
+ // hashing the discriminant value is enough. To access the rest of the
+ // variants' data, we create a match and destructure each internal field and
+ // hash it.
+ //
+ // So for example with the following enum:
+ //
+ // ```rust
+ // enum Foo {
+ // A,
+ // B(i32),
+ // C { a: i32 },
+ // }
+ // ```
+ //
+ // we create the following implementation:
+ //
+ // ```rust
+ // fn hash<H: Hasher>(&self, state: &mut H) {
+ // let discriminant = intrinsics::discriminant_value(&self);
+ // Hash::hash(&discriminant, state);
+ //
+ // match self {
+ // B(self_0) => { Hash::hash(self_0, state); },
+ // C { a } => { Hash::hash(a, state); },
+ // _ => {},
+ // }
+ // }
+ // ```
+ //
+ // Note the extra wildcard pattern to satisfy the exhaust checker.
+
+ auto cases = std::vector<MatchCase> ();
+ auto type_name = item.get_identifier ().as_string ();
+
+ auto intrinsic = ptrify (
+ builder.path_in_expression ({"core", "intrinsics", "discriminant_value"},
+ true));
+
+ auto let_discr
+ = builder.let (builder.identifier_pattern (DeriveHash::discr), nullptr,
+ builder.call (std::move (intrinsic),
+ builder.identifier ("self")));
+
+ auto discr_hash = builder.statementify (
+ hash_call (builder.ref (builder.identifier (DeriveHash::discr))));
+
+ for (auto &variant : item.get_variants ())
+ {
+ auto variant_path
+ = builder.variant_path (type_name,
+ variant->get_identifier ().as_string ());
+
+ switch (variant->get_enum_item_kind ())
+ {
+ case EnumItem::Kind::Identifier:
+ case EnumItem::Kind::Discriminant:
+ // nothing to do in these cases, as we just need to hash the
+ // discriminant value
+ continue;
+ case EnumItem::Kind::Tuple:
+ cases.emplace_back (
+ match_enum_tuple (variant_path,
+ static_cast<EnumItemTuple &> (*variant)));
+ break;
+ case EnumItem::Kind::Struct:
+ cases.emplace_back (
+ match_enum_struct (variant_path,
+ static_cast<EnumItemStruct &> (*variant)));
+ break;
+ }
+ }
+
+ // The extra empty wildcard case
+ cases.emplace_back (
+ builder.match_case (builder.wildcard (), builder.block ()));
+
+ auto match = builder.match (builder.identifier ("self"), std::move (cases));
+
+ auto block
+ = builder.block (vec (std::move (let_discr), std::move (discr_hash)),
+ std::move (match));
+
+ expanded = hash_impl (hash_fn (std::move (block)), type_name,
+ item.get_generic_params ());
+}
+
+void
+DeriveHash::visit_union (Union &item)
+{
+ rust_error_at (item.get_locus (), "derive(Hash) cannot be used on unions");
+}
+
+} // namespace AST
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-derive-hash.h b/gcc/rust/expand/rust-derive-hash.h
new file mode 100644
index 0000000..02b0bee
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-hash.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2025 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_HASH_H
+#define RUST_DERIVE_HASH_H
+
+#include "rust-derive.h"
+
+namespace Rust {
+namespace AST {
+
+class DeriveHash : DeriveVisitor
+{
+public:
+ DeriveHash (location_t loc);
+
+ std::unique_ptr<AST::Item> go (Item &item);
+
+private:
+ std::unique_ptr<Item> expanded;
+
+ constexpr static const char *state = "#state";
+ constexpr static const char *state_type = "#__H";
+ constexpr static const char *discr = "#discr";
+
+ std::unique_ptr<Expr> hash_call (std::unique_ptr<Expr> &&value);
+ std::unique_ptr<AssociatedItem> hash_fn (std::unique_ptr<BlockExpr> &&block);
+ std::unique_ptr<Item>
+ hash_impl (std::unique_ptr<AssociatedItem> &&hash_fn, std::string name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics);
+
+ MatchCase match_enum_tuple (PathInExpression variant_path,
+ const EnumItemTuple &variant);
+ MatchCase match_enum_struct (PathInExpression variant_path,
+ const EnumItemStruct &variant);
+
+ 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_HASH_H
diff --git a/gcc/rust/expand/rust-derive-partial-eq.cc b/gcc/rust/expand/rust-derive-partial-eq.cc
new file mode 100644
index 0000000..ff66faa
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-partial-eq.cc
@@ -0,0 +1,313 @@
+// Copyright (C) 2025 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-partial-eq.h"
+#include "rust-ast.h"
+#include "rust-expr.h"
+#include "rust-item.h"
+#include "rust-operators.h"
+#include "rust-path.h"
+#include "rust-pattern.h"
+#include "rust-system.h"
+
+namespace Rust {
+namespace AST {
+DerivePartialEq::DerivePartialEq (location_t loc) : DeriveVisitor (loc) {}
+
+std::vector<std::unique_ptr<AST::Item>>
+DerivePartialEq::go (Item &item)
+{
+ item.accept_vis (*this);
+
+ return std::move (expanded);
+}
+
+std::vector<std::unique_ptr<Item>>
+DerivePartialEq::partialeq_impls (
+ std::unique_ptr<AssociatedItem> &&eq_fn, std::string name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics)
+{
+ auto eq = builder.type_path (LangItem::Kind::EQ);
+ auto speq = builder.type_path (LangItem::Kind::STRUCTURAL_PEQ);
+
+ auto trait_items = vec (std::move (eq_fn));
+
+ // no extra bound on StructuralPeq
+ auto peq_generics
+ = setup_impl_generics (name, type_generics, builder.trait_bound (eq));
+ auto speq_generics = setup_impl_generics (name, type_generics);
+
+ auto peq = builder.trait_impl (eq, std::move (peq_generics.self_type),
+ std::move (trait_items),
+ std::move (peq_generics.impl));
+
+ auto structural_peq
+ = builder.trait_impl (speq, std::move (speq_generics.self_type), {},
+ std::move (speq_generics.impl));
+
+ return vec (std::move (peq), std::move (structural_peq));
+}
+
+std::unique_ptr<AssociatedItem>
+DerivePartialEq::eq_fn (std::unique_ptr<Expr> &&cmp_expression,
+ std::string type_name)
+{
+ auto block = builder.block (tl::nullopt, std::move (cmp_expression));
+
+ auto self_type
+ = std::unique_ptr<TypeNoBounds> (new TypePath (builder.type_path ("Self")));
+
+ auto params
+ = vec (builder.self_ref_param (),
+ builder.function_param (builder.identifier_pattern ("other"),
+ builder.reference_type (
+ std::move (self_type))));
+
+ return builder.function ("eq", std::move (params),
+ builder.single_type_path ("bool"),
+ std::move (block));
+}
+
+DerivePartialEq::SelfOther
+DerivePartialEq::tuple_indexes (int idx)
+{
+ return SelfOther{
+ builder.tuple_idx ("self", idx),
+ builder.tuple_idx ("other", idx),
+ };
+}
+
+DerivePartialEq::SelfOther
+DerivePartialEq::field_acccesses (const std::string &field_name)
+{
+ return SelfOther{
+ builder.field_access (builder.identifier ("self"), field_name),
+ builder.field_access (builder.identifier ("other"), field_name),
+ };
+}
+
+std::unique_ptr<Expr>
+DerivePartialEq::build_eq_expression (
+ std::vector<SelfOther> &&field_expressions)
+{
+ // for unit structs or empty tuples, this is always true
+ if (field_expressions.empty ())
+ return builder.literal_bool (true);
+
+ auto cmp_expression
+ = builder.comparison_expr (std::move (field_expressions.at (0).self_expr),
+ std::move (field_expressions.at (0).other_expr),
+ ComparisonOperator::EQUAL);
+
+ for (size_t i = 1; i < field_expressions.size (); i++)
+ {
+ auto tmp = builder.comparison_expr (
+ std::move (field_expressions.at (i).self_expr),
+ std::move (field_expressions.at (i).other_expr),
+ ComparisonOperator::EQUAL);
+
+ cmp_expression
+ = builder.boolean_operation (std::move (cmp_expression),
+ std::move (tmp),
+ LazyBooleanOperator::LOGICAL_AND);
+ }
+
+ return cmp_expression;
+}
+
+void
+DerivePartialEq::visit_tuple (TupleStruct &item)
+{
+ auto type_name = item.get_struct_name ().as_string ();
+ auto fields = std::vector<SelfOther> ();
+
+ for (size_t idx = 0; idx < item.get_fields ().size (); idx++)
+ fields.emplace_back (tuple_indexes (idx));
+
+ auto fn = eq_fn (build_eq_expression (std::move (fields)), type_name);
+
+ expanded
+ = partialeq_impls (std::move (fn), type_name, item.get_generic_params ());
+}
+
+void
+DerivePartialEq::visit_struct (StructStruct &item)
+{
+ auto type_name = item.get_struct_name ().as_string ();
+ auto fields = std::vector<SelfOther> ();
+
+ for (auto &field : item.get_fields ())
+ fields.emplace_back (
+ field_acccesses (field.get_field_name ().as_string ()));
+
+ auto fn = eq_fn (build_eq_expression (std::move (fields)), type_name);
+
+ expanded
+ = partialeq_impls (std::move (fn), type_name, item.get_generic_params ());
+}
+
+MatchCase
+DerivePartialEq::match_enum_identifier (
+ PathInExpression variant_path, const std::unique_ptr<EnumItem> &variant)
+{
+ auto inner_ref_patterns
+ = vec (builder.ref_pattern (
+ std::unique_ptr<Pattern> (new PathInExpression (variant_path))),
+ builder.ref_pattern (
+ std::unique_ptr<Pattern> (new PathInExpression (variant_path))));
+
+ auto tuple_items = std::make_unique<TuplePatternItemsMultiple> (
+ std::move (inner_ref_patterns));
+
+ auto pattern = std::make_unique<TuplePattern> (std::move (tuple_items), loc);
+
+ return builder.match_case (std::move (pattern), builder.literal_bool (true));
+}
+
+MatchCase
+DerivePartialEq::match_enum_tuple (PathInExpression variant_path,
+ const EnumItemTuple &variant)
+{
+ auto self_patterns = std::vector<std::unique_ptr<Pattern>> ();
+ auto other_patterns = std::vector<std::unique_ptr<Pattern>> ();
+
+ auto self_other_exprs = std::vector<SelfOther> ();
+
+ for (size_t i = 0; i < variant.get_tuple_fields ().size (); i++)
+ {
+ // The patterns we're creating for each field are `self_<i>` and
+ // `other_<i>` where `i` is the index of the field. It doesn't actually
+ // matter what we use, as long as it's ordered, unique, and that we can
+ // reuse it in the match case's return expression to check that they are
+ // equal.
+
+ auto self_pattern_str = "__self_" + std::to_string (i);
+ auto other_pattern_str = "__other_" + std::to_string (i);
+
+ rust_debug ("]ARTHUR[ %s", self_pattern_str.c_str ());
+
+ self_patterns.emplace_back (
+ builder.identifier_pattern (self_pattern_str));
+ other_patterns.emplace_back (
+ builder.identifier_pattern (other_pattern_str));
+
+ self_other_exprs.emplace_back (SelfOther{
+ builder.identifier (self_pattern_str),
+ builder.identifier (other_pattern_str),
+ });
+ }
+
+ auto self_pattern_items = std::unique_ptr<TupleStructItems> (
+ new TupleStructItemsNoRange (std::move (self_patterns)));
+ auto other_pattern_items = std::unique_ptr<TupleStructItems> (
+ new TupleStructItemsNoRange (std::move (other_patterns)));
+
+ auto self_pattern = std::unique_ptr<Pattern> (
+ new ReferencePattern (std::unique_ptr<Pattern> (new TupleStructPattern (
+ variant_path, std::move (self_pattern_items))),
+ false, false, loc));
+ auto other_pattern = std::unique_ptr<Pattern> (
+ new ReferencePattern (std::unique_ptr<Pattern> (new TupleStructPattern (
+ variant_path, std::move (other_pattern_items))),
+ false, false, loc));
+
+ auto tuple_items = std::make_unique<TuplePatternItemsMultiple> (
+ vec (std::move (self_pattern), std::move (other_pattern)));
+
+ auto pattern = std::make_unique<TuplePattern> (std::move (tuple_items), loc);
+
+ auto expr = build_eq_expression (std::move (self_other_exprs));
+
+ return builder.match_case (std::move (pattern), std::move (expr));
+}
+
+MatchCase
+DerivePartialEq::match_enum_struct (PathInExpression variant_path,
+ const EnumItemStruct &variant)
+{
+ // NOTE: We currently do not support compiling struct patterns where an
+ // identifier is assigned a new pattern, e.g. Bloop { f0: x }
+ // This is what we should be using to compile PartialEq for enum struct
+ // variants, as we need to be comparing the field of each instance meaning we
+ // need to give two different names to two different instances of the same
+ // field. We cannot just use the field's name like we do when deriving
+ // `Clone`.
+
+ rust_unreachable ();
+}
+
+void
+DerivePartialEq::visit_enum (Enum &item)
+{
+ auto cases = std::vector<MatchCase> ();
+ auto type_name = item.get_identifier ().as_string ();
+
+ for (auto &variant : item.get_variants ())
+ {
+ auto variant_path
+ = builder.variant_path (type_name,
+ variant->get_identifier ().as_string ());
+
+ switch (variant->get_enum_item_kind ())
+ {
+ case EnumItem::Kind::Identifier:
+ case EnumItem::Kind::Discriminant:
+ cases.emplace_back (match_enum_identifier (variant_path, variant));
+ break;
+ case EnumItem::Kind::Tuple:
+ cases.emplace_back (
+ match_enum_tuple (variant_path,
+ static_cast<EnumItemTuple &> (*variant)));
+ break;
+ case EnumItem::Kind::Struct:
+ rust_sorry_at (
+ item.get_locus (),
+ "cannot derive(PartialEq) for enum struct variants yet");
+ break;
+ }
+ }
+
+ // NOTE: Mention using discriminant_value and skipping that last case, and
+ // instead skipping all identifiers/discriminant enum items and returning
+ // `true` in the wildcard case
+
+ // In case the two instances of `Self` don't have the same discriminant,
+ // automatically return false.
+ cases.emplace_back (
+ builder.match_case (builder.wildcard (), builder.literal_bool (false)));
+
+ auto match
+ = builder.match (builder.tuple (vec (builder.identifier ("self"),
+ builder.identifier ("other"))),
+ std::move (cases));
+
+ auto fn = eq_fn (std::move (match), type_name);
+
+ expanded
+ = partialeq_impls (std::move (fn), type_name, item.get_generic_params ());
+}
+
+void
+DerivePartialEq::visit_union (Union &item)
+{
+ rust_error_at (item.get_locus (),
+ "derive(PartialEq) cannot be used on unions");
+}
+
+} // namespace AST
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-derive-partial-eq.h b/gcc/rust/expand/rust-derive-partial-eq.h
new file mode 100644
index 0000000..ac963a6
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-partial-eq.h
@@ -0,0 +1,85 @@
+// Copyright (C) 2025 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_PARTIAL_EQ_H
+#define RUST_DERIVE_PARTIAL_EQ_H
+
+#include "rust-derive.h"
+#include "rust-path.h"
+
+namespace Rust {
+namespace AST {
+
+class DerivePartialEq : DeriveVisitor
+{
+public:
+ DerivePartialEq (location_t loc);
+
+ std::vector<std::unique_ptr<AST::Item>> go (Item &item);
+
+private:
+ std::vector<std::unique_ptr<Item>> expanded;
+
+ /**
+ * Generate both an implementation of `PartialEq` and `StructuralPartialEq`
+ * for the given type
+ */
+ std::vector<std::unique_ptr<Item>> partialeq_impls (
+ std::unique_ptr<AssociatedItem> &&eq_fn, std::string name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics);
+
+ std::unique_ptr<AssociatedItem> eq_fn (std::unique_ptr<Expr> &&cmp_expression,
+ std::string type_name);
+
+ /**
+ * A pair of two expressions from each instance being compared. E.g. this
+ * could be `self.0` and `other.0`, or `self.field` and `other.field`
+ */
+ struct SelfOther
+ {
+ std::unique_ptr<Expr> self_expr;
+ std::unique_ptr<Expr> other_expr;
+ };
+
+ SelfOther tuple_indexes (int idx);
+ SelfOther field_acccesses (const std::string &field_name);
+
+ /**
+ * Build a suite of equality arithmetic expressions chained together by a
+ * boolean AND operator
+ */
+ std::unique_ptr<Expr>
+ build_eq_expression (std::vector<SelfOther> &&field_expressions);
+
+ MatchCase match_enum_identifier (PathInExpression variant_path,
+ const std::unique_ptr<EnumItem> &variant);
+ MatchCase match_enum_tuple (PathInExpression variant_path,
+ const EnumItemTuple &variant);
+ MatchCase match_enum_struct (PathInExpression variant_path,
+ const EnumItemStruct &variant);
+
+ 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_PARTIAL_EQ_H
diff --git a/gcc/rust/expand/rust-derive.cc b/gcc/rust/expand/rust-derive.cc
index a378483..015b81e 100644
--- a/gcc/rust/expand/rust-derive.cc
+++ b/gcc/rust/expand/rust-derive.cc
@@ -19,6 +19,11 @@
#include "rust-derive.h"
#include "rust-derive-clone.h"
#include "rust-derive-copy.h"
+#include "rust-derive-debug.h"
+#include "rust-derive-default.h"
+#include "rust-derive-eq.h"
+#include "rust-derive-partial-eq.h"
+#include "rust-derive-hash.h"
namespace Rust {
namespace AST {
@@ -27,28 +32,113 @@ DeriveVisitor::DeriveVisitor (location_t loc)
: loc (loc), builder (Builder (loc))
{}
-std::unique_ptr<Item>
+std::vector<std::unique_ptr<Item>>
DeriveVisitor::derive (Item &item, const Attribute &attr,
BuiltinMacro to_derive)
{
+ auto loc = attr.get_locus ();
+
switch (to_derive)
{
case BuiltinMacro::Clone:
- return DeriveClone (attr.get_locus ()).go (item);
+ return vec (DeriveClone (loc).go (item));
case BuiltinMacro::Copy:
- return DeriveCopy (attr.get_locus ()).go (item);
+ return vec (DeriveCopy (loc).go (item));
case BuiltinMacro::Debug:
+ rust_warning_at (
+ loc, 0,
+ "derive(Debug) is not fully implemented yet and has no effect - only a "
+ "stub implementation will be generated");
+ return vec (DeriveDebug (loc).go (item));
case BuiltinMacro::Default:
+ return vec (DeriveDefault (loc).go (item));
case BuiltinMacro::Eq:
+ return DeriveEq (loc).go (item);
case BuiltinMacro::PartialEq:
+ return DerivePartialEq (loc).go (item);
+ case BuiltinMacro::Hash:
+ return vec (DeriveHash (loc).go (item));
case BuiltinMacro::Ord:
case BuiltinMacro::PartialOrd:
- case BuiltinMacro::Hash:
default:
- rust_sorry_at (attr.get_locus (), "unimplemented builtin derive macro");
- return nullptr;
+ rust_sorry_at (loc, "unimplemented builtin derive macro");
+ return {};
};
}
+DeriveVisitor::ImplGenerics
+DeriveVisitor::setup_impl_generics (
+ const std::string &type_name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics,
+ tl::optional<std::unique_ptr<TypeParamBound>> &&extra_bound) const
+{
+ std::vector<Lifetime> lifetime_args;
+ std::vector<GenericArg> generic_args;
+ std::vector<std::unique_ptr<GenericParam>> impl_generics;
+ for (const auto &generic : type_generics)
+ {
+ switch (generic->get_kind ())
+ {
+ case GenericParam::Kind::Lifetime: {
+ LifetimeParam &lifetime_param = (LifetimeParam &) *generic.get ();
+
+ Lifetime l = builder.new_lifetime (lifetime_param.get_lifetime ());
+ lifetime_args.push_back (std::move (l));
+
+ auto impl_lifetime_param
+ = builder.new_lifetime_param (lifetime_param);
+ impl_generics.push_back (std::move (impl_lifetime_param));
+ }
+ break;
+
+ case GenericParam::Kind::Type: {
+ TypeParam &type_param = (TypeParam &) *generic.get ();
+
+ std::unique_ptr<Type> associated_type = builder.single_type_path (
+ type_param.get_type_representation ().as_string ());
+
+ GenericArg type_arg
+ = GenericArg::create_type (std::move (associated_type));
+ generic_args.push_back (std::move (type_arg));
+
+ std::vector<std::unique_ptr<TypeParamBound>> extra_bounds;
+
+ if (extra_bound)
+ extra_bounds.emplace_back (std::move (*extra_bound));
+
+ auto impl_type_param
+ = builder.new_type_param (type_param, std::move (extra_bounds));
+
+ impl_generics.push_back (std::move (impl_type_param));
+ }
+ break;
+
+ case GenericParam::Kind::Const: {
+ rust_unreachable ();
+
+ // TODO
+ // const ConstGenericParam *const_param
+ // = (const ConstGenericParam *) generic.get ();
+ // std::unique_ptr<Expr> const_expr = nullptr;
+
+ // GenericArg type_arg
+ // = GenericArg::create_const (std::move (const_expr));
+ // generic_args.push_back (std::move (type_arg));
+ }
+ break;
+ }
+ }
+
+ auto generic_args_for_self
+ = GenericArgs (lifetime_args, generic_args, {} /*binding args*/, loc);
+
+ std::unique_ptr<Type> self_type_path
+ = impl_generics.empty ()
+ ? builder.single_type_path (type_name)
+ : builder.single_generic_type_path (type_name, generic_args_for_self);
+
+ return ImplGenerics{std::move (self_type_path), std::move (impl_generics)};
+}
+
} // namespace AST
} // namespace Rust
diff --git a/gcc/rust/expand/rust-derive.h b/gcc/rust/expand/rust-derive.h
index 967064c..5fca49c 100644
--- a/gcc/rust/expand/rust-derive.h
+++ b/gcc/rust/expand/rust-derive.h
@@ -34,8 +34,12 @@ namespace AST {
class DeriveVisitor : public AST::ASTVisitor
{
public:
- static std::unique_ptr<Item> derive (Item &item, const Attribute &derive,
- BuiltinMacro to_derive);
+ /**
+ * Expand a built-in derive macro on an item. This may generate multiple items
+ * which all need to be integrated to the existing AST
+ */
+ static std::vector<std::unique_ptr<Item>>
+ derive (Item &item, const Attribute &derive, BuiltinMacro to_derive);
protected:
DeriveVisitor (location_t loc);
@@ -43,6 +47,29 @@ protected:
location_t loc;
Builder builder;
+ struct ImplGenerics
+ {
+ /* The type we are deriving the impl for */
+ std::unique_ptr<Type> self_type;
+
+ /* Generics for the impl itself */
+ std::vector<std::unique_ptr<GenericParam>> impl;
+ };
+
+ /**
+ * Create the generic parameters for a derive impl block. Derived impl blocks
+ * will often share the same structure of reusing the exact same bounds as
+ * their original type, plus adding an extra one for the trait we are
+ * deriving. For example, when deriving `Clone` on `Foo<T>`, you want to make
+ * sure that you implement `Clone` only if `T: Clone` - so you add an extra
+ * `Clone` bound to all of your generics.
+ */
+ ImplGenerics setup_impl_generics (
+ const std::string &type_name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics,
+ tl::optional<std::unique_ptr<TypeParamBound>> &&extra_bound
+ = tl::nullopt) const;
+
private:
// the 4 "allowed" visitors, which a derive-visitor can specify and override
virtual void visit_struct (StructStruct &struct_item) = 0;
@@ -81,8 +108,6 @@ private:
virtual void visit (Lifetime &lifetime) override final{};
virtual void visit (LifetimeParam &lifetime_param) override final{};
virtual void visit (ConstGenericParam &const_param) override final{};
- virtual void visit (RegularPath &path) override final{};
- virtual void visit (LangItemPath &path) override final{};
virtual void visit (PathInExpression &path) override final{};
virtual void visit (TypePathSegment &segment) override final{};
virtual void visit (TypePathSegmentGeneric &segment) override final{};
@@ -146,6 +171,7 @@ private:
virtual void visit (AwaitExpr &expr) override final{};
virtual void visit (AsyncBlockExpr &expr) override final{};
virtual void visit (InlineAsm &expr) override final{};
+ virtual void visit (LlvmInlineAsm &expr) override final{};
virtual void visit (TypeParam &param) override final{};
virtual void visit (LifetimeWhereClauseItem &item) override final{};
virtual void visit (TypeBoundWhereClauseItem &item) override final{};
diff --git a/gcc/rust/expand/rust-expand-format-args.cc b/gcc/rust/expand/rust-expand-format-args.cc
index c8087ee..af6182f 100644
--- a/gcc/rust/expand/rust-expand-format-args.cc
+++ b/gcc/rust/expand/rust-expand-format-args.cc
@@ -120,7 +120,7 @@ expand_format_args (AST::FormatArgs &fmt,
auto pieces = builder.ref (builder.array (std::move (static_pieces)));
auto args_slice = builder.ref (builder.array (std::move (args_array)));
- auto final_path = make_unique<AST::PathInExpression> (
+ auto final_path = std::make_unique<AST::PathInExpression> (
builder.path_in_expression ({"core", "fmt", "Arguments", "new_v1"}));
auto final_args = std::vector<std::unique_ptr<AST::Expr>> ();
final_args.emplace_back (std::move (pieces));
diff --git a/gcc/rust/expand/rust-expand-visitor.cc b/gcc/rust/expand/rust-expand-visitor.cc
index 38399d0d..42df5e1 100644
--- a/gcc/rust/expand/rust-expand-visitor.cc
+++ b/gcc/rust/expand/rust-expand-visitor.cc
@@ -43,7 +43,7 @@ ExpandVisitor::go (AST::Crate &crate)
visit (crate);
}
-static std::unique_ptr<AST::Item>
+static std::vector<std::unique_ptr<AST::Item>>
builtin_derive_item (AST::Item &item, const AST::Attribute &derive,
BuiltinMacro to_derive)
{
@@ -189,11 +189,12 @@ ExpandVisitor::expand_inner_items (
to_derive.get ().as_string ());
if (maybe_builtin.has_value ())
{
- auto new_item
+ auto new_items
= builtin_derive_item (item, current,
maybe_builtin.value ());
- it = items.insert (it, std::move (new_item));
+ for (auto &&new_item : new_items)
+ it = items.insert (it, std::move (new_item));
}
else
{
@@ -276,12 +277,14 @@ ExpandVisitor::expand_inner_stmts (AST::BlockExpr &expr)
to_derive.get ().as_string ());
if (maybe_builtin.has_value ())
{
- auto new_item
+ auto new_items
= builtin_derive_item (item, current,
maybe_builtin.value ());
+
// this inserts the derive *before* the item - is it a
// problem?
- it = stmts.insert (it, std::move (new_item));
+ for (auto &&new_item : new_items)
+ it = stmts.insert (it, std::move (new_item));
}
else
{
@@ -477,14 +480,18 @@ ExpandVisitor::visit (AST::MacroInvocation &macro_invoc)
void
ExpandVisitor::visit (AST::PathInExpression &path)
{
- for (auto &segment : path.get_segments ())
- if (segment.has_generic_args ())
- expand_generic_args (segment.get_generic_args ());
+ if (!path.is_lang_item ())
+ for (auto &segment : path.get_segments ())
+ if (segment.has_generic_args ())
+ expand_generic_args (segment.get_generic_args ());
}
void
ExpandVisitor::visit (AST::TypePathSegmentGeneric &segment)
-{}
+{
+ if (segment.has_generic_args ())
+ expand_generic_args (segment.get_generic_args ());
+}
void
ExpandVisitor::visit (AST::TypePathSegmentFunction &segment)
@@ -718,6 +725,12 @@ ExpandVisitor::visit (AST::TypeBoundWhereClauseItem &item)
}
void
+ExpandVisitor::visit (AST::Module &module)
+{
+ expand_inner_items (module.get_items ());
+}
+
+void
ExpandVisitor::visit (AST::ExternCrate &crate)
{}
@@ -855,7 +868,7 @@ ExpandVisitor::visit (AST::Trait &trait)
std::function<std::unique_ptr<AST::AssociatedItem> (AST::SingleASTNode)>
extractor
- = [] (AST::SingleASTNode node) { return node.take_trait_item (); };
+ = [] (AST::SingleASTNode node) { return node.take_assoc_item (); };
expand_macro_children (MacroExpander::ContextType::TRAIT,
trait.get_trait_items (), extractor);
@@ -882,7 +895,8 @@ ExpandVisitor::visit (AST::InherentImpl &impl)
expand_where_clause (impl.get_where_clause ());
std::function<std::unique_ptr<AST::AssociatedItem> (AST::SingleASTNode)>
- extractor = [] (AST::SingleASTNode node) { return node.take_impl_item (); };
+ extractor
+ = [] (AST::SingleASTNode node) { return node.take_assoc_item (); };
expand_macro_children (MacroExpander::ContextType::IMPL,
impl.get_impl_items (), extractor);
@@ -910,7 +924,7 @@ ExpandVisitor::visit (AST::TraitImpl &impl)
std::function<std::unique_ptr<AST::AssociatedItem> (AST::SingleASTNode)>
extractor
- = [] (AST::SingleASTNode node) { return node.take_trait_impl_item (); };
+ = [] (AST::SingleASTNode node) { return node.take_assoc_item (); };
expand_macro_children (MacroExpander::ContextType::TRAIT_IMPL,
impl.get_impl_items (), extractor);
diff --git a/gcc/rust/expand/rust-expand-visitor.h b/gcc/rust/expand/rust-expand-visitor.h
index 5fc1011e..ad237c0 100644
--- a/gcc/rust/expand/rust-expand-visitor.h
+++ b/gcc/rust/expand/rust-expand-visitor.h
@@ -237,6 +237,7 @@ public:
void visit (AST::TypeParam &param) override;
void visit (AST::LifetimeWhereClauseItem &) override;
void visit (AST::TypeBoundWhereClauseItem &item) override;
+ void visit (AST::Module &module) override;
void visit (AST::ExternCrate &crate) override;
void visit (AST::UseTreeGlob &) override;
void visit (AST::UseTreeList &) override;
diff --git a/gcc/rust/expand/rust-macro-builtins-asm.cc b/gcc/rust/expand/rust-macro-builtins-asm.cc
index 5ed24d6..e255729 100644
--- a/gcc/rust/expand/rust-macro-builtins-asm.cc
+++ b/gcc/rust/expand/rust-macro-builtins-asm.cc
@@ -17,12 +17,12 @@
// <http://www.gnu.org/licenses/>.
#include "expected.h"
-#include "rust-make-unique.h"
#include "rust-macro-builtins-asm.h"
#include "rust-ast-fragment.h"
#include "rust-ast.h"
#include "rust-fmt.h"
#include "rust-stmt.h"
+#include "rust-parse.h"
namespace Rust {
std::map<AST::InlineAsmOption, std::string> InlineAsmOptionMap{
@@ -40,16 +40,28 @@ std::map<AST::InlineAsmOption, std::string> InlineAsmOptionMap{
std::set<std::string> potentially_nonpromoted_keywords
= {"in", "out", "lateout", "inout", "inlateout", "const", "sym", "label"};
+// Helper function strips the beginning and ending double quotes from a
+// string.
std::string
strip_double_quotes (const std::string &str)
{
- // Helper function strips the beginning and ending double quotes from a
- // string.
std::string result = str;
+ rust_assert (!str.empty ());
+
+ rust_assert (str.front () == '\"');
+ rust_assert (str.back () == '\"');
+
+ // we have to special case empty strings which just contain a set of quotes
+ // so, if the string is "\"\"", just return ""
+ if (result.size () == 2)
+ return "";
+
rust_assert (result.size () >= 3);
+
result.erase (0, 1);
result.erase (result.size () - 1, 1);
+
return result;
}
@@ -241,12 +253,10 @@ parse_reg_operand (InlineAsmContext inline_asm_ctx)
// Loop over and execute the parsing functions, if the parser successfullly
// parses or if the parser fails to parse while it has committed to a token,
// we propogate the result.
- int count = 0;
tl::expected<InlineAsmContext, InlineAsmParseError> parsing_operand (
inline_asm_ctx);
for (auto &parse_func : parse_funcs)
{
- count++;
auto result = parsing_operand.and_then (parse_func);
// Per rust's asm.rs's structure
@@ -324,14 +334,14 @@ parse_reg_operand_in (InlineAsmContext inline_asm_ctx)
// We are sure to be failing a test here, based on asm.rs
// https://github.com/rust-lang/rust/blob/a330e49593ee890f9197727a3a558b6e6b37f843/compiler/rustc_builtin_macros/src/asm.rs#L112
rust_unreachable ();
- return tl::unexpected<InlineAsmParseError> (COMMITTED);
+ // return tl::unexpected<InlineAsmParseError> (COMMITTED);
}
auto expr = parser.parse_expr ();
// TODO: When we've succesfully parse an expr, remember to clone_expr()
// instead of nullptr
- struct AST::InlineAsmOperand::In in (reg, std::move (expr));
+ AST::InlineAsmOperand::In in (reg, std::move (expr));
inline_asm_ctx.inline_asm.operands.emplace_back (in, locus);
return inline_asm_ctx;
}
@@ -355,7 +365,7 @@ parse_reg_operand_out (InlineAsmContext inline_asm_ctx)
// TODO: When we've succesfully parse an expr, remember to clone_expr()
// instead of nullptr
- struct AST::InlineAsmOperand::Out out (reg, false, std::move (expr));
+ AST::InlineAsmOperand::Out out (reg, false, std::move (expr));
inline_asm_ctx.inline_asm.operands.emplace_back (out, locus);
@@ -651,6 +661,15 @@ MacroBuiltin::asm_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
return parse_asm (invoc_locus, invoc, semicolon, is_global_asm);
}
+tl::optional<AST::Fragment>
+MacroBuiltin::llvm_asm_handler (location_t invoc_locus,
+ AST::MacroInvocData &invoc,
+ AST::InvocKind semicolon,
+ AST::AsmKind is_global_asm)
+{
+ return parse_llvm_asm (invoc_locus, invoc, semicolon, is_global_asm);
+}
+
tl::expected<InlineAsmContext, InlineAsmParseError>
parse_asm_arg (InlineAsmContext inline_asm_ctx)
{
@@ -662,6 +681,14 @@ parse_asm_arg (InlineAsmContext inline_asm_ctx)
{
token = parser.peek_current_token ();
+ if (token->get_id () == COLON || token->get_id () == SCOPE_RESOLUTION)
+ {
+ rust_error_at (
+ token->get_locus (),
+ "the legacy LLVM-style %<asm!%> syntax is no longer supported");
+ return tl::unexpected<InlineAsmParseError> (COMMITTED);
+ }
+
// We accept a comma token here.
if (token->get_id () != COMMA
&& inline_asm_ctx.consumed_comma_without_formatted_string)
@@ -858,9 +885,9 @@ parse_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
// properly.
if (semicolon == AST::InvocKind::Semicoloned)
single_vec.emplace_back (AST::SingleASTNode (
- Rust::make_unique<AST::ExprStmt> (std::move (node), invoc_locus,
- semicolon
- == AST::InvocKind::Semicoloned)));
+ std::make_unique<AST::ExprStmt> (std::move (node), invoc_locus,
+ semicolon
+ == AST::InvocKind::Semicoloned)));
else
single_vec.emplace_back (AST::SingleASTNode (std::move (node)));
@@ -953,4 +980,223 @@ validate (InlineAsmContext inline_asm_ctx)
{
return tl::expected<InlineAsmContext, InlineAsmParseError> (inline_asm_ctx);
}
+
+tl::optional<LlvmAsmContext>
+parse_llvm_templates (LlvmAsmContext ctx)
+{
+ auto &parser = ctx.parser;
+
+ auto token = parser.peek_current_token ();
+
+ if (token->get_id () == ctx.last_token_id
+ || token->get_id () != STRING_LITERAL)
+ {
+ return tl::nullopt;
+ }
+
+ ctx.llvm_asm.get_templates ().emplace_back (token->get_locus (),
+ strip_double_quotes (
+ token->as_string ()));
+ ctx.parser.skip_token ();
+
+ token = parser.peek_current_token ();
+ if (token->get_id () != ctx.last_token_id && token->get_id () != COLON
+ && token->get_id () != SCOPE_RESOLUTION)
+ {
+ // We do not handle multiple template string, we provide minimal support
+ // for the black_box intrinsics.
+ rust_unreachable ();
+ }
+
+ return ctx;
+}
+
+tl::optional<LlvmAsmContext>
+parse_llvm_arguments (LlvmAsmContext ctx)
+{
+ auto &parser = ctx.parser;
+ enum State
+ {
+ Templates = 0,
+ Output,
+ Input,
+ Clobbers,
+ Options
+ } current_state
+ = State::Templates;
+
+ while (parser.peek_current_token ()->get_id () != ctx.last_token_id
+ && parser.peek_current_token ()->get_id () != END_OF_FILE)
+ {
+ if (parser.peek_current_token ()->get_id () == SCOPE_RESOLUTION)
+ {
+ parser.skip_token (SCOPE_RESOLUTION);
+ current_state = static_cast<State> (current_state + 2);
+ }
+ else
+ {
+ parser.skip_token (COLON);
+ current_state = static_cast<State> (current_state + 1);
+ }
+
+ switch (current_state)
+ {
+ case State::Output:
+ parse_llvm_outputs (ctx);
+ break;
+ case State::Input:
+ parse_llvm_inputs (ctx);
+ break;
+ case State::Clobbers:
+ parse_llvm_clobbers (ctx);
+ break;
+ case State::Options:
+ parse_llvm_options (ctx);
+ break;
+ case State::Templates:
+ default:
+ rust_unreachable ();
+ }
+ }
+
+ return ctx;
+}
+
+void
+parse_llvm_operands (LlvmAsmContext &ctx, std::vector<AST::LlvmOperand> &result)
+{
+ auto &parser = ctx.parser;
+ auto token = parser.peek_current_token ();
+ while (token->get_id () != COLON && token->get_id () != SCOPE_RESOLUTION
+ && token->get_id () != ctx.last_token_id)
+ {
+ std::string constraint;
+ if (token->get_id () == STRING_LITERAL)
+ {
+ constraint = strip_double_quotes (token->as_string ());
+ }
+ parser.skip_token (STRING_LITERAL);
+ parser.skip_token (LEFT_PAREN);
+
+ token = parser.peek_current_token ();
+
+ ParseRestrictions restrictions;
+ restrictions.expr_can_be_null = true;
+ auto expr = parser.parse_expr ();
+
+ parser.skip_token (RIGHT_PAREN);
+
+ result.emplace_back (constraint, std::move (expr));
+
+ if (parser.peek_current_token ()->get_id () == COMMA)
+ parser.skip_token (COMMA);
+
+ token = parser.peek_current_token ();
+ }
+}
+
+void
+parse_llvm_outputs (LlvmAsmContext &ctx)
+{
+ parse_llvm_operands (ctx, ctx.llvm_asm.get_outputs ());
+}
+
+void
+parse_llvm_inputs (LlvmAsmContext &ctx)
+{
+ parse_llvm_operands (ctx, ctx.llvm_asm.get_inputs ());
+}
+
+void
+parse_llvm_clobbers (LlvmAsmContext &ctx)
+{
+ auto &parser = ctx.parser;
+ auto token = parser.peek_current_token ();
+ while (token->get_id () != COLON && token->get_id () != ctx.last_token_id)
+ {
+ if (token->get_id () == STRING_LITERAL)
+ {
+ ctx.llvm_asm.get_clobbers ().push_back (
+ {strip_double_quotes (token->as_string ()), token->get_locus ()});
+ }
+ parser.skip_token (STRING_LITERAL);
+ token = parser.peek_current_token ();
+ }
+}
+
+void
+parse_llvm_options (LlvmAsmContext &ctx)
+{
+ auto &parser = ctx.parser;
+ auto token = parser.peek_current_token ();
+
+ while (token->get_id () != ctx.last_token_id)
+ {
+ if (token->get_id () == STRING_LITERAL)
+ {
+ auto token_str = strip_double_quotes (token->as_string ());
+
+ if (token_str == "volatile")
+ ctx.llvm_asm.set_volatile (true);
+ else if (token_str == "alignstack")
+ ctx.llvm_asm.set_align_stack (true);
+ else if (token_str == "intel")
+ ctx.llvm_asm.set_dialect (AST::LlvmInlineAsm::Dialect::Intel);
+ else
+ rust_error_at (token->get_locus (),
+ "Unknown llvm assembly option %qs",
+ token_str.c_str ());
+ }
+ parser.skip_token (STRING_LITERAL);
+
+ token = parser.peek_current_token ();
+
+ if (token->get_id () == ctx.last_token_id)
+ continue;
+ parser.skip_token (COMMA);
+ }
+
+ parser.skip_token ();
+}
+
+tl::optional<AST::Fragment>
+parse_llvm_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
+ AST::InvocKind semicolon, AST::AsmKind is_global_asm)
+{
+ MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
+ Parser<MacroInvocLexer> parser (lex);
+ auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
+
+ AST::LlvmInlineAsm llvm_asm{invoc_locus};
+
+ auto asm_ctx = LlvmAsmContext (llvm_asm, parser, last_token_id);
+
+ auto resulting_context
+ = parse_llvm_templates (asm_ctx).and_then (parse_llvm_arguments);
+
+ if (resulting_context)
+ {
+ auto node = (*resulting_context).llvm_asm.clone_expr_without_block ();
+
+ std::vector<AST::SingleASTNode> single_vec = {};
+
+ // If the macro invocation has a semicolon (`asm!("...");`), then we
+ // need to make it a statement. This way, it will be expanded
+ // properly.
+ if (semicolon == AST::InvocKind::Semicoloned)
+ single_vec.emplace_back (AST::SingleASTNode (
+ std::make_unique<AST::ExprStmt> (std::move (node), invoc_locus,
+ semicolon
+ == AST::InvocKind::Semicoloned)));
+ else
+ single_vec.emplace_back (AST::SingleASTNode (std::move (node)));
+
+ AST::Fragment fragment_ast
+ = AST::Fragment (single_vec,
+ std::vector<std::unique_ptr<AST::Token>> ());
+ return fragment_ast;
+ }
+ return tl::nullopt;
+}
+
} // namespace Rust
diff --git a/gcc/rust/expand/rust-macro-builtins-asm.h b/gcc/rust/expand/rust-macro-builtins-asm.h
index 8081dae..bd64a7f 100644
--- a/gcc/rust/expand/rust-macro-builtins-asm.h
+++ b/gcc/rust/expand/rust-macro-builtins-asm.h
@@ -172,4 +172,36 @@ tl::optional<std::string>
parse_label (Parser<MacroInvocLexer> &parser, TokenId last_token_id,
InlineAsmContext &inline_asm_ctx);
+// LLVM ASM bits
+
+class LlvmAsmContext
+{
+public:
+ AST::LlvmInlineAsm &llvm_asm;
+ Parser<MacroInvocLexer> &parser;
+ int last_token_id;
+
+public:
+ LlvmAsmContext (AST::LlvmInlineAsm &llvm_asm, Parser<MacroInvocLexer> &parser,
+ int last_token_id)
+ : llvm_asm (llvm_asm), parser (parser), last_token_id (last_token_id)
+ {}
+};
+
+void
+parse_llvm_outputs (LlvmAsmContext &ctx);
+
+void
+parse_llvm_inputs (LlvmAsmContext &ctx);
+
+void
+parse_llvm_clobbers (LlvmAsmContext &ctx);
+
+void
+parse_llvm_options (LlvmAsmContext &ctx);
+
+WARN_UNUSED_RESULT tl::optional<AST::Fragment>
+parse_llvm_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
+ AST::InvocKind semicolon, AST::AsmKind is_global_asm);
+
} // namespace Rust
diff --git a/gcc/rust/expand/rust-macro-builtins-format-args.cc b/gcc/rust/expand/rust-macro-builtins-format-args.cc
index 8eb32d5..3e1249d 100644
--- a/gcc/rust/expand/rust-macro-builtins-format-args.cc
+++ b/gcc/rust/expand/rust-macro-builtins-format-args.cc
@@ -55,6 +55,8 @@ format_args_parse_arguments (AST::MacroInvocData &invoc)
if (parser.peek_current_token ()->get_id () == STRING_LITERAL)
format_expr = parser.parse_literal_expr ();
+ rust_assert (format_expr);
+
// TODO(Arthur): Clean this up - if we haven't parsed a string literal but a
// macro invocation, what do we do here? return a tl::unexpected?
auto format_str = static_cast<AST::LiteralExpr &> (*format_expr)
@@ -81,6 +83,11 @@ format_args_parse_arguments (AST::MacroInvocData &invoc)
{
parser.skip_token (COMMA);
+ // Check in case of an extraneous comma in the args list, which is
+ // allowed - format_args!("fmt", arg, arg2,)
+ if (parser.peek_current_token ()->get_id () == last_token_id)
+ break;
+
if (parser.peek_current_token ()->get_id () == IDENTIFIER
&& parser.peek (1)->get_id () == EQUAL)
{
diff --git a/gcc/rust/expand/rust-macro-builtins-helpers.cc b/gcc/rust/expand/rust-macro-builtins-helpers.cc
index 55d113c..864379a 100644
--- a/gcc/rust/expand/rust-macro-builtins-helpers.cc
+++ b/gcc/rust/expand/rust-macro-builtins-helpers.cc
@@ -36,7 +36,7 @@ check_for_eager_invocations (
std::vector<std::unique_ptr<AST::MacroInvocation>> pending;
for (auto &expr : expressions)
- if (expr->get_ast_kind () == AST::Kind::MACRO_INVOCATION)
+ if (expr->get_expr_kind () == AST::Expr::Kind::MacroInvocation)
pending.emplace_back (std::unique_ptr<AST::MacroInvocation> (
static_cast<AST::MacroInvocation *> (expr->clone_expr ().release ())));
diff --git a/gcc/rust/expand/rust-macro-builtins-helpers.h b/gcc/rust/expand/rust-macro-builtins-helpers.h
index ee5cae7..429537e 100644
--- a/gcc/rust/expand/rust-macro-builtins-helpers.h
+++ b/gcc/rust/expand/rust-macro-builtins-helpers.h
@@ -29,7 +29,6 @@
#include "rust-macro-invoc-lexer.h"
#include "rust-macro.h"
#include "rust-parse.h"
-#include "rust-session-manager.h"
#include "rust-system.h"
#include "rust-token.h"
namespace Rust {
diff --git a/gcc/rust/expand/rust-macro-builtins-include.cc b/gcc/rust/expand/rust-macro-builtins-include.cc
index 4719b0e..2ab2a3a 100644
--- a/gcc/rust/expand/rust-macro-builtins-include.cc
+++ b/gcc/rust/expand/rust-macro-builtins-include.cc
@@ -20,6 +20,7 @@
#include "rust-common.h"
#include "rust-macro-builtins.h"
#include "rust-macro-builtins-helpers.h"
+#include "rust-session-manager.h"
#include "optional.h"
namespace Rust {
/* Expand builtin macro include_bytes!("filename"), which includes the contents
diff --git a/gcc/rust/expand/rust-macro-builtins-log-debug.cc b/gcc/rust/expand/rust-macro-builtins-log-debug.cc
index 49670d2..3d7b54f 100644
--- a/gcc/rust/expand/rust-macro-builtins-log-debug.cc
+++ b/gcc/rust/expand/rust-macro-builtins-log-debug.cc
@@ -30,4 +30,4 @@ MacroBuiltin::assert_handler (location_t invoc_locus,
return AST::Fragment::create_error ();
}
-} // namespace Rust \ No newline at end of file
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-macro-builtins-utility.cc b/gcc/rust/expand/rust-macro-builtins-utility.cc
index 2da7d18..b20b479 100644
--- a/gcc/rust/expand/rust-macro-builtins-utility.cc
+++ b/gcc/rust/expand/rust-macro-builtins-utility.cc
@@ -17,8 +17,10 @@
// <http://www.gnu.org/licenses/>.
#include "rust-fmt.h"
+#include "rust-ast-builder.h"
#include "rust-macro-builtins.h"
#include "rust-macro-builtins-helpers.h"
+#include "rust-session-manager.h"
namespace Rust {
@@ -117,7 +119,7 @@ MacroBuiltin::concat_handler (location_t invoc_locus,
for (auto &expr : expanded_expr)
{
if (!expr->is_literal ()
- && expr->get_ast_kind () != AST::Kind::MACRO_INVOCATION)
+ && expr->get_expr_kind () != AST::Expr::Kind::MacroInvocation)
{
has_error = true;
rust_error_at (expr->get_locus (), "expected a literal");
@@ -226,6 +228,83 @@ MacroBuiltin::env_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
return AST::Fragment ({node}, std::move (tok));
}
+/* Expand builtin macro option_env!(), which inspects an environment variable at
+ compile time. */
+tl::optional<AST::Fragment>
+MacroBuiltin::option_env_handler (location_t invoc_locus,
+ AST::MacroInvocData &invoc,
+ AST::InvocKind semicolon)
+{
+ auto invoc_token_tree = invoc.get_delim_tok_tree ();
+ MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
+ Parser<MacroInvocLexer> parser (lex);
+
+ auto last_token_id = macro_end_token (invoc_token_tree, parser);
+ std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
+ bool has_error = false;
+
+ auto start = lex.get_offs ();
+ auto expanded_expr = try_expand_many_expr (parser, last_token_id,
+ invoc.get_expander (), has_error);
+ auto end = lex.get_offs ();
+
+ auto tokens = lex.get_token_slice (start, end);
+
+ if (has_error)
+ return AST::Fragment::create_error ();
+
+ auto pending = check_for_eager_invocations (expanded_expr);
+ if (!pending.empty ())
+ return make_eager_builtin_invocation (BuiltinMacro::OptionEnv, invoc_locus,
+ invoc_token_tree,
+ std::move (pending));
+
+ if (expanded_expr.size () != 1)
+ {
+ rust_error_at (invoc_locus, "%<option_env!%> takes 1 argument");
+ return AST::Fragment::create_error ();
+ }
+
+ if (expanded_expr.size () > 0)
+ if (!(lit_expr
+ = try_extract_string_literal_from_fragment (invoc_locus,
+ expanded_expr[0])))
+ return AST::Fragment::create_error ();
+
+ parser.skip_token (last_token_id);
+
+ auto env_value = getenv (lit_expr->as_string ().c_str ());
+ AST::Builder b (invoc_locus);
+
+ if (env_value == nullptr)
+ {
+ auto none_expr = std::unique_ptr<AST::Expr> (
+ new AST::PathInExpression (LangItem::Kind::OPTION_NONE, {},
+ invoc_locus));
+
+ auto node = AST::SingleASTNode (std::move (none_expr));
+ std::vector<AST::SingleASTNode> nodes;
+ nodes.push_back (node);
+
+ return AST::Fragment (nodes, std::vector<std::unique_ptr<AST::Token>> ());
+ }
+ std::vector<std::unique_ptr<AST::Expr>> args;
+ args.push_back (b.literal_string (env_value));
+
+ std::unique_ptr<AST::Expr> some_expr
+ = b.call (std::unique_ptr<AST::Expr> (
+ new AST::PathInExpression (LangItem::Kind::OPTION_SOME, {},
+ invoc_locus)),
+ std::move (args));
+
+ auto node = AST::SingleASTNode (std::move (some_expr));
+
+ std::vector<AST::SingleASTNode> nodes;
+ nodes.push_back (node);
+
+ return AST::Fragment (nodes, std::vector<std::unique_ptr<AST::Token>> ());
+}
+
tl::optional<AST::Fragment>
MacroBuiltin::cfg_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
AST::InvocKind semicolon)
@@ -296,4 +375,4 @@ MacroBuiltin::stringify_handler (location_t invoc_locus,
return AST::Fragment ({node}, std::move (token));
}
-} // namespace Rust \ No newline at end of file
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-macro-builtins.cc b/gcc/rust/expand/rust-macro-builtins.cc
index 2457bc0..b58ed71 100644
--- a/gcc/rust/expand/rust-macro-builtins.cc
+++ b/gcc/rust/expand/rust-macro-builtins.cc
@@ -62,6 +62,8 @@ const BiMap<std::string, BuiltinMacro> MacroBuiltin::builtins = {{
{"concat_idents", BuiltinMacro::ConcatIdents},
{"module_path", BuiltinMacro::ModulePath},
{"asm", BuiltinMacro::Asm},
+ // FIXME: Is that okay
+ {"llvm_asm", BuiltinMacro::Asm},
{"global_asm", BuiltinMacro::GlobalAsm},
{"log_syntax", BuiltinMacro::LogSyntax},
{"trace_macros", BuiltinMacro::TraceMacros},
@@ -81,7 +83,6 @@ const BiMap<std::string, BuiltinMacro> MacroBuiltin::builtins = {{
{"Ord", BuiltinMacro::Ord},
{"PartialOrd", BuiltinMacro::PartialOrd},
{"Hash", BuiltinMacro::Hash},
-
}};
AST::MacroTranscriberFunc
@@ -102,6 +103,15 @@ inline_asm_maker (AST::AsmKind global_asm)
};
}
+AST::MacroTranscriberFunc
+inline_llvm_asm_maker (AST::AsmKind global_asm)
+{
+ return [global_asm] (location_t loc, AST::MacroInvocData &invoc,
+ AST::InvocKind semicolon) {
+ return MacroBuiltin::llvm_asm_handler (loc, invoc, semicolon, global_asm);
+ };
+}
+
std::unordered_map<std::string, AST::MacroTranscriberFunc>
MacroBuiltin::builtin_transcribers = {
{"assert", MacroBuiltin::assert_handler},
@@ -119,9 +129,11 @@ std::unordered_map<std::string, AST::MacroTranscriberFunc>
{"format_args", format_args_maker (AST::FormatArgs::Newline::No)},
{"format_args_nl", format_args_maker (AST::FormatArgs::Newline::Yes)},
{"asm", inline_asm_maker (AST::AsmKind::Inline)},
+ // FIXME: Is that okay?
+ {"llvm_asm", inline_llvm_asm_maker (AST::AsmKind::Inline)},
{"global_asm", inline_asm_maker (AST::AsmKind::Global)},
+ {"option_env", MacroBuiltin::option_env_handler},
/* Unimplemented macro builtins */
- {"option_env", MacroBuiltin::sorry},
{"concat_idents", MacroBuiltin::sorry},
{"module_path", MacroBuiltin::sorry},
{"log_syntax", MacroBuiltin::sorry},
@@ -133,6 +145,7 @@ std::unordered_map<std::string, AST::MacroTranscriberFunc>
{"cfg_accessible", MacroBuiltin::sorry},
{"rustc_const_stable", MacroBuiltin::sorry},
{"rustc_const_unstable", MacroBuiltin::sorry},
+ {"track_caller", MacroBuiltin::sorry},
/* Derive builtins do not need a real transcriber, but still need one. It
should however never be called since builtin derive macros get expanded
differently, and benefit from knowing on what kind of items they are
diff --git a/gcc/rust/expand/rust-macro-builtins.h b/gcc/rust/expand/rust-macro-builtins.h
index 6a9b31c..541e956 100644
--- a/gcc/rust/expand/rust-macro-builtins.h
+++ b/gcc/rust/expand/rust-macro-builtins.h
@@ -159,6 +159,10 @@ public:
AST::MacroInvocData &invoc,
AST::InvocKind semicolon);
+ static tl::optional<AST::Fragment>
+ option_env_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
+ AST::InvocKind semicolon);
+
static tl::optional<AST::Fragment> cfg_handler (location_t invoc_locus,
AST::MacroInvocData &invoc,
AST::InvocKind semicolon);
@@ -177,6 +181,10 @@ public:
AST::AsmKind is_global_asm);
static tl::optional<AST::Fragment>
+ llvm_asm_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
+ AST::InvocKind semicolon, AST::AsmKind is_global_asm);
+
+ static tl::optional<AST::Fragment>
format_args_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
AST::InvocKind semicolon, AST::FormatArgs::Newline nl);
diff --git a/gcc/rust/expand/rust-macro-expand.cc b/gcc/rust/expand/rust-macro-expand.cc
index e86bfcb..673b8fb 100644
--- a/gcc/rust/expand/rust-macro-expand.cc
+++ b/gcc/rust/expand/rust-macro-expand.cc
@@ -23,11 +23,12 @@
#include "rust-ast-full.h"
#include "rust-ast-visitor.h"
#include "rust-diagnostics.h"
+#include "rust-macro.h"
#include "rust-parse.h"
#include "rust-cfg-strip.h"
#include "rust-early-name-resolver.h"
-#include "rust-session-manager.h"
#include "rust-proc-macro.h"
+#include "rust-token-tree-desugar.h"
namespace Rust {
@@ -78,7 +79,10 @@ MacroExpander::expand_decl_macro (location_t invoc_locus,
* trees.
*/
- AST::DelimTokenTree &invoc_token_tree = invoc.get_delim_tok_tree ();
+ AST::DelimTokenTree &invoc_token_tree_sugar = invoc.get_delim_tok_tree ();
+
+ // We must first desugar doc comments into proper attributes
+ auto invoc_token_tree = AST::TokenTreeDesugar ().go (invoc_token_tree_sugar);
// find matching arm
AST::MacroRule *matched_rule = nullptr;
@@ -119,7 +123,7 @@ MacroExpander::expand_decl_macro (location_t invoc_locus,
for (auto &ent : matched_fragments)
matched_fragments_ptr.emplace (ent.first, ent.second.get ());
- return transcribe_rule (*matched_rule, invoc_token_tree,
+ return transcribe_rule (rules_def, *matched_rule, invoc_token_tree,
matched_fragments_ptr, semicolon, peek_context ());
}
@@ -621,9 +625,10 @@ MacroExpander::match_n_matches (Parser<MacroInvocLexer> &parser,
// matched fragment get the offset in the token stream
size_t offs_end = source.get_offs ();
- sub_stack.insert_metavar (
- MatchedFragment (fragment->get_ident ().as_string (),
- offs_begin, offs_end));
+ if (valid_current_match)
+ sub_stack.insert_metavar (
+ MatchedFragment (fragment->get_ident ().as_string (),
+ offs_begin, offs_end));
}
break;
@@ -650,15 +655,15 @@ MacroExpander::match_n_matches (Parser<MacroInvocLexer> &parser,
}
auto old_stack = sub_stack.pop ();
- // nest metavars into repetitions
- for (auto &ent : old_stack)
- sub_stack.append_fragment (ent.first, std::move (ent.second));
-
// If we've encountered an error once, stop trying to match more
// repetitions
if (!valid_current_match)
break;
+ // nest metavars into repetitions
+ for (auto &ent : old_stack)
+ sub_stack.append_fragment (ent.first, std::move (ent.second));
+
match_amount++;
// Break early if we notice there's too many expressions already
@@ -1024,7 +1029,8 @@ tokens_to_str (std::vector<std::unique_ptr<AST::Token>> &tokens)
AST::Fragment
MacroExpander::transcribe_rule (
- AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
+ AST::MacroRulesDefinition &definition, AST::MacroRule &match_rule,
+ AST::DelimTokenTree &invoc_token_tree,
std::map<std::string, MatchedFragmentContainer *> &matched_fragments,
AST::InvocKind invoc_kind, ContextType ctx)
{
@@ -1038,8 +1044,8 @@ MacroExpander::transcribe_rule (
auto invoc_stream = invoc_token_tree.to_token_stream ();
auto macro_rule_tokens = transcribe_tree.to_token_stream ();
- auto substitute_context
- = SubstituteCtx (invoc_stream, macro_rule_tokens, matched_fragments);
+ auto substitute_context = SubstituteCtx (invoc_stream, macro_rule_tokens,
+ matched_fragments, definition);
std::vector<std::unique_ptr<AST::Token>> substituted_tokens
= substitute_context.substitute_tokens ();
diff --git a/gcc/rust/expand/rust-macro-expand.h b/gcc/rust/expand/rust-macro-expand.h
index 5e12790..360294c 100644
--- a/gcc/rust/expand/rust-macro-expand.h
+++ b/gcc/rust/expand/rust-macro-expand.h
@@ -331,7 +331,8 @@ struct MacroExpander
AST::DelimTokenTree &invoc_token_tree);
AST::Fragment transcribe_rule (
- AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
+ AST::MacroRulesDefinition &definition, AST::MacroRule &match_rule,
+ AST::DelimTokenTree &invoc_token_tree,
std::map<std::string, MatchedFragmentContainer *> &matched_fragments,
AST::InvocKind invoc_kind, ContextType ctx);
diff --git a/gcc/rust/expand/rust-macro-substitute-ctx.cc b/gcc/rust/expand/rust-macro-substitute-ctx.cc
index a06f831..02e4e3b 100644
--- a/gcc/rust/expand/rust-macro-substitute-ctx.cc
+++ b/gcc/rust/expand/rust-macro-substitute-ctx.cc
@@ -17,10 +17,47 @@
// <http://www.gnu.org/licenses/>.
#include "rust-macro-substitute-ctx.h"
+#include "input.h"
+#include "rust-hir-map.h"
+#include "rust-token.h"
namespace Rust {
bool
+SubstituteCtx::substitute_dollar_crate (
+ std::vector<std::unique_ptr<AST::Token>> &expanded)
+{
+ auto &mappings = Analysis::Mappings::get ();
+
+ auto def_crate = mappings.lookup_macro_def_crate (definition.get_node_id ());
+ auto current_crate = mappings.get_current_crate ();
+
+ rust_assert (def_crate);
+
+ // If we're expanding a macro defined in the current crate which uses $crate,
+ // we can just replace the metavar with the `crate` path segment. Otherwise,
+ // use the fully qualified extern-crate lookup path `::<crate_name>`
+ if (*def_crate == current_crate)
+ {
+ expanded.push_back (std::make_unique<AST::Token> (
+ Rust::Token::make_identifier (UNKNOWN_LOCATION, "crate")));
+ }
+ else
+ {
+ auto name = mappings.get_crate_name (*def_crate);
+
+ rust_assert (name);
+
+ expanded.push_back (std::make_unique<AST::Token> (
+ Rust::Token::make (SCOPE_RESOLUTION, UNKNOWN_LOCATION)));
+ expanded.push_back (std::make_unique<AST::Token> (
+ Rust::Token::make_identifier (UNKNOWN_LOCATION, std::string (*name))));
+ }
+
+ return true;
+}
+
+bool
SubstituteCtx::substitute_metavar (
std::unique_ptr<AST::Token> &metavar,
std::vector<std::unique_ptr<AST::Token>> &expanded)
@@ -30,14 +67,15 @@ SubstituteCtx::substitute_metavar (
auto it = fragments.find (metavar_name);
if (it == fragments.end ())
{
- // fail to substitute
+ // fail to substitute, unless we are dealing with a special-case metavar
+ // like $crate
- // HACK: substitute ($ crate) => (crate)
- if (metavar->get_id () != CRATE)
- return false;
+ if (metavar->get_id () == CRATE)
+ return substitute_dollar_crate (expanded);
expanded.push_back (metavar->clone_token ());
- return true;
+
+ return false;
}
else
{
@@ -187,7 +225,8 @@ SubstituteCtx::substitute_repetition (
kv_match.second->get_fragments ().at (i).get ());
}
- auto substitute_context = SubstituteCtx (input, new_macro, sub_map);
+ auto substitute_context
+ = SubstituteCtx (input, new_macro, sub_map, definition);
auto new_tokens = substitute_context.substitute_tokens ();
// Skip the first repetition, but add the separator to the expanded
diff --git a/gcc/rust/expand/rust-macro-substitute-ctx.h b/gcc/rust/expand/rust-macro-substitute-ctx.h
index e3100a3..c5c4956 100644
--- a/gcc/rust/expand/rust-macro-substitute-ctx.h
+++ b/gcc/rust/expand/rust-macro-substitute-ctx.h
@@ -18,6 +18,7 @@
#include "rust-ast.h"
#include "rust-macro-expand.h"
+#include "rust-macro.h"
namespace Rust {
class SubstituteCtx
@@ -25,6 +26,7 @@ class SubstituteCtx
std::vector<std::unique_ptr<AST::Token>> &input;
std::vector<std::unique_ptr<AST::Token>> &macro;
std::map<std::string, MatchedFragmentContainer *> &fragments;
+ AST::MacroRulesDefinition &definition;
/**
* Find the repetition amount to use when expanding a repetition, and
@@ -40,11 +42,28 @@ class SubstituteCtx
public:
SubstituteCtx (std::vector<std::unique_ptr<AST::Token>> &input,
std::vector<std::unique_ptr<AST::Token>> &macro,
- std::map<std::string, MatchedFragmentContainer *> &fragments)
- : input (input), macro (macro), fragments (fragments)
+ std::map<std::string, MatchedFragmentContainer *> &fragments,
+ AST::MacroRulesDefinition &definition)
+ : input (input), macro (macro), fragments (fragments),
+ definition (definition)
{}
/**
+ * Special-case the $crate metavar to expand to the name of the crate in which
+ * the macro was defined.
+ *
+ * https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.hygiene.crate
+ *
+ *
+ * @param expanded Reference to a vector upon which expanded tokens will be
+ * pushed
+ *
+ * @return True if the substitution succeeded
+ */
+ bool
+ substitute_dollar_crate (std::vector<std::unique_ptr<AST::Token>> &expanded);
+
+ /**
* Substitute a metavariable by its given fragment in a transcribing context,
* i.e. replacing $var with the associated fragment.
*
@@ -52,7 +71,7 @@ public:
* @param expanded Reference to a vector upon which expanded tokens will be
* pushed
*
- * @return True iff the substitution succeeded
+ * @return True if the substitution succeeded
*/
bool substitute_metavar (std::unique_ptr<AST::Token> &metavar,
std::vector<std::unique_ptr<AST::Token>> &expanded);
diff --git a/gcc/rust/expand/rust-token-tree-desugar.cc b/gcc/rust/expand/rust-token-tree-desugar.cc
new file mode 100644
index 0000000..3b47180
--- /dev/null
+++ b/gcc/rust/expand/rust-token-tree-desugar.cc
@@ -0,0 +1,72 @@
+// Copyright (C) 2025 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-token-tree-desugar.h"
+#include "rust-ast.h"
+#include "rust-token.h"
+
+namespace Rust {
+namespace AST {
+
+DelimTokenTree
+TokenTreeDesugar::go (DelimTokenTree &tts)
+{
+ tts.accept_vis (*this);
+
+ return DelimTokenTree (tts.get_delim_type (), std::move (desugared),
+ tts.get_locus ());
+}
+
+void
+TokenTreeDesugar::append (TokenPtr &&new_token)
+{
+ desugared.emplace_back (std::make_unique<Token> (std::move (new_token)));
+}
+
+void
+TokenTreeDesugar::append (std::unique_ptr<TokenTree> &&new_token)
+{
+ desugared.emplace_back (std::move (new_token));
+}
+
+void
+TokenTreeDesugar::visit (Token &tts)
+{
+ if (tts.get_id () == TokenId::OUTER_DOC_COMMENT
+ || tts.get_id () == TokenId::INNER_DOC_COMMENT)
+ {
+ append (Rust::Token::make (TokenId::HASH, tts.get_locus ()));
+
+ if (tts.get_id () == TokenId::INNER_DOC_COMMENT)
+ append (Rust::Token::make (EXCLAM, tts.get_locus ()));
+
+ append (Rust::Token::make (TokenId::LEFT_SQUARE, tts.get_locus ()));
+ append (Rust::Token::make_identifier (tts.get_locus (), "doc"));
+ append (Rust::Token::make (TokenId::EQUAL, tts.get_locus ()));
+ append (Rust::Token::make_string (tts.get_locus (),
+ std::string (tts.get_str ())));
+ append (Rust::Token::make (TokenId::RIGHT_SQUARE, tts.get_locus ()));
+ }
+ else
+ {
+ append (tts.clone_token ());
+ }
+}
+
+}; // namespace AST
+}; // namespace Rust
diff --git a/gcc/rust/expand/rust-token-tree-desugar.h b/gcc/rust/expand/rust-token-tree-desugar.h
new file mode 100644
index 0000000..ccba53b
--- /dev/null
+++ b/gcc/rust/expand/rust-token-tree-desugar.h
@@ -0,0 +1,55 @@
+// Copyright (C) 2025 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_TOKEN_TREE_DESUGAR_H
+#define RUST_TOKEN_TREE_DESUGAR_H
+
+#include "rust-ast-visitor.h"
+#include "rust-system.h"
+#include "rust-ast.h"
+
+namespace Rust {
+namespace AST {
+
+/**
+ * Desugar a given token-tree before parsing it for a macro invocation. At the
+ * moment, the sole purpose of this desugar is to transform doc-comments into
+ * their attribute form (/// comment -> #[doc = "comment"])
+ */
+class TokenTreeDesugar : public DefaultASTVisitor
+{
+public:
+ TokenTreeDesugar () : desugared (std::vector<std::unique_ptr<TokenTree>> ())
+ {}
+
+ DelimTokenTree go (DelimTokenTree &tts);
+
+private:
+ std::vector<std::unique_ptr<TokenTree>> desugared;
+ void append (TokenPtr &&new_token);
+ void append (std::unique_ptr<TokenTree> &&new_token);
+
+ using DefaultASTVisitor::visit;
+
+ virtual void visit (Token &tts) override;
+};
+
+}; // namespace AST
+}; // namespace Rust
+
+#endif //! RUST_TOKEN_TREE_DESUGAR_H