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
|