diff options
Diffstat (limited to 'gcc/rust/expand')
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>> ¶ms); 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 ¯o_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 ¶m) 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>> ¯o; 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>> ¯o, - 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); |