Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 34 additions & 12 deletions crates/hir_ty/src/infer/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ impl<'a> InferenceContext<'a> {
expected: &Ty,
default_bm: BindingMode,
id: PatId,
ellipsis: Option<usize>,
) -> Ty {
let (ty, def) = self.resolve_variant(path);
let var_data = def.map(|it| variant_data(self.db.upcast(), it));
Expand All @@ -34,8 +35,15 @@ impl<'a> InferenceContext<'a> {
let substs = ty.substs().unwrap_or_else(Substs::empty);

let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
let (pre, post) = match ellipsis {
Some(idx) => subpats.split_at(idx),
None => (&subpats[..], &[][..]),
};
let post_idx_offset = field_tys.iter().count() - post.len();

for (i, &subpat) in subpats.iter().enumerate() {
let pre_iter = pre.iter().enumerate();
let post_iter = (post_idx_offset..).zip(post.iter());
for (i, &subpat) in pre_iter.chain(post_iter) {
let expected_ty = var_data
.as_ref()
.and_then(|d| d.field(&Name::new_tuple_field(i)))
Expand Down Expand Up @@ -111,20 +119,29 @@ impl<'a> InferenceContext<'a> {
let expected = expected;

let ty = match &body[pat] {
Pat::Tuple { ref args, .. } => {
&Pat::Tuple { ref args, ellipsis } => {
let expectations = match expected.as_tuple() {
Some(parameters) => &*parameters.0,
_ => &[],
};
let expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown));

let inner_tys = args
.iter()
.zip(expectations_iter)
.map(|(&pat, ty)| self.infer_pat(pat, ty, default_bm))
.collect();
let (pre, post) = match ellipsis {
Some(idx) => args.split_at(idx),
None => (&args[..], &[][..]),
};
let n_uncovered_patterns = expectations.len().saturating_sub(args.len());
let mut expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown));
let mut infer_pat = |(&pat, ty)| self.infer_pat(pat, ty, default_bm);

let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + args.len());
inner_tys.extend(pre.iter().zip(expectations_iter.by_ref()).map(&mut infer_pat));
inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns).cloned());
inner_tys.extend(post.iter().zip(expectations_iter).map(infer_pat));

Ty::apply(TypeCtor::Tuple { cardinality: args.len() as u16 }, Substs(inner_tys))
Ty::apply(
TypeCtor::Tuple { cardinality: inner_tys.len() as u16 },
Substs(inner_tys.into()),
)
}
Pat::Or(ref pats) => {
if let Some((first_pat, rest)) = pats.split_first() {
Expand All @@ -150,9 +167,14 @@ impl<'a> InferenceContext<'a> {
let subty = self.infer_pat(*pat, expectation, default_bm);
Ty::apply_one(TypeCtor::Ref(*mutability), subty)
}
Pat::TupleStruct { path: p, args: subpats, .. } => {
self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat)
}
Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
p.as_ref(),
subpats,
expected,
default_bm,
pat,
*ellipsis,
),
Pat::Record { path: p, args: fields, ellipsis: _ } => {
self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat)
}
Expand Down
95 changes: 95 additions & 0 deletions crates/hir_ty/src/tests/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -679,3 +679,98 @@ fn box_pattern() {
"#]],
);
}

#[test]
fn tuple_ellipsis_pattern() {
check_infer(
r#"
fn foo(tuple: (u8, i16, f32)) {
match tuple {
(.., b, c) => {},
(a, .., c) => {},
(a, b, ..) => {},
(a, b) => {/*too short*/}
(a, b, c, d) => {/*too long*/}
_ => {}
}
}"#,
expect![[r#"
7..12 'tuple': (u8, i16, f32)
30..224 '{ ... } }': ()
36..222 'match ... }': ()
42..47 'tuple': (u8, i16, f32)
58..68 '(.., b, c)': (u8, i16, f32)
63..64 'b': i16
66..67 'c': f32
72..74 '{}': ()
84..94 '(a, .., c)': (u8, i16, f32)
85..86 'a': u8
92..93 'c': f32
98..100 '{}': ()
110..120 '(a, b, ..)': (u8, i16, f32)
111..112 'a': u8
114..115 'b': i16
124..126 '{}': ()
136..142 '(a, b)': (u8, i16, f32)
137..138 'a': u8
140..141 'b': i16
146..161 '{/*too short*/}': ()
170..182 '(a, b, c, d)': (u8, i16, f32, {unknown})
171..172 'a': u8
174..175 'b': i16
177..178 'c': f32
180..181 'd': {unknown}
186..200 '{/*too long*/}': ()
209..210 '_': (u8, i16, f32)
214..216 '{}': ()
"#]],
);
}

#[test]
fn tuple_struct_ellipsis_pattern() {
check_infer(
r#"
struct Tuple(u8, i16, f32);
fn foo(tuple: Tuple) {
match tuple {
Tuple(.., b, c) => {},
Tuple(a, .., c) => {},
Tuple(a, b, ..) => {},
Tuple(a, b) => {/*too short*/}
Tuple(a, b, c, d) => {/*too long*/}
_ => {}
}
}"#,
expect![[r#"
35..40 'tuple': Tuple
49..268 '{ ... } }': ()
55..266 'match ... }': ()
61..66 'tuple': Tuple
77..92 'Tuple(.., b, c)': Tuple
87..88 'b': i16
90..91 'c': f32
96..98 '{}': ()
108..123 'Tuple(a, .., c)': Tuple
114..115 'a': u8
121..122 'c': f32
127..129 '{}': ()
139..154 'Tuple(a, b, ..)': Tuple
145..146 'a': u8
148..149 'b': i16
158..160 '{}': ()
170..181 'Tuple(a, b)': Tuple
176..177 'a': u8
179..180 'b': i16
185..200 '{/*too short*/}': ()
209..226 'Tuple(... c, d)': Tuple
215..216 'a': u8
218..219 'b': i16
221..222 'c': f32
224..225 'd': {unknown}
230..244 '{/*too long*/}': ()
253..254 '_': Tuple
258..260 '{}': ()
"#]],
);
}