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::error::Error<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 name = match tref.as_builtin() {
151 Some(builtin_ty) => &builtin_ty.get_name(),
152 None => ctx.item_name(tref.adt_id()),
153 };
154 self.matches_with_generics(ctx, name, Some(&tref.generics))
155 }
156 TyKind::Array(ty, len) => {
157 let type_name = Name::from_path(&["Array"]);
158 let args = GenericArgs {
159 regions: [].into(),
160 types: [ty.clone()].into(),
161 const_generics: [*len.clone()].into(),
162 trait_refs: [].into(),
163 };
164 self.matches_with_generics(ctx, &type_name, Some(&args))
165 }
166 TyKind::Slice(ty) => {
167 let type_name = Name::from_path(&["Slice"]);
168 let args = GenericArgs {
169 regions: [].into(),
170 types: [ty.clone()].into(),
171 const_generics: [].into(),
172 trait_refs: [].into(),
173 };
174 self.matches_with_generics(ctx, &type_name, Some(&args))
175 }
176 TyKind::Pattern(ty, _) => self.matches_ty(ctx, ty),
177 TyKind::TypeVar(..)
178 | TyKind::Literal(..)
179 | TyKind::Never
180 | TyKind::Ref(..)
181 | TyKind::RawPtr(..)
182 | TyKind::TraitType(..)
183 | TyKind::DynTrait(..)
184 | TyKind::FnPtr(..)
185 | TyKind::FnDef(..)
186 | TyKind::PtrMetadata(..)
187 | TyKind::Error(..) => false,
188 }
189 }
190
191 pub fn matches_const(&self, _ctx: &TranslatedCrate, _c: &ConstantExpr) -> bool {
192 if let [PatElem::Glob] = self.elems.as_slice() {
193 return true;
194 }
195 todo!("non-trivial const generics patterns aren't implemented")
196 }
197
198 pub fn compare(&self, other: &Self) -> Ordering {
202 use Ordering::*;
203 use PatElem::*;
204 match self.len().cmp(&other.len()) {
205 o @ (Less | Greater) => return o,
206 _ if self.len() == 0 => return Equal,
207 Equal => {}
208 }
209 match (self.elems.last().unwrap(), other.elems.last().unwrap()) {
210 (Glob, Glob) => Equal,
211 (Glob, _) => Less,
212 (_, Glob) => Greater,
213 _ => Equal,
215 }
216 }
217}
218
219fn generic_args_match_params(params: &GenericParams, args: &GenericArgs) -> bool {
220 params.regions.len() == args.regions.len()
221 && params.types.len() == args.types.len()
222 && params.const_generics.len() == args.const_generics.len()
223 && params.trait_clauses.len() == args.trait_refs.len()
224}
225
226impl Ord for Pattern {
229 fn cmp(&self, other: &Self) -> Ordering {
230 self.compare(other)
231 }
232}
233impl PartialOrd for Pattern {
234 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
235 Some(self.cmp(other))
236 }
237}
238
239impl PatElem {
240 fn matches_with_generics(
241 &self,
242 ctx: &TranslatedCrate,
243 elem: &PathElem,
244 args: Option<&GenericArgs>,
245 ) -> bool {
246 match (self, elem) {
247 (PatElem::Glob, _) => true,
248 (
249 PatElem::Ident {
250 name: pat_ident,
251 generics,
252 ..
253 },
254 PathElem::Ident(ident, _),
255 ) => {
256 let same_ident =
258 pat_ident == ident || (pat_ident == "crate" && ident == &ctx.crate_name);
259 same_ident && PatTy::matches_generics(ctx, generics, args)
260 }
261 (PatElem::Impl(_pat), PathElem::Impl(ImplElem::Ty(..))) => {
262 false
264 }
265 (PatElem::Impl(pat), PathElem::Impl(ImplElem::Trait(impl_id))) => {
266 let Some(timpl) = ctx.trait_impls.get(*impl_id) else {
267 return false;
268 };
269 let trait_name = ctx.item_name(timpl.impl_trait.id);
270 pat.matches_with_generics(ctx, trait_name, Some(&timpl.impl_trait.generics))
271 }
272 _ => false,
273 }
274 }
275}
276
277impl PatTy {
278 pub fn matches_generics(
279 ctx: &TranslatedCrate,
280 pats: &[Self],
281 generics: Option<&GenericArgs>,
282 ) -> bool {
283 let Some(generics) = generics else {
284 return true;
286 };
287 if pats.is_empty() {
288 return true;
290 }
291 if pats.len() != generics.types.len() + generics.const_generics.len() {
293 return false;
294 }
295 let (type_pats, const_pats) = pats.split_at(generics.types.len());
296 let types_match = generics
297 .types
298 .iter()
299 .zip(type_pats)
300 .all(|(ty, pat)| pat.matches_ty(ctx, ty));
301 let consts_match = generics
302 .const_generics
303 .iter()
304 .zip(const_pats)
305 .all(|(c, pat)| pat.matches_const(ctx, c));
306 types_match && consts_match
307 }
308
309 pub fn matches_ty(&self, ctx: &TranslatedCrate, ty: &Ty) -> bool {
310 match (self, ty.kind()) {
311 (PatTy::Pat(p), _) => p.matches_ty(ctx, ty),
312 (PatTy::Ref(pat_mtbl, p_ty), TyKind::Ref(_, ty, ty_mtbl)) => {
313 pat_mtbl == ty_mtbl && p_ty.matches_ty(ctx, ty)
314 }
315 _ => false,
316 }
317 }
318
319 pub fn matches_const(&self, ctx: &TranslatedCrate, c: &ConstantExpr) -> bool {
320 match self {
321 PatTy::Pat(p) => p.matches_const(ctx, c),
322 PatTy::Ref(..) => false,
323 }
324 }
325}
326
327#[test]
328fn test_compare() {
329 use Ordering::*;
330 let tests = [
331 ("_", Less, "crate"),
332 ("crate::_", Less, "crate::foo"),
333 ("crate::foo", Less, "crate::foo::_"),
334 ];
335 for (x, o, y) in tests {
336 let x = Pattern::parse(x).unwrap();
337 let y = Pattern::parse(y).unwrap();
338 assert_eq!(x.compare(&y), o);
339 }
340}