1use std::cmp::Ordering;
2
3use itertools::{EitherOrBoth, Itertools};
4use serde::{Deserialize, Serialize};
5
6use crate::{ast::*, formatter::IntoFormatter, pretty::FmtWithCtx};
7
8mod parser;
9
10pub use Pattern as NamePattern;
11
12#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct Pattern {
14 pub elems: Vec<PatElem>,
15}
16
17#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
18pub enum PatElem {
19 Ident {
21 name: String,
22 generics: Vec<PatTy>,
23 is_trait: bool,
25 },
26 Impl(Box<Pattern>),
29 Glob,
31}
32
33#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
34pub enum PatTy {
35 Pat(Pattern),
37 Ref(RefKind, Box<Self>),
39}
40
41impl Pattern {
42 pub fn parse(i: &str) -> Result<Self, nom_supreme::error::ErrorTree<String>> {
43 use std::str::FromStr;
44 Self::from_str(i)
45 }
46 pub fn impl_for(trait_pat: Self) -> Self {
48 Pattern {
49 elems: vec![PatElem::Impl(Box::new(trait_pat))],
50 }
51 }
52
53 fn len(&self) -> usize {
54 self.elems.len()
55 }
56
57 pub fn matches(&self, ctx: &TranslatedCrate, name: &Name) -> bool {
58 self.matches_with_generics(ctx, name, None)
59 }
60
61 pub fn matches_item(&self, ctx: &TranslatedCrate, item: ItemRef<'_>) -> bool {
62 let generics = item.identity_args();
63 let name = &item.item_meta().name;
64 self.matches_with_generics(ctx, name, Some(&generics))
65 }
66
67 pub fn matches_with_generics(
68 &self,
69 ctx: &TranslatedCrate,
70 name: &Name,
71 args: Option<&GenericArgs>,
72 ) -> bool {
73 let mut scrutinee_elems = name.name.as_slice();
74 let mut args: Option<GenericArgs> = args.cloned();
75 if let [prefix @ .., PathElem::Instantiated(instantiation)] = scrutinee_elems {
76 args = match args {
79 None => None,
80 Some(args) if instantiation.params.is_empty() => {
81 assert!(
84 args.len() == args.regions.len(),
85 "In pattern \"{}\" matching against name \"{}\": we have both monomorphized generics {} and regular generics {}",
86 self,
87 name.with_ctx(&ctx.into_fmt()),
88 instantiation.skip_binder.with_ctx(&ctx.into_fmt()),
89 args.with_ctx(&ctx.into_fmt())
90 );
91 let mut mono_args = instantiation.skip_binder.clone();
93 mono_args.regions.extend(args.regions);
94 Some(mono_args)
95 }
96 Some(args) => {
97 assert!(
98 generic_args_match_params(&instantiation.params, &args),
99 "In pattern \"{}\" matching against name \"{}\": the instantiated item generics {} do not match the item parameters",
100 self,
101 name.with_ctx(&ctx.into_fmt()),
102 args.with_ctx(&ctx.into_fmt())
103 );
104 Some(instantiation.as_ref().clone().apply(&args))
105 }
106 };
107 scrutinee_elems = prefix;
108 };
109 let args = args.as_ref();
110 if let Some(PatElem::Impl(_)) = self.elems.first()
114 && let Some((i, _)) = scrutinee_elems
115 .iter()
116 .enumerate()
117 .rfind(|(_, elem)| elem.is_impl())
118 {
119 scrutinee_elems = &scrutinee_elems[i..];
120 }
121
122 let zipped = self.elems.iter().zip_longest(scrutinee_elems).collect_vec();
123 let zipped_len = zipped.len();
124 for (i, x) in zipped.into_iter().enumerate() {
125 let is_last = i + 1 == zipped_len;
126 match x {
127 EitherOrBoth::Both(pat, elem) => {
128 let args = if is_last { args } else { None };
129 if !pat.matches_with_generics(ctx, elem, args) {
130 return false;
131 }
132 }
133 EitherOrBoth::Right(_) => return true,
136 EitherOrBoth::Left(_) => return false,
138 }
139 }
140 true
142 }
143
144 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
145 if let [PatElem::Glob] = self.elems.as_slice() {
146 return true;
147 }
148 match ty.kind() {
149 TyKind::Adt(tref) => {
150 let args = &tref.generics;
151 match tref.id {
152 TypeId::Adt(type_id) => {
153 let type_name = ctx.item_name(type_id);
154 self.matches_with_generics(ctx, type_name, Some(args))
155 }
156 TypeId::Builtin(builtin_ty) => {
157 let name = builtin_ty.get_name();
158 self.matches_with_generics(ctx, &name, Some(args))
159 }
160 TypeId::Tuple => false,
161 }
162 }
163 TyKind::Array(ty, len) => {
164 let type_name = Name::from_path(&["Array"]);
165 let args = GenericArgs {
166 regions: [].into(),
167 types: [ty.clone()].into(),
168 const_generics: [*len.clone()].into(),
169 trait_refs: [].into(),
170 };
171 self.matches_with_generics(ctx, &type_name, Some(&args))
172 }
173 TyKind::Slice(ty) => {
174 let type_name = Name::from_path(&["Slice"]);
175 let args = GenericArgs {
176 regions: [].into(),
177 types: [ty.clone()].into(),
178 const_generics: [].into(),
179 trait_refs: [].into(),
180 };
181 self.matches_with_generics(ctx, &type_name, Some(&args))
182 }
183 TyKind::Pattern(ty, _) => self.matches_ty(ctx, ty),
184 TyKind::TypeVar(..)
185 | TyKind::Literal(..)
186 | TyKind::Never
187 | TyKind::Ref(..)
188 | TyKind::RawPtr(..)
189 | TyKind::TraitType(..)
190 | TyKind::DynTrait(..)
191 | TyKind::FnPtr(..)
192 | TyKind::FnDef(..)
193 | TyKind::PtrMetadata(..)
194 | TyKind::Error(..) => false,
195 }
196 }
197
198 pub fn matches_const(&self, _ctx: &TranslatedCrate, _c: &ConstantExpr) -> bool {
199 if let [PatElem::Glob] = self.elems.as_slice() {
200 return true;
201 }
202 todo!("non-trivial const generics patterns aren't implemented")
203 }
204
205 pub fn compare(&self, other: &Self) -> Ordering {
209 use Ordering::*;
210 use PatElem::*;
211 match self.len().cmp(&other.len()) {
212 o @ (Less | Greater) => return o,
213 _ if self.len() == 0 => return Equal,
214 Equal => {}
215 }
216 match (self.elems.last().unwrap(), other.elems.last().unwrap()) {
217 (Glob, Glob) => Equal,
218 (Glob, _) => Less,
219 (_, Glob) => Greater,
220 _ => Equal,
222 }
223 }
224}
225
226fn generic_args_match_params(params: &GenericParams, args: &GenericArgs) -> bool {
227 params.regions.len() == args.regions.len()
228 && params.types.len() == args.types.len()
229 && params.const_generics.len() == args.const_generics.len()
230 && params.trait_clauses.len() == args.trait_refs.len()
231}
232
233impl Ord for Pattern {
236 fn cmp(&self, other: &Self) -> Ordering {
237 self.compare(other)
238 }
239}
240impl PartialOrd for Pattern {
241 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
242 Some(self.cmp(other))
243 }
244}
245
246impl PatElem {
247 fn matches_with_generics(
248 &self,
249 ctx: &TranslatedCrate,
250 elem: &PathElem,
251 args: Option<&GenericArgs>,
252 ) -> bool {
253 match (self, elem) {
254 (PatElem::Glob, _) => true,
255 (
256 PatElem::Ident {
257 name: pat_ident,
258 generics,
259 ..
260 },
261 PathElem::Ident(ident, _),
262 ) => {
263 let same_ident =
265 pat_ident == ident || (pat_ident == "crate" && ident == &ctx.crate_name);
266 same_ident && PatTy::matches_generics(ctx, generics, args)
267 }
268 (PatElem::Impl(_pat), PathElem::Impl(ImplElem::Ty(..))) => {
269 false
271 }
272 (PatElem::Impl(pat), PathElem::Impl(ImplElem::Trait(impl_id))) => {
273 let Some(timpl) = ctx.trait_impls.get(*impl_id) else {
274 return false;
275 };
276 let trait_name = ctx.item_name(timpl.impl_trait.id);
277 pat.matches_with_generics(ctx, trait_name, Some(&timpl.impl_trait.generics))
278 }
279 _ => false,
280 }
281 }
282}
283
284impl PatTy {
285 pub fn matches_generics(
286 ctx: &TranslatedCrate,
287 pats: &[Self],
288 generics: Option<&GenericArgs>,
289 ) -> bool {
290 let Some(generics) = generics else {
291 return true;
293 };
294 if pats.is_empty() {
295 return true;
297 }
298 if pats.len() != generics.types.len() + generics.const_generics.len() {
300 return false;
301 }
302 let (type_pats, const_pats) = pats.split_at(generics.types.len());
303 let types_match = generics
304 .types
305 .iter()
306 .zip(type_pats)
307 .all(|(ty, pat)| pat.matches_ty(ctx, ty));
308 let consts_match = generics
309 .const_generics
310 .iter()
311 .zip(const_pats)
312 .all(|(c, pat)| pat.matches_const(ctx, c));
313 types_match && consts_match
314 }
315
316 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
317 match (self, ty.kind()) {
318 (PatTy::Pat(p), _) => p.matches_ty(ctx, ty),
319 (PatTy::Ref(pat_mtbl, p_ty), TyKind::Ref(_, ty, ty_mtbl)) => {
320 pat_mtbl == ty_mtbl && p_ty.matches_ty(ctx, ty)
321 }
322 _ => false,
323 }
324 }
325
326 pub fn matches_const(&self, ctx: &TranslatedCrate, c: &ConstantExpr) -> bool {
327 match self {
328 PatTy::Pat(p) => p.matches_const(ctx, c),
329 PatTy::Ref(..) => false,
330 }
331 }
332}
333
334#[test]
335fn test_compare() {
336 use Ordering::*;
337 let tests = [
338 ("_", Less, "crate"),
339 ("crate::_", Less, "crate::foo"),
340 ("crate::foo", Less, "crate::foo::_"),
341 ];
342 for (x, o, y) in tests {
343 let x = Pattern::parse(x).unwrap();
344 let y = Pattern::parse(y).unwrap();
345 assert_eq!(x.compare(&y), o);
346 }
347}