1use std::cmp::Ordering;
6
7use cairo_lang_defs::ids::{FunctionTitleId, LanguageElementId};
8use cairo_lang_defs::stable_order::{cmp_elements, cmp_nodes};
9use salsa::Database;
10
11use crate::items::constant::{ConstValue, ConstValueId};
12use crate::items::enm::ConcreteVariant;
13use crate::items::functions::{
14 ConcreteFunctionWithBodyId, GenericFunctionId, GenericFunctionWithBodyId,
15};
16use crate::items::imp::{
17 GeneratedImplItems, ImplId, ImplLongId, NegativeImplId, NegativeImplLongId, UninferredImpl,
18};
19use crate::items::trt::ConcreteTraitId;
20use crate::types::{TypeId, TypeLongId};
21use crate::{FunctionId, GenericArgumentId};
22
23pub trait StableOrd<'db> {
25 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering;
27}
28
29pub fn cmp_slices<'db, T: StableOrd<'db>>(db: &'db dyn Database, a: &[T], b: &[T]) -> Ordering {
31 a.iter()
32 .zip(b)
33 .map(|(a, b)| a.stable_cmp(b, db))
34 .find(|ordering| ordering.is_ne())
35 .unwrap_or(a.len().cmp(&b.len()))
36}
37
38fn cmp_concrete<'db>(
40 db: &'db dyn Database,
41 (a, a_args): (&impl LanguageElementId<'db>, &[GenericArgumentId<'db>]),
42 (b, b_args): (&impl LanguageElementId<'db>, &[GenericArgumentId<'db>]),
43) -> Ordering {
44 cmp_elements(db, a, b).then_with(|| cmp_slices(db, a_args, b_args))
45}
46
47impl<'db, T: StableOrd<'db>, E> StableOrd<'db> for Result<T, E> {
49 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering {
50 match (self, other) {
51 (Ok(a), Ok(b)) => a.stable_cmp(b, db),
52 (Ok(_), Err(_)) => Ordering::Less,
53 (Err(_), Ok(_)) => Ordering::Greater,
54 (Err(_), Err(_)) => Ordering::Equal,
55 }
56 }
57}
58
59impl<'db> StableOrd<'db> for GenericArgumentId<'db> {
60 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering {
61 let kind = |arg: &Self| match arg {
62 Self::Type(_) => 0,
63 Self::Constant(_) => 1,
64 Self::Impl(_) => 2,
65 Self::NegImpl(_) => 3,
66 };
67 match (self, other) {
68 (Self::Type(a), Self::Type(b)) => a.stable_cmp(b, db),
69 (Self::Constant(a), Self::Constant(b)) => a.stable_cmp(b, db),
70 (Self::Impl(a), Self::Impl(b)) => a.stable_cmp(b, db),
71 (Self::NegImpl(a), Self::NegImpl(b)) => a.stable_cmp(b, db),
72 (a, b) => kind(a).cmp(&kind(b)),
73 }
74 }
75}
76
77impl<'db> StableOrd<'db> for TypeId<'db> {
78 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering {
79 use TypeLongId::*;
80 let kind = |ty: &TypeLongId<'db>| match ty {
81 Concrete(_) => 0,
82 Tuple(_) => 1,
83 Snapshot(_) => 2,
84 GenericParameter(_) => 3,
85 Var(_) => 4,
86 NumericLiteral(_) => 5,
87 Coupon(_) => 6,
88 FixedSizeArray { .. } => 7,
89 ImplType(_) => 8,
90 Closure(_) => 9,
91 Missing(_) => 10,
92 };
93 match (self.long(db), other.long(db)) {
94 (Concrete(a), Concrete(b)) => {
95 cmp_elements(db, &a.generic_type(db), &b.generic_type(db))
96 .then_with(|| cmp_slices(db, &a.generic_args(db), &b.generic_args(db)))
97 }
98 (Tuple(a), Tuple(b)) => cmp_slices(db, a, b),
99 (Snapshot(a), Snapshot(b)) => a.stable_cmp(b, db),
100 (GenericParameter(a), GenericParameter(b)) => cmp_elements(db, a, b),
101 (Var(a), Var(b)) | (NumericLiteral(a), NumericLiteral(b)) => a.id.0.cmp(&b.id.0),
102 (Coupon(a), Coupon(b)) => a.stable_cmp(b, db),
103 (
104 FixedSizeArray { type_id: a, size: a_size },
105 FixedSizeArray { type_id: b, size: b_size },
106 ) => a.stable_cmp(b, db).then_with(|| a_size.stable_cmp(b_size, db)),
107 (ImplType(a), ImplType(b)) => a
108 .impl_id()
109 .stable_cmp(&b.impl_id(), db)
110 .then_with(|| cmp_elements(db, &a.ty(), &b.ty())),
111 (Closure(a), Closure(b)) => {
112 cmp_nodes(db, a.params_location.stable_ptr(), b.params_location.stable_ptr())
113 .then_with(|| a.parent_function.stable_cmp(&b.parent_function, db))
114 }
115 (Missing(_), Missing(_)) => Ordering::Equal,
116 (a, b) => kind(a).cmp(&kind(b)),
117 }
118 }
119}
120
121impl<'db> StableOrd<'db> for ConstValueId<'db> {
122 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering {
123 use ConstValue::*;
124 let kind = |value: &ConstValue<'db>| match value {
125 Int(..) => 0,
126 Struct(..) => 1,
127 Enum(..) => 2,
128 NonZero(_) => 3,
129 Generic(_) => 4,
130 ImplConstant(_) => 5,
131 Var(..) => 6,
132 Missing(_) => 7,
133 };
134 match (self.long(db), other.long(db)) {
135 (Int(a, a_ty), Int(b, b_ty)) => a.cmp(b).then_with(|| a_ty.stable_cmp(b_ty, db)),
136 (Struct(a, a_ty), Struct(b, b_ty)) => {
137 a_ty.stable_cmp(b_ty, db).then_with(|| cmp_slices(db, a, b))
138 }
139 (Enum(a, a_payload), Enum(b, b_payload)) => {
140 a.stable_cmp(b, db).then_with(|| a_payload.stable_cmp(b_payload, db))
141 }
142 (NonZero(a), NonZero(b)) => a.stable_cmp(b, db),
143 (Generic(a), Generic(b)) => cmp_elements(db, a, b),
144 (ImplConstant(a), ImplConstant(b)) => a
145 .impl_id()
146 .stable_cmp(&b.impl_id(), db)
147 .then_with(|| cmp_elements(db, &a.trait_constant_id(), &b.trait_constant_id())),
148 (Var(a, a_ty), Var(b, b_ty)) => {
149 a.id.0.cmp(&b.id.0).then_with(|| a_ty.stable_cmp(b_ty, db))
150 }
151 (Missing(_), Missing(_)) => Ordering::Equal,
152 (a, b) => kind(a).cmp(&kind(b)),
153 }
154 }
155}
156
157impl<'db> StableOrd<'db> for ConcreteVariant<'db> {
158 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering {
159 cmp_concrete(
160 db,
161 (&self.concrete_enum_id.enum_id(db), &self.concrete_enum_id.long(db).generic_args),
162 (&other.concrete_enum_id.enum_id(db), &other.concrete_enum_id.long(db).generic_args),
163 )
164 .then_with(|| cmp_elements(db, &self.id, &other.id))
165 }
166}
167
168impl<'db> StableOrd<'db> for ImplId<'db> {
169 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering {
170 use ImplLongId::*;
171 let kind = |imp: &ImplLongId<'db>| match imp {
172 Concrete(_) => 0,
173 GenericParameter(_) => 1,
174 ImplVar(_) => 2,
175 ImplImpl(_) => 3,
176 SelfImpl(_) => 4,
177 GeneratedImpl(_) => 5,
178 };
179 match (self.long(db), other.long(db)) {
180 (Concrete(a), Concrete(b)) => {
181 let (a, b) = (a.long(db), b.long(db));
182 cmp_concrete(
183 db,
184 (&a.impl_def_id, &a.generic_args),
185 (&b.impl_def_id, &b.generic_args),
186 )
187 }
188 (GenericParameter(a), GenericParameter(b)) => cmp_elements(db, a, b),
189 (ImplVar(a), ImplVar(b)) => a.long(db).id.0.cmp(&b.long(db).id.0),
190 (ImplImpl(a), ImplImpl(b)) => a
191 .impl_id()
192 .stable_cmp(&b.impl_id(), db)
193 .then_with(|| cmp_elements(db, &a.trait_impl_id(), &b.trait_impl_id())),
194 (SelfImpl(a), SelfImpl(b)) => a.stable_cmp(b, db),
195 (GeneratedImpl(a), GeneratedImpl(b)) => {
196 let (a, b) = (a.long(db), b.long(db));
197 a.concrete_trait
198 .stable_cmp(&b.concrete_trait, db)
199 .then_with(|| a.impl_items.stable_cmp(&b.impl_items, db))
200 }
201 (a, b) => kind(a).cmp(&kind(b)),
202 }
203 }
204}
205
206impl<'db> StableOrd<'db> for NegativeImplId<'db> {
207 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering {
208 use NegativeImplLongId::*;
209 let kind = |imp: &NegativeImplLongId<'db>| match imp {
210 Solved(_) => 0,
211 GenericParameter(_) => 1,
212 NegativeImplVar(_) => 2,
213 };
214 match (self.long(db), other.long(db)) {
215 (Solved(a), Solved(b)) => a.stable_cmp(b, db),
216 (GenericParameter(a), GenericParameter(b)) => cmp_elements(db, a, b),
217 (NegativeImplVar(a), NegativeImplVar(b)) => a.long(db).id.0.cmp(&b.long(db).id.0),
218 (a, b) => kind(a).cmp(&kind(b)),
219 }
220 }
221}
222
223impl<'db> StableOrd<'db> for GeneratedImplItems<'db> {
225 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering {
226 self.0
227 .iter()
228 .zip(other.0.iter())
229 .map(|((a_ty, a), (b_ty, b))| {
230 cmp_elements(db, a_ty, b_ty).then_with(|| a.stable_cmp(b, db))
231 })
232 .find(|ordering| ordering.is_ne())
233 .unwrap_or(self.0.len().cmp(&other.0.len()))
234 }
235}
236
237impl<'db> StableOrd<'db> for ConcreteTraitId<'db> {
238 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering {
239 let (a, b) = (self.long(db), other.long(db));
240 cmp_concrete(db, (&a.trait_id, &a.generic_args), (&b.trait_id, &b.generic_args))
241 }
242}
243
244impl<'db> StableOrd<'db> for FunctionId<'db> {
247 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering {
248 use GenericFunctionId::*;
249 let (a, b) = (&self.long(db).function, &other.long(db).function);
250 let title = |function: &GenericFunctionId<'db>| match function {
251 Free(id) => (FunctionTitleId::Free(*id), 0),
252 Extern(id) => (FunctionTitleId::Extern(*id), 1),
253 Impl(id) => (FunctionTitleId::Trait(id.function), 2),
254 };
255 let ((a_title, a_kind), (b_title, b_kind)) =
256 (title(&a.generic_function), title(&b.generic_function));
257 cmp_elements(db, &a_title, &b_title)
258 .then_with(|| a_kind.cmp(&b_kind))
259 .then_with(|| match (&a.generic_function, &b.generic_function) {
260 (Impl(a), Impl(b)) => a.impl_id.stable_cmp(&b.impl_id, db),
261 _ => Ordering::Equal,
262 })
263 .then_with(|| cmp_slices(db, &a.generic_args, &b.generic_args))
264 }
265}
266
267impl<'db> StableOrd<'db> for ConcreteFunctionWithBodyId<'db> {
269 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering {
270 use GenericFunctionWithBodyId::*;
271 let (a, b) = (self.long(db), other.long(db));
272 let kind = |function: &GenericFunctionWithBodyId<'db>| match function {
273 Free(_) => 0,
274 Impl(_) => 1,
275 Trait(_) => 2,
276 };
277 cmp_elements(db, &a.function_with_body_id(db), &b.function_with_body_id(db))
278 .then_with(|| kind(&a.generic_function).cmp(&kind(&b.generic_function)))
279 .then_with(|| match (&a.generic_function, &b.generic_function) {
280 (Impl(a), Impl(b)) => {
281 let (a, b) = (a.concrete_impl_id.long(db), b.concrete_impl_id.long(db));
282 cmp_concrete(
283 db,
284 (&a.impl_def_id, &a.generic_args),
285 (&b.impl_def_id, &b.generic_args),
286 )
287 }
288 (Trait(a), Trait(b)) => a.concrete_trait(db).stable_cmp(&b.concrete_trait(db), db),
289 _ => Ordering::Equal,
290 })
291 .then_with(|| cmp_slices(db, &a.generic_args, &b.generic_args))
292 }
293}
294
295impl<'db> StableOrd<'db> for UninferredImpl<'db> {
296 fn stable_cmp(&self, other: &Self, db: &'db dyn Database) -> Ordering {
297 use UninferredImpl::*;
298 let kind = |imp: &Self| match imp {
299 Def(_) => 0,
300 ImplAlias(_) => 1,
301 GenericParam(_) => 2,
302 ImplImpl(_) => 3,
303 GeneratedImpl(_) => 4,
304 };
305 match (self, other) {
306 (Def(a), Def(b)) => cmp_elements(db, a, b),
307 (ImplAlias(a), ImplAlias(b)) => cmp_elements(db, a, b),
308 (GenericParam(a), GenericParam(b)) => cmp_elements(db, a, b),
309 (ImplImpl(a), ImplImpl(b)) => a
310 .impl_id()
311 .stable_cmp(&b.impl_id(), db)
312 .then_with(|| cmp_elements(db, &a.trait_impl_id(), &b.trait_impl_id())),
313 (GeneratedImpl(a), GeneratedImpl(b)) => {
314 let (a, b) = (a.long(db), b.long(db));
315 a.concrete_trait
316 .stable_cmp(&b.concrete_trait, db)
317 .then_with(|| a.impl_items.stable_cmp(&b.impl_items, db))
318 }
319 (a, b) => kind(a).cmp(&kind(b)),
320 }
321 }
322}