LCOV - code coverage report
Current view: top level - gcc/rust/expand - rust-derive-ord.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 97.1 % 136 132
Test Date: 2026-08-22 16:33:35 Functions: 92.9 % 14 13
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2025-2026 Free Software Foundation, Inc.
       2              : 
       3              : // This file is part of GCC.
       4              : 
       5              : // GCC is free software; you can redistribute it and/or modify it under
       6              : // the terms of the GNU General Public License as published by the Free
       7              : // Software Foundation; either version 3, or (at your option) any later
       8              : // version.
       9              : 
      10              : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11              : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12              : // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13              : // for more details.
      14              : 
      15              : // You should have received a copy of the GNU General Public License
      16              : // along with GCC; see the file COPYING3.  If not see
      17              : // <http://www.gnu.org/licenses/>.
      18              : 
      19              : #include "rust-derive-ord.h"
      20              : #include "rust-ast.h"
      21              : #include "rust-derive-cmp-common.h"
      22              : #include "rust-derive.h"
      23              : #include "rust-item.h"
      24              : #include "rust-system.h"
      25              : 
      26              : namespace Rust {
      27              : namespace AST {
      28              : 
      29          125 : DeriveOrd::DeriveOrd (Ordering ordering, location_t loc,
      30              :                       Builder::Source item_source)
      31          125 :   : DeriveVisitor (loc, item_source), ordering (ordering)
      32          125 : {}
      33              : 
      34              : std::unique_ptr<Item>
      35          125 : DeriveOrd::go (Item &item)
      36              : {
      37          125 :   item.accept_vis (*this);
      38              : 
      39          125 :   return std::move (expanded);
      40              : }
      41              : 
      42              : std::unique_ptr<Expr>
      43          259 : DeriveOrd::cmp_call (std::unique_ptr<Expr> &&self_expr,
      44              :                      std::unique_ptr<Expr> &&other_expr)
      45              : {
      46         1554 :   auto cmp_fn_path = builder.path_in_expression (
      47          518 :     {builder.get_path_start (), "cmp", trait (ordering), fn (ordering)}, true);
      48              : 
      49          518 :   return builder.call (ptrify (cmp_fn_path),
      50          518 :                        vec (builder.ref (std::move (self_expr)),
      51          777 :                             builder.ref (std::move (other_expr))));
      52          259 : }
      53              : 
      54              : std::unique_ptr<Item>
      55          125 : DeriveOrd::cmp_impl (
      56              :   std::unique_ptr<BlockExpr> &&fn_block, Identifier type_name,
      57              :   const std::vector<std::unique_ptr<GenericParam>> &type_generics)
      58              : {
      59          125 :   auto fn = cmp_fn (std::move (fn_block), type_name);
      60              : 
      61          125 :   auto trait = ordering == Ordering::Partial ? "PartialOrd" : "Ord";
      62          266 :   auto trait_path = [&, this] () {
      63          564 :     return builder.type_path ({builder.get_path_start (), "cmp", trait}, true);
      64          125 :   };
      65              : 
      66          125 :   auto trait_bound
      67           16 :     = [&, this] () { return builder.trait_bound (trait_path ()); };
      68              : 
      69          125 :   auto trait_items = vec (std::move (fn));
      70              : 
      71          125 :   auto cmp_generics
      72          125 :     = setup_impl_generics (type_name.as_string (), type_generics, trait_bound);
      73              : 
      74          250 :   return builder.trait_impl (trait_path (), std::move (cmp_generics.self_type),
      75              :                              std::move (trait_items),
      76          250 :                              std::move (cmp_generics.impl));
      77          125 : }
      78              : 
      79              : std::unique_ptr<AssociatedItem>
      80          125 : DeriveOrd::cmp_fn (std::unique_ptr<BlockExpr> &&block, Identifier type_name)
      81              : {
      82              :   // Ordering
      83          125 :   auto return_type
      84          500 :     = builder.type_path ({builder.get_path_start (), "cmp", "Ordering"}, true);
      85              : 
      86              :   // In the case of PartialOrd, we return an Option<Ordering>
      87          125 :   if (ordering == Ordering::Partial)
      88              :     {
      89           63 :       auto generic = GenericArg::create_type (ptrify (return_type));
      90              : 
      91           63 :       auto generic_seg = builder.type_path_segment_generic (
      92          126 :         "Option", GenericArgs ({}, {generic}, {}, loc));
      93           63 :       auto core = builder.type_path_segment (builder.get_path_start ());
      94           63 :       auto option = builder.type_path_segment ("option");
      95              : 
      96           63 :       return_type
      97          126 :         = builder.type_path (vec (std::move (core), std::move (option),
      98              :                                   std::move (generic_seg)),
      99           63 :                              true);
     100           63 :     }
     101              : 
     102              :   // &self, other: &Self
     103          125 :   auto params = vec (
     104          250 :     builder.self_ref_param (),
     105          250 :     builder.function_param (builder.identifier_pattern ("other"),
     106          250 :                             builder.reference_type (ptrify (
     107          500 :                               builder.type_path (type_name.as_string ())))));
     108              : 
     109          125 :   auto function_name = fn (ordering);
     110              : 
     111          625 :   return builder.function (function_name, std::move (params),
     112          375 :                            ptrify (return_type), std::move (block));
     113          125 : }
     114              : 
     115              : std::unique_ptr<Pattern>
     116           98 : DeriveOrd::make_equal ()
     117              : {
     118          490 :   std::unique_ptr<Pattern> equal = ptrify (builder.path_in_expression (
     119           98 :     {builder.get_path_start (), "cmp", "Ordering", "Equal"}, true));
     120              : 
     121              :   // We need to wrap the pattern in Option::Some if we are doing partial
     122              :   // ordering
     123           98 :   if (ordering == Ordering::Partial)
     124              :     {
     125           61 :       auto pattern_items = std::unique_ptr<TupleStructItems> (
     126           61 :         new TupleStructItemsNoRest (vec (std::move (equal))));
     127              : 
     128           61 :       equal
     129          122 :         = std::make_unique<TupleStructPattern> (builder.path_in_expression (
     130              :                                                   LangItem::Kind::OPTION_SOME),
     131           61 :                                                 std::move (pattern_items));
     132           61 :     }
     133              : 
     134           98 :   return equal;
     135              : }
     136              : 
     137              : std::pair<MatchArm, MatchArm>
     138           98 : DeriveOrd::make_cmp_arms ()
     139              : {
     140              :   // All comparison results other than Ordering::Equal
     141           98 :   auto non_equal = builder.identifier_pattern (DeriveOrd::not_equal);
     142           98 :   auto equal = make_equal ();
     143              : 
     144           98 :   return {builder.match_arm (std::move (equal)),
     145           98 :           builder.match_arm (std::move (non_equal))};
     146           98 : }
     147              : 
     148              : std::unique_ptr<Expr>
     149          137 : DeriveOrd::recursive_match (std::vector<SelfOther> &&members)
     150              : {
     151          137 :   if (members.empty ())
     152              :     {
     153           36 :       std::unique_ptr<Expr> value = ptrify (builder.path_in_expression (
     154            6 :         {builder.get_path_start (), "cmp", "Ordering", "Equal"}, true));
     155              : 
     156            6 :       if (ordering == Ordering::Partial)
     157            9 :         value = builder.call (ptrify (builder.path_in_expression (
     158              :                                 LangItem::Kind::OPTION_SOME)),
     159            3 :                               std::move (value));
     160              : 
     161              :       return value;
     162              :     }
     163              : 
     164          131 :   std::unique_ptr<Expr> final_expr = nullptr;
     165              : 
     166          360 :   for (auto it = members.rbegin (); it != members.rend (); it++)
     167              :     {
     168          229 :       auto &member = *it;
     169              : 
     170          229 :       auto call = cmp_call (std::move (member.self_expr),
     171          229 :                             std::move (member.other_expr));
     172              : 
     173              :       // For the last member (so the first iterator), we just create a call
     174              :       // expression
     175          229 :       if (it == members.rbegin ())
     176              :         {
     177          131 :           final_expr = std::move (call);
     178          131 :           continue;
     179              :         }
     180              : 
     181              :       // If we aren't dealing with the last member, then we need to wrap all of
     182              :       // that in a big match expression and keep going
     183           98 :       auto match_arms = make_cmp_arms ();
     184              : 
     185           98 :       auto match_cases
     186              :         = {builder.match_case (std::move (match_arms.first),
     187              :                                std::move (final_expr)),
     188              :            builder.match_case (std::move (match_arms.second),
     189          490 :                                builder.identifier (DeriveOrd::not_equal))};
     190              : 
     191           98 :       final_expr = builder.match (std::move (call), std::move (match_cases));
     192          523 :     }
     193              : 
     194          131 :   return final_expr;
     195          131 : }
     196              : 
     197              : // we need to do a recursive match expression for all of the fields used in a
     198              : // struct so for something like struct Foo { a: i32, b: i32, c: i32 } we must
     199              : // first compare each `a` field, then `b`, then `c`, like this:
     200              : //
     201              : // match cmp_fn(self.<field>, other.<field>) {
     202              : //     Ordering::Equal => <recurse>,
     203              : //     cmp => cmp,
     204              : // }
     205              : //
     206              : // and the recurse will be the exact same expression, on the next field. so that
     207              : // our result looks like this:
     208              : //
     209              : // match cmp_fn(self.a, other.a) {
     210              : //     Ordering::Equal => match cmp_fn(self.b, other.b) {
     211              : //         Ordering::Equal =>cmp_fn(self.c, other.c),
     212              : //         cmp => cmp,
     213              : //     }
     214              : //     cmp => cmp,
     215              : // }
     216              : //
     217              : // the last field comparison needs not to be a match but just the function call.
     218              : // this is going to be annoying lol
     219              : void
     220           69 : DeriveOrd::visit_struct (StructStruct &item)
     221              : {
     222           69 :   auto fields = SelfOther::fields (builder, item.get_fields ());
     223              : 
     224           69 :   auto match_expr = recursive_match (std::move (fields));
     225              : 
     226          138 :   expanded = cmp_impl (builder.block (std::move (match_expr)),
     227          138 :                        item.get_identifier (), item.get_generic_params ());
     228           69 : }
     229              : 
     230              : // same as structs, but for each field index instead of each field name -
     231              : // straightforward once we have `visit_struct` working
     232              : void
     233           26 : DeriveOrd::visit_tuple (TupleStruct &item)
     234              : {
     235           26 :   auto fields = SelfOther::indexes (builder, item.get_fields ());
     236              : 
     237           26 :   auto match_expr = recursive_match (std::move (fields));
     238              : 
     239           52 :   expanded = cmp_impl (builder.block (std::move (match_expr)),
     240           52 :                        item.get_identifier (), item.get_generic_params ());
     241           26 : }
     242              : 
     243              : // for enums, we need to generate a match for each of the enum's variant that
     244              : // contains data and then do the same thing as visit_struct or visit_enum. if
     245              : // the two aren't the same variant, then compare the two discriminant values for
     246              : // all the dataless enum variants and in the general case.
     247              : //
     248              : // so for enum Foo { A(i32, i32), B, C } we need to do the following
     249              : //
     250              : // match (self, other) {
     251              : //     (A(self_0, self_1), A(other_0, other_1)) => {
     252              : //         match cmp_fn(self_0, other_0) {
     253              : //             Ordering::Equal => cmp_fn(self_1, other_1),
     254              : //             cmp => cmp,
     255              : //         },
     256              : //     _ => cmp_fn(discr_value(self), discr_value(other))
     257              : // }
     258              : void
     259           30 : DeriveOrd::visit_enum (Enum &item)
     260              : {
     261              :   // NOTE: We can factor this even further with DerivePartialEq, but this is
     262              :   // getting out of scope for this PR surely
     263              : 
     264           30 :   auto cases = std::vector<MatchCase> ();
     265           60 :   auto type_name = item.get_identifier ().as_string ();
     266              : 
     267           30 :   auto let_sd = builder.discriminant_value (DeriveOrd::self_discr, "self");
     268           30 :   auto let_od = builder.discriminant_value (DeriveOrd::other_discr, "other");
     269              : 
     270           60 :   auto discr_cmp = cmp_call (builder.identifier (DeriveOrd::self_discr),
     271           90 :                              builder.identifier (DeriveOrd::other_discr));
     272              : 
     273           72 :   auto recursive_match_fn = [this] (std::vector<SelfOther> &&fields) {
     274           42 :     return recursive_match (std::move (fields));
     275           30 :   };
     276              : 
     277           98 :   for (auto &variant : item.get_variants ())
     278              :     {
     279           68 :       auto enum_builder
     280          136 :         = EnumMatchBuilder (type_name, variant->get_identifier ().as_string (),
     281           68 :                             recursive_match_fn, builder);
     282              : 
     283           68 :       switch (variant->get_enum_item_kind ())
     284              :         {
     285            8 :         case EnumItem::Kind::Struct:
     286            8 :           cases.emplace_back (enum_builder.strukt (*variant));
     287            8 :           break;
     288           34 :         case EnumItem::Kind::Tuple:
     289           34 :           cases.emplace_back (enum_builder.tuple (*variant));
     290           34 :           break;
     291              :         case EnumItem::Kind::Identifier:
     292              :         case EnumItem::Kind::Discriminant:
     293              :           // We don't need to do anything for these, as they are handled by the
     294              :           // discriminant value comparison
     295              :           break;
     296              :         }
     297           68 :     }
     298              : 
     299              :   // Add the last case which compares the discriminant values in case `self` and
     300              :   // `other` are actually different variants of the enum
     301           30 :   cases.emplace_back (
     302           60 :     builder.match_case (builder.wildcard (), std::move (discr_cmp)));
     303              : 
     304           30 :   auto match
     305           60 :     = builder.match (builder.tuple (vec (builder.identifier ("self"),
     306           60 :                                          builder.identifier ("other"))),
     307           30 :                      std::move (cases));
     308              : 
     309           30 :   expanded
     310           60 :     = cmp_impl (builder.block (vec (std::move (let_sd), std::move (let_od)),
     311              :                                std::move (match)),
     312          150 :                 type_name, item.get_generic_params ());
     313           30 : }
     314              : 
     315              : void
     316            0 : DeriveOrd::visit_union (Union &item)
     317              : {
     318            0 :   auto trait_name = trait (ordering);
     319              : 
     320            0 :   rust_error_at (item.get_locus (), "derive(%s) cannot be used on unions",
     321              :                  trait_name.c_str ());
     322            0 : }
     323              : 
     324              : } // namespace AST
     325              : } // namespace Rust
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.