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.cc36
-rw-r--r--gcc/rust/expand/rust-cfg-strip.h2
-rw-r--r--gcc/rust/expand/rust-derive-clone.cc263
-rw-r--r--gcc/rust/expand/rust-derive-clone.h22
-rw-r--r--gcc/rust/expand/rust-derive-copy.cc40
-rw-r--r--gcc/rust/expand/rust-derive-copy.h4
-rw-r--r--gcc/rust/expand/rust-derive-debug.cc122
-rw-r--r--gcc/rust/expand/rust-derive-debug.h55
-rw-r--r--gcc/rust/expand/rust-derive-default.cc173
-rw-r--r--gcc/rust/expand/rust-derive-default.h58
-rw-r--r--gcc/rust/expand/rust-derive-eq.cc217
-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.h31
-rw-r--r--gcc/rust/expand/rust-expand-format-args.cc2
-rw-r--r--gcc/rust/expand/rust-expand-visitor.cc37
-rw-r--r--gcc/rust/expand/rust-expand-visitor.h3
-rw-r--r--gcc/rust/expand/rust-macro-builtins-asm.cc226
-rw-r--r--gcc/rust/expand/rust-macro-builtins-asm.h13
-rw-r--r--gcc/rust/expand/rust-macro-builtins-helpers.cc9
-rw-r--r--gcc/rust/expand/rust-macro-builtins-helpers.h4
-rw-r--r--gcc/rust/expand/rust-macro-builtins-include.cc55
-rw-r--r--gcc/rust/expand/rust-macro-builtins-utility.cc83
-rw-r--r--gcc/rust/expand/rust-macro-builtins.cc6
-rw-r--r--gcc/rust/expand/rust-macro-builtins.h4
-rw-r--r--gcc/rust/expand/rust-macro-expand.cc18
-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
33 files changed, 2335 insertions, 163 deletions
diff --git a/gcc/rust/expand/rust-cfg-strip.cc b/gcc/rust/expand/rust-cfg-strip.cc
index 8abc5cb..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"
@@ -195,6 +196,26 @@ CfgStrip::maybe_strip_struct_fields (std::vector<AST::StructField> &fields)
}
void
+CfgStrip::maybe_strip_struct_expr_fields (
+ std::vector<std::unique_ptr<AST::StructExprField>> &fields)
+{
+ for (auto it = fields.begin (); it != fields.end ();)
+ {
+ auto &field = *it;
+
+ auto &field_attrs = field->get_outer_attrs ();
+ expand_cfg_attrs (field_attrs);
+ if (fails_cfg_with_expand (field_attrs))
+ {
+ it = fields.erase (it);
+ continue;
+ }
+
+ ++it;
+ }
+}
+
+void
CfgStrip::maybe_strip_tuple_fields (std::vector<AST::TupleField> &fields)
{
for (auto it = fields.begin (); it != fields.end ();)
@@ -414,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 ());
+ }
}
}
@@ -962,6 +986,8 @@ CfgStrip::visit (AST::StructExprStructFields &expr)
"cannot strip expression in this position - outer "
"attributes not allowed");
}
+
+ maybe_strip_struct_expr_fields (expr.get_fields ());
}
void
@@ -1852,6 +1878,10 @@ CfgStrip::visit (AST::StructStruct &struct_item)
}
AST::DefaultASTVisitor::visit (struct_item);
+
+ /* strip struct fields if required - this is presumably
+ * allowed by spec */
+ maybe_strip_struct_fields (struct_item.get_fields ());
}
void
CfgStrip::visit (AST::TupleStruct &tuple_struct)
diff --git a/gcc/rust/expand/rust-cfg-strip.h b/gcc/rust/expand/rust-cfg-strip.h
index 773bfde..4900ae8 100644
--- a/gcc/rust/expand/rust-cfg-strip.h
+++ b/gcc/rust/expand/rust-cfg-strip.h
@@ -36,6 +36,8 @@ public:
void go (AST::Crate &crate);
void maybe_strip_struct_fields (std::vector<AST::StructField> &fields);
+ void maybe_strip_struct_expr_fields (
+ std::vector<std::unique_ptr<AST::StructExprField>> &fields);
void maybe_strip_tuple_fields (std::vector<AST::TupleField> &fields);
void maybe_strip_function_params (
std::vector<std::unique_ptr<AST::Param>> &params);
diff --git a/gcc/rust/expand/rust-derive-clone.cc b/gcc/rust/expand/rust-derive-clone.cc
index 2f2554d..074ea01 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"})));
@@ -73,23 +87,23 @@ DeriveClone::clone_fn (std::unique_ptr<Expr> &&clone_expr)
*
*/
std::unique_ptr<Item>
-DeriveClone::clone_impl (std::unique_ptr<AssociatedItem> &&clone_fn,
- std::string name)
+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));
-
- return std::unique_ptr<Item> (
- new TraitImpl (clone, /* unsafe */ false,
- /* exclam */ false, std::move (trait_items),
- /* generics */ {}, builder.single_type_path (name),
- WhereClause::create_empty (), Visibility::create_private (),
- {}, {}, loc));
+ // 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);
+
+ auto trait_items = vec (std::move (clone_fn));
+
+ auto generics = setup_impl_generics (name, type_generics,
+ builder.trait_bound (clone_bound));
+
+ 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
@@ -122,7 +136,8 @@ DeriveClone::visit_tuple (TupleStruct &item)
auto constructor = builder.call (std::move (path), std::move (cloned_fields));
expanded = clone_impl (clone_fn (std::move (constructor)),
- item.get_identifier ().as_string ());
+ item.get_identifier ().as_string (),
+ item.get_generic_params ());
}
void
@@ -133,7 +148,8 @@ DeriveClone::visit_struct (StructStruct &item)
auto unit_ctor
= builder.struct_expr_struct (item.get_struct_name ().as_string ());
expanded = clone_impl (clone_fn (std::move (unit_ctor)),
- item.get_struct_name ().as_string ());
+ item.get_struct_name ().as_string (),
+ item.get_generic_params ());
return;
}
@@ -151,27 +167,220 @@ DeriveClone::visit_struct (StructStruct &item)
auto ctor = builder.struct_expr (item.get_struct_name ().as_string (),
std::move (cloned_fields));
expanded = clone_impl (clone_fn (std::move (ctor)),
- item.get_struct_name ().as_string ());
+ item.get_struct_name ().as_string (),
+ 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));
+ auto expr = std::unique_ptr<Expr> (
+ new StructExprStructFields (variant_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));
@@ -179,15 +388,17 @@ 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)),
- item.get_identifier ().as_string ());
+ item.get_identifier ().as_string (),
+ item.get_generic_params ());
}
} // namespace AST
diff --git a/gcc/rust/expand/rust-derive-clone.h b/gcc/rust/expand/rust-derive-clone.h
index ab64829..61224ba 100644
--- a/gcc/rust/expand/rust-derive-clone.h
+++ b/gcc/rust/expand/rust-derive-clone.h
@@ -59,8 +59,26 @@ private:
* }
*
*/
- std::unique_ptr<Item> clone_impl (std::unique_ptr<AssociatedItem> &&clone_fn,
- std::string name);
+ std::unique_ptr<Item>
+ 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);
diff --git a/gcc/rust/expand/rust-derive-copy.cc b/gcc/rust/expand/rust-derive-copy.cc
index a1c8ed0..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 {
@@ -37,43 +38,48 @@ DeriveCopy::go (Item &item)
}
std::unique_ptr<Item>
-DeriveCopy::copy_impl (std::string name)
+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);
-
- return std::unique_ptr<Item> (
- new TraitImpl (copy, /* unsafe */ false,
- /* exclam */ false, /* trait items */ {},
- /* generics */ {}, builder.single_type_path (name),
- 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
DeriveCopy::visit_struct (StructStruct &item)
{
- expanded = copy_impl (item.get_struct_name ().as_string ());
+ expanded = copy_impl (item.get_struct_name ().as_string (),
+ item.get_generic_params ());
}
void
DeriveCopy::visit_tuple (TupleStruct &item)
{
- expanded = copy_impl (item.get_struct_name ().as_string ());
+ expanded = copy_impl (item.get_struct_name ().as_string (),
+ item.get_generic_params ());
}
void
DeriveCopy::visit_enum (Enum &item)
{
- expanded = copy_impl (item.get_identifier ().as_string ());
+ expanded = copy_impl (item.get_identifier ().as_string (),
+ item.get_generic_params ());
}
void
DeriveCopy::visit_union (Union &item)
{
- expanded = copy_impl (item.get_identifier ().as_string ());
+ expanded = copy_impl (item.get_identifier ().as_string (),
+ item.get_generic_params ());
}
} // namespace AST
diff --git a/gcc/rust/expand/rust-derive-copy.h b/gcc/rust/expand/rust-derive-copy.h
index 98decc0..71972eb 100644
--- a/gcc/rust/expand/rust-derive-copy.h
+++ b/gcc/rust/expand/rust-derive-copy.h
@@ -40,7 +40,9 @@ private:
*
* impl Copy for <type> {}
*/
- std::unique_ptr<Item> copy_impl (std::string name);
+ std::unique_ptr<Item>
+ copy_impl (std::string name,
+ const std::vector<std::unique_ptr<GenericParam>> &type_generics);
virtual void visit_struct (StructStruct &item);
virtual void visit_tuple (TupleStruct &item);
diff --git a/gcc/rust/expand/rust-derive-debug.cc b/gcc/rust/expand/rust-derive-debug.cc
new file mode 100644
index 0000000..7ad3908
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-debug.cc
@@ -0,0 +1,122 @@
+// 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), {}, {},
+ AST::LoopLabel::error (), 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..c54f8c3
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-default.cc
@@ -0,0 +1,173 @@
+// 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), {}, {},
+ AST::LoopLabel::error (), 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..dc173de
--- /dev/null
+++ b/gcc/rust/expand/rust-derive-eq.cc
@@ -0,0 +1,217 @@
+// 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, {}, {}, AST::LoopLabel::error (),
+ 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 1924432..d8cc0a4 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;
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..d4db313 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,17 @@ 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)
-{}
+{
+ expand_generic_args (segment.get_generic_args ());
+}
void
ExpandVisitor::visit (AST::TypePathSegmentFunction &segment)
@@ -718,6 +724,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 +867,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 +894,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 +923,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 aca6c93..ad237c0 100644
--- a/gcc/rust/expand/rust-expand-visitor.h
+++ b/gcc/rust/expand/rust-expand-visitor.h
@@ -140,7 +140,7 @@ public:
it = values.erase (it);
for (auto &node : final_fragment.get_nodes ())
{
- auto new_node = extractor (node);
+ U new_node = extractor (node);
if (new_node != nullptr)
{
it = values.insert (it, std::move (new_node));
@@ -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 214265d..4d02604 100644
--- a/gcc/rust/expand/rust-macro-builtins-asm.cc
+++ b/gcc/rust/expand/rust-macro-builtins-asm.cc
@@ -17,10 +17,10 @@
// <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"
namespace Rust {
@@ -38,6 +38,32 @@ 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)
+{
+ 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;
+}
+
tl::expected<InlineAsmContext, InlineAsmParseError>
parse_clobber_abi (InlineAsmContext inline_asm_ctx)
{
@@ -193,7 +219,6 @@ parse_reg_operand (InlineAsmContext inline_asm_ctx)
// None
// };
auto &parser = inline_asm_ctx.parser;
- AST::InlineAsmOperand reg_operand;
auto token = parser.peek_current_token ();
auto iden_token = parser.peek_current_token ();
@@ -227,20 +252,28 @@ 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.
+ tl::expected<InlineAsmContext, InlineAsmParseError> parsing_operand (
+ inline_asm_ctx);
for (auto &parse_func : parse_funcs)
{
- auto parsing_operand
- = tl::expected<InlineAsmContext, InlineAsmParseError> (inline_asm_ctx);
- parsing_operand.map (parse_func);
+ auto result = parsing_operand.and_then (parse_func);
// Per rust's asm.rs's structure
// After we've parse successfully, we break out and do a local validation
// of named, positional & explicit register operands
- if (parsing_operand.has_value ())
- break;
- if (parsing_operand.error () == COMMITTED)
- return parsing_operand;
+ if (result.has_value ())
+ {
+ inline_asm_ctx = *result;
+ break;
+ }
+ else if (result.error () == COMMITTED)
+ {
+ if (parse_func == parse_reg_operand_unexpected)
+ return inline_asm_ctx;
+ else
+ return result;
+ }
}
auto &inline_asm = inline_asm_ctx.inline_asm;
@@ -289,8 +322,8 @@ parse_reg_operand_in (InlineAsmContext inline_asm_ctx)
{
// For the keyword IN, currently we count it as a seperate keyword called
// Rust::IN search for #define RS_TOKEN_LIST in code base.
- AST::InlineAsmOperand reg_operand;
auto &parser = inline_asm_ctx.parser;
+ location_t locus = parser.peek_current_token ()->get_locus ();
if (!inline_asm_ctx.is_global_asm () && parser.skip_token (IN))
{
auto reg = parse_reg (inline_asm_ctx);
@@ -300,16 +333,15 @@ 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 = parse_format_string (inline_asm_ctx);
+ 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, nullptr);
- // reg_operand.set_in (in);
- // inline_asm_ctx.inline_asm.operands.push_back (reg_operand);
+ AST::InlineAsmOperand::In in (reg, std::move (expr));
+ inline_asm_ctx.inline_asm.operands.emplace_back (in, locus);
return inline_asm_ctx;
}
return tl::unexpected<InlineAsmParseError> (NONCOMMITED);
@@ -319,18 +351,22 @@ tl::expected<InlineAsmContext, InlineAsmParseError>
parse_reg_operand_out (InlineAsmContext inline_asm_ctx)
{
auto &parser = inline_asm_ctx.parser;
- AST::InlineAsmOperand reg_operand;
+ location_t locus = parser.peek_current_token ()->get_locus ();
if (!inline_asm_ctx.is_global_asm () && check_identifier (parser, "out"))
{
auto reg = parse_reg (inline_asm_ctx);
+ std::unique_ptr<AST::Expr> expr = parser.parse_expr ();
- auto expr = parse_format_string (inline_asm_ctx);
+ rust_assert (expr != nullptr);
+
+ /*auto expr_ptr =
+ std::make_unique<AST::Expr>(AST::LiteralExpr(Literal))*/
// TODO: When we've succesfully parse an expr, remember to clone_expr()
// instead of nullptr
- // struct AST::InlineAsmOperand::Out out (reg, false, nullptr);
- // reg_operand.set_out (out);
- // inline_asm_ctx.inline_asm.operands.push_back (reg_operand);
+ AST::InlineAsmOperand::Out out (reg, false, std::move (expr));
+
+ inline_asm_ctx.inline_asm.operands.emplace_back (out, locus);
return inline_asm_ctx;
}
@@ -360,7 +396,6 @@ parse_reg_operand_inout (InlineAsmContext inline_asm_ctx)
auto &parser = inline_asm_ctx.parser;
auto token = parser.peek_current_token ();
- AST::InlineAsmOperand reg_operand;
if (!inline_asm_ctx.is_global_asm () && check_identifier (parser, "inout"))
{
auto reg = parse_reg (inline_asm_ctx);
@@ -378,7 +413,9 @@ parse_reg_operand_inout (InlineAsmContext inline_asm_ctx)
// TODO: Is error propogation our top priority, the ? in rust's asm.rs is
// doing a lot of work.
// TODO: Not sure how to use parse_expr
- auto expr = parse_format_string (inline_asm_ctx);
+ if (!check_identifier (parser, ""))
+ rust_unreachable ();
+ // auto expr = parse_format_string (inline_asm_ctx);
std::unique_ptr<AST::Expr> out_expr;
@@ -386,9 +423,9 @@ parse_reg_operand_inout (InlineAsmContext inline_asm_ctx)
{
if (!parser.skip_token (UNDERSCORE))
{
- auto result = parse_format_string (inline_asm_ctx);
+ // auto result = parse_format_string (inline_asm_ctx);
- if (!result.has_value ())
+ if (!check_identifier (parser, ""))
rust_unreachable ();
// out_expr = parser.parse_expr();
}
@@ -399,8 +436,8 @@ parse_reg_operand_inout (InlineAsmContext inline_asm_ctx)
// expr, out_expr, late: false }
// struct AST::InlineAsmOperand::SplitInOut split_in_out (reg,
// false, nullptr,
- // nullptr); reg_operand.set_split_in_out (split_in_out);
- // inline_asm_ctx.inline_asm.operands.push_back (reg_operand);
+ // nullptr);
+ // inline_asm_ctx.inline_asm.operands.push_back (split_in_out);
return inline_asm_ctx;
}
@@ -410,8 +447,8 @@ parse_reg_operand_inout (InlineAsmContext inline_asm_ctx)
// RUST VERSION: ast::InlineAsmOperand::InOut { reg, expr, late: false
// }
// struct AST::InlineAsmOperand::InOut inout (reg, false,
- // nullptr); reg_operand.set_in_out (inout);
- // inline_asm_ctx.inline_asm.operands.push_back (reg_operand);
+ // nullptr);
+ // inline_asm_ctx.inline_asm.operands.push_back (inout);
return inline_asm_ctx;
}
}
@@ -423,12 +460,10 @@ tl::expected<InlineAsmContext, InlineAsmParseError>
parse_reg_operand_const (InlineAsmContext inline_asm_ctx)
{
auto &parser = inline_asm_ctx.parser;
- AST::InlineAsmOperand reg_operand;
if (parser.peek_current_token ()->get_id () == CONST)
{
// TODO: Please handle const with parse_expr instead.
auto anon_const = parse_format_string (inline_asm_ctx);
- reg_operand.set_cnst (tl::nullopt);
rust_unreachable ();
return tl::unexpected<InlineAsmParseError> (COMMITTED);
}
@@ -457,9 +492,9 @@ parse_reg_operand_unexpected (InlineAsmContext inline_asm_ctx)
// TODO: It is weird that we can't seem to match any identifier,
// something must be wrong. consult compiler code in asm.rs or rust online
// compiler.
- rust_unreachable ();
+ // rust_unreachable ();
- rust_error_at (token->get_locus (), "ERROR RIGHT HERE");
+ // rust_error_at (token->get_locus (), "ERROR RIGHT HERE");
return tl::unexpected<InlineAsmParseError> (COMMITTED);
}
@@ -551,9 +586,7 @@ parse_options (InlineAsmContext &inline_asm_ctx)
// Parse comma as optional
if (parser.skip_token (COMMA))
- {
- continue;
- }
+ continue;
else
{
rust_unreachable ();
@@ -678,7 +711,9 @@ parse_asm_arg (InlineAsmContext inline_asm_ctx)
if (check_identifier (parser, "clobber_abi"))
{
auto expected = parse_clobber_abi (inline_asm_ctx);
- if (expected || expected.error () == COMMITTED)
+ if (expected.has_value ())
+ continue;
+ else if (expected.error () == COMMITTED)
return expected;
// The error type is definitely non-committed (we have checked above),
@@ -688,7 +723,9 @@ parse_asm_arg (InlineAsmContext inline_asm_ctx)
if (check_identifier (parser, "options"))
{
auto expected = parse_options (inline_asm_ctx);
- if (expected || expected.error () == COMMITTED)
+ if (expected.has_value ())
+ continue;
+ else if (expected.error () == COMMITTED)
return expected;
// The error type is definitely non-committed (we have checked above),
@@ -699,7 +736,9 @@ parse_asm_arg (InlineAsmContext inline_asm_ctx)
// only other logical choice is reg_operand
auto expected = parse_reg_operand (inline_asm_ctx);
- if (expected || expected.error () == COMMITTED)
+ if (expected.has_value ())
+ continue;
+ else if (expected.error () == COMMITTED)
return expected;
// Since parse_reg_operand is the last thing we've considered,
@@ -718,6 +757,74 @@ parse_asm_arg (InlineAsmContext inline_asm_ctx)
return tl::expected<InlineAsmContext, InlineAsmParseError> (inline_asm_ctx);
}
+tl::expected<InlineAsmContext, InlineAsmParseError>
+expand_inline_asm_strings (InlineAsmContext inline_asm_ctx)
+{
+ auto &inline_asm = inline_asm_ctx.inline_asm;
+
+ auto str_vec = inline_asm.get_template_strs ();
+
+ decltype (str_vec) resulting_template_vec;
+ for (auto &template_str : str_vec)
+ {
+ /*std::cout << template_str.symbol << std::endl;*/
+
+ auto pieces = Fmt::Pieces::collect (template_str.symbol, false,
+ Fmt::ffi::ParseMode::InlineAsm);
+ auto pieces_vec = pieces.get_pieces ();
+
+ std::string transformed_template_str = "";
+ for (size_t i = 0; i < pieces_vec.size (); i++)
+ {
+ auto piece = pieces_vec[i];
+ if (piece.tag == Fmt::ffi::Piece::Tag::String)
+ {
+ transformed_template_str += piece.string._0.to_string ();
+ }
+ else if (piece.tag == Fmt::ffi::Piece::Tag::NextArgument)
+ {
+ /* std::cout << " " << i << ": "*/
+ /*<< piece.next_argument._0.to_string () << std::endl;*/
+
+ auto next_argument = piece.next_argument._0;
+ switch (piece.next_argument._0.position.tag)
+ {
+ case Fmt::ffi::Position::Tag::ArgumentImplicitlyIs: {
+ auto idx = next_argument.position.argument_implicitly_is._0;
+ /*auto trait = next_argument.format;*/
+ /*auto arg = arguments.at (idx);*/
+
+ /* // FIXME(Arthur): This API sucks*/
+ /* rust_assert (arg.get_kind ().kind*/
+ /*== AST::FormatArgumentKind::Kind::Normal);*/
+ /**/
+ /* args.push_back ({arg.get_expr ().clone_expr (),
+ * trait});*/
+
+ transformed_template_str += "%" + std::to_string (idx);
+ // std::cout << "argument implicitly is: " << idx <<
+ // std::endl; std::cout << "transformed template str is:"
+ // << transformed_template_str << std::endl;
+ /*std::cout << "trait: " << trait.to_string () <<
+ * std::endl;*/
+ /*std::cout << "arg: " << arg.to_string () << std::endl;*/
+ }
+ break;
+ case Fmt::ffi::Position::Tag::ArgumentIs:
+ case Fmt::ffi::Position::Tag::ArgumentNamed:
+ rust_sorry_at (inline_asm.get_locus (),
+ "unhandled argument position specifier");
+ break;
+ }
+ }
+ }
+ template_str.symbol = transformed_template_str;
+ }
+
+ inline_asm.template_strs = str_vec;
+ return inline_asm_ctx;
+}
+
tl::optional<AST::Fragment>
parse_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
AST::InvocKind semicolon, AST::AsmKind is_global_asm)
@@ -726,7 +833,8 @@ parse_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
// We first parse all formatted strings. If we fail, then we return
// tl::nullopt
- // We then parse the asm arguments. If we fail, then we return tl::nullopt
+ // We then parse the asm arguments. If we fail, then we return
+ // tl::nullopt
// We then validate. If we fail, then we return tl::nullopt
@@ -741,26 +849,27 @@ parse_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
auto resulting_context = parse_format_strings (inline_asm_ctx)
.and_then (parse_asm_arg)
- .and_then (validate);
-
- // TODO: I'm putting the validation here because the rust reference put it
- // here Per Arthur's advice we would actually do the validation in a different
- // stage. and visit on the InlineAsm AST instead of it's context.
- auto is_valid = (bool) resulting_context;
+ .and_then (validate)
+ .and_then (expand_inline_asm_strings);
- if (is_valid)
+ // TODO: I'm putting the validation here because the rust reference put
+ // it here Per Arthur's advice we would actually do the validation in a
+ // different stage. and visit on the InlineAsm AST instead of it's
+ // context.
+ if (resulting_context)
{
- auto node = inline_asm_ctx.inline_asm.clone_expr_without_block ();
+ auto node = (*resulting_context).inline_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 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 (
- 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)));
@@ -778,7 +887,8 @@ parse_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
tl::expected<InlineAsmContext, InlineAsmParseError>
parse_format_strings (InlineAsmContext inline_asm_ctx)
{
- // Parse the first ever formatted string, success or not, will skip 1 token
+ // Parse the first ever formatted string, success or not, will skip 1
+ // token
auto &parser = inline_asm_ctx.parser;
auto last_token_id = inline_asm_ctx.last_token_id;
auto fm_string = parse_format_string (inline_asm_ctx);
@@ -794,7 +904,8 @@ parse_format_strings (InlineAsmContext inline_asm_ctx)
else
{
auto template_str
- = AST::TupleTemplateStr (token->get_locus (), fm_string.value ());
+ = AST::TupleTemplateStr (token->get_locus (),
+ strip_double_quotes (fm_string.value ()));
inline_asm.template_strs.push_back (template_str);
}
@@ -806,8 +917,8 @@ parse_format_strings (InlineAsmContext inline_asm_ctx)
{
break;
}
- // Ok after the comma is good, we better be parsing correctly everything
- // in here, which is formatted string in ABNF
+ // Ok after the comma is good, we better be parsing correctly
+ // everything in here, which is formatted string in ABNF
inline_asm_ctx.consumed_comma_without_formatted_string = false;
token = parser.peek_current_token ();
@@ -820,7 +931,8 @@ parse_format_strings (InlineAsmContext inline_asm_ctx)
else
{
auto template_str
- = AST::TupleTemplateStr (token->get_locus (), fm_string.value ());
+ = AST::TupleTemplateStr (token->get_locus (),
+ strip_double_quotes (fm_string.value ()));
inline_asm.template_strs.push_back (template_str);
}
}
diff --git a/gcc/rust/expand/rust-macro-builtins-asm.h b/gcc/rust/expand/rust-macro-builtins-asm.h
index 6997770..8081dae 100644
--- a/gcc/rust/expand/rust-macro-builtins-asm.h
+++ b/gcc/rust/expand/rust-macro-builtins-asm.h
@@ -65,6 +65,14 @@ public:
// {
// }
+ InlineAsmContext &operator= (const InlineAsmContext &inline_asm_ctx)
+ {
+ allow_templates = inline_asm_ctx.allow_templates;
+ is_explicit = inline_asm_ctx.is_explicit;
+ consumed_comma_without_formatted_string = false;
+ last_token_id = inline_asm_ctx.last_token_id;
+ return *this;
+ }
bool is_global_asm () { return inline_asm.is_global_asm; }
@@ -75,6 +83,9 @@ public:
this->allow_templates = allow_templates;
}
};
+WARN_UNUSED_RESULT
+tl::expected<InlineAsmContext, InlineAsmParseError>
+expand_inline_asm_strings (InlineAsmContext inline_asm_ctx);
// Expected calls
WARN_UNUSED_RESULT
@@ -161,4 +172,4 @@ tl::optional<std::string>
parse_label (Parser<MacroInvocLexer> &parser, TokenId last_token_id,
InlineAsmContext &inline_asm_ctx);
-} // namespace Rust \ No newline at end of file
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-macro-builtins-helpers.cc b/gcc/rust/expand/rust-macro-builtins-helpers.cc
index d44efcc..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 ())));
@@ -174,7 +174,8 @@ try_expand_many_expr (Parser<MacroInvocLexer> &parser,
std::unique_ptr<AST::Expr>
parse_single_string_literal (BuiltinMacro kind,
AST::DelimTokenTree &invoc_token_tree,
- location_t invoc_locus, MacroExpander *expander)
+ location_t invoc_locus, MacroExpander *expander,
+ bool is_semicoloned)
{
MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
Parser<MacroInvocLexer> parser (lex);
@@ -221,7 +222,7 @@ parse_single_string_literal (BuiltinMacro kind,
AST::MacroInvocData (AST::SimplePath ({AST::SimplePathSegment (
path_str, invoc_locus)}),
std::move (invoc_token_tree)),
- {}, invoc_locus, std::move (pending_invocations));
+ {}, invoc_locus, std::move (pending_invocations), is_semicoloned);
}
else
{
@@ -281,4 +282,4 @@ load_file_bytes (location_t invoc_locus, const char *filename)
return buf;
}
-} // namespace Rust \ No newline at end of file
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-macro-builtins-helpers.h b/gcc/rust/expand/rust-macro-builtins-helpers.h
index bf058df..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 {
@@ -74,7 +73,8 @@ try_expand_many_expr (Parser<MacroInvocLexer> &parser,
std::unique_ptr<AST::Expr>
parse_single_string_literal (BuiltinMacro kind,
AST::DelimTokenTree &invoc_token_tree,
- location_t invoc_locus, MacroExpander *expander);
+ location_t invoc_locus, MacroExpander *expander,
+ bool is_semicoloned = false);
// Treat PATH as a path relative to the source file currently being
// compiled, and return the absolute path for it.
diff --git a/gcc/rust/expand/rust-macro-builtins-include.cc b/gcc/rust/expand/rust-macro-builtins-include.cc
index cf3f93d..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
@@ -40,7 +41,12 @@ MacroBuiltin::include_bytes_handler (location_t invoc_locus,
if (lit_expr == nullptr)
return AST::Fragment::create_error ();
- rust_assert (lit_expr->is_literal ());
+ if (!lit_expr->is_literal ())
+ {
+ auto token_tree = invoc.get_delim_tok_tree ();
+ return AST::Fragment ({AST::SingleASTNode (std::move (lit_expr))},
+ token_tree.to_token_stream ());
+ }
std::string target_filename
= source_relative_path (lit_expr->as_string (), invoc_locus);
@@ -188,16 +194,36 @@ MacroBuiltin::include_handler (location_t invoc_locus,
AST::MacroInvocData &invoc,
AST::InvocKind semicolon)
{
+ bool is_semicoloned = semicolon == AST::InvocKind::Semicoloned;
/* Get target filename from the macro invocation, which is treated as a path
relative to the include!-ing file (currently being compiled). */
- auto lit_expr
+ std::unique_ptr<AST::Expr> lit_expr
= parse_single_string_literal (BuiltinMacro::Include,
invoc.get_delim_tok_tree (), invoc_locus,
- invoc.get_expander ());
+ invoc.get_expander (), is_semicoloned);
if (lit_expr == nullptr)
return AST::Fragment::create_error ();
- rust_assert (lit_expr->is_literal ());
+ if (!lit_expr->is_literal ())
+ {
+ // We have to expand an inner macro eagerly
+ auto token_tree = invoc.get_delim_tok_tree ();
+
+ // parse_single_string_literal returned an AST::MacroInvocation, which
+ // can either be an AST::Item or AST::Expr. Depending on the context the
+ // original macro was invoked in, we will set AST::Item or AST::Expr
+ // appropriately.
+ if (is_semicoloned)
+ {
+ std::unique_ptr<AST::Item> lit_item = std::unique_ptr<AST::Item> (
+ static_cast<AST::MacroInvocation *> (lit_expr.release ()));
+ return AST::Fragment ({AST::SingleASTNode (std::move (lit_item))},
+ token_tree.to_token_stream ());
+ }
+ else
+ return AST::Fragment ({AST::SingleASTNode (std::move (lit_expr))},
+ token_tree.to_token_stream ());
+ }
std::string filename
= source_relative_path (lit_expr->as_string (), invoc_locus);
@@ -218,8 +244,14 @@ MacroBuiltin::include_handler (location_t invoc_locus,
Lexer lex (target_filename, std::move (target_file), linemap);
Parser<Lexer> parser (lex);
+ std::unique_ptr<AST::Expr> parsed_expr = nullptr;
+ std::vector<std::unique_ptr<AST::Item>> parsed_items{};
+
+ if (is_semicoloned)
+ parsed_items = parser.parse_items ();
+ else
+ parsed_expr = parser.parse_expr ();
- auto parsed_items = parser.parse_items ();
bool has_error = !parser.get_errors ().empty ();
for (const auto &error : parser.get_errors ())
@@ -233,17 +265,22 @@ MacroBuiltin::include_handler (location_t invoc_locus,
}
std::vector<AST::SingleASTNode> nodes{};
- for (auto &item : parsed_items)
+ if (is_semicoloned)
+ for (auto &item : parsed_items)
+ {
+ AST::SingleASTNode node (std::move (item));
+ nodes.push_back (node);
+ }
+ else
{
- AST::SingleASTNode node (std::move (item));
+ AST::SingleASTNode node (std::move (parsed_expr));
nodes.push_back (node);
}
-
// FIXME: This returns an empty vector of tokens and works fine, but is that
// the expected behavior? `include` macros are a bit harder to reason about
// since they include tokens. Furthermore, our lexer has no easy way to return
// a slice of tokens like the MacroInvocLexer. So it gets even harder to
- // extrac tokens from here. For now, let's keep it that way and see if it
+ // extract tokens from here. For now, let's keep it that way and see if it
// eventually breaks, but I don't expect it to cause many issues since the
// list of tokens is only used when a macro invocation mixes eager
// macro invocations and already expanded tokens. Think
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..39c4c46 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},
@@ -119,9 +121,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_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},
diff --git a/gcc/rust/expand/rust-macro-builtins.h b/gcc/rust/expand/rust-macro-builtins.h
index 6a9b31c..ff06ebf 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);
diff --git a/gcc/rust/expand/rust-macro-expand.cc b/gcc/rust/expand/rust-macro-expand.cc
index 4d9cade..cd17a3f 100644
--- a/gcc/rust/expand/rust-macro-expand.cc
+++ b/gcc/rust/expand/rust-macro-expand.cc
@@ -23,10 +23,10 @@
#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"
namespace Rust {
@@ -119,7 +119,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 ());
}
@@ -250,7 +250,12 @@ MacroExpander::expand_invoc (AST::MacroInvocation &invoc,
}
if (invoc.get_kind () == AST::MacroInvocation::InvocKind::Builtin)
- expand_eager_invocations (invoc);
+ {
+ // Eager expansions are always expressions
+ push_context (ContextType::EXPR);
+ expand_eager_invocations (invoc);
+ pop_context ();
+ }
AST::MacroInvocData &invoc_data = invoc.get_invoc_data ();
@@ -1019,7 +1024,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)
{
@@ -1033,8 +1039,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);