Skip to content

Commit 67d4585

Browse files
bors[bot]Veykril
andauthored
Merge #6618
6618: Properly infer tuple patterns when encountering ellipsis r=Veykril a=Veykril We basically just split the subpatterns into two halves when the ellipsis is present and then offset the latter half to account for the ignored bindings. Fixes #6616 Co-authored-by: Lukas Wirth <[email protected]>
2 parents f7e63bd + a5efefd commit 67d4585

File tree

2 files changed

+129
-12
lines changed

2 files changed

+129
-12
lines changed

crates/hir_ty/src/infer/pat.rs

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ impl<'a> InferenceContext<'a> {
2323
expected: &Ty,
2424
default_bm: BindingMode,
2525
id: PatId,
26+
ellipsis: Option<usize>,
2627
) -> Ty {
2728
let (ty, def) = self.resolve_variant(path);
2829
let var_data = def.map(|it| variant_data(self.db.upcast(), it));
@@ -34,8 +35,15 @@ impl<'a> InferenceContext<'a> {
3435
let substs = ty.substs().unwrap_or_else(Substs::empty);
3536

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

38-
for (i, &subpat) in subpats.iter().enumerate() {
44+
let pre_iter = pre.iter().enumerate();
45+
let post_iter = (post_idx_offset..).zip(post.iter());
46+
for (i, &subpat) in pre_iter.chain(post_iter) {
3947
let expected_ty = var_data
4048
.as_ref()
4149
.and_then(|d| d.field(&Name::new_tuple_field(i)))
@@ -111,20 +119,29 @@ impl<'a> InferenceContext<'a> {
111119
let expected = expected;
112120

113121
let ty = match &body[pat] {
114-
Pat::Tuple { ref args, .. } => {
122+
&Pat::Tuple { ref args, ellipsis } => {
115123
let expectations = match expected.as_tuple() {
116124
Some(parameters) => &*parameters.0,
117125
_ => &[],
118126
};
119-
let expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown));
120127

121-
let inner_tys = args
122-
.iter()
123-
.zip(expectations_iter)
124-
.map(|(&pat, ty)| self.infer_pat(pat, ty, default_bm))
125-
.collect();
128+
let (pre, post) = match ellipsis {
129+
Some(idx) => args.split_at(idx),
130+
None => (&args[..], &[][..]),
131+
};
132+
let n_uncovered_patterns = expectations.len().saturating_sub(args.len());
133+
let mut expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown));
134+
let mut infer_pat = |(&pat, ty)| self.infer_pat(pat, ty, default_bm);
135+
136+
let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + args.len());
137+
inner_tys.extend(pre.iter().zip(expectations_iter.by_ref()).map(&mut infer_pat));
138+
inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns).cloned());
139+
inner_tys.extend(post.iter().zip(expectations_iter).map(infer_pat));
126140

127-
Ty::apply(TypeCtor::Tuple { cardinality: args.len() as u16 }, Substs(inner_tys))
141+
Ty::apply(
142+
TypeCtor::Tuple { cardinality: inner_tys.len() as u16 },
143+
Substs(inner_tys.into()),
144+
)
128145
}
129146
Pat::Or(ref pats) => {
130147
if let Some((first_pat, rest)) = pats.split_first() {
@@ -150,9 +167,14 @@ impl<'a> InferenceContext<'a> {
150167
let subty = self.infer_pat(*pat, expectation, default_bm);
151168
Ty::apply_one(TypeCtor::Ref(*mutability), subty)
152169
}
153-
Pat::TupleStruct { path: p, args: subpats, .. } => {
154-
self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat)
155-
}
170+
Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
171+
p.as_ref(),
172+
subpats,
173+
expected,
174+
default_bm,
175+
pat,
176+
*ellipsis,
177+
),
156178
Pat::Record { path: p, args: fields, ellipsis: _ } => {
157179
self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat)
158180
}

crates/hir_ty/src/tests/patterns.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,3 +679,98 @@ fn box_pattern() {
679679
"#]],
680680
);
681681
}
682+
683+
#[test]
684+
fn tuple_ellipsis_pattern() {
685+
check_infer(
686+
r#"
687+
fn foo(tuple: (u8, i16, f32)) {
688+
match tuple {
689+
(.., b, c) => {},
690+
(a, .., c) => {},
691+
(a, b, ..) => {},
692+
(a, b) => {/*too short*/}
693+
(a, b, c, d) => {/*too long*/}
694+
_ => {}
695+
}
696+
}"#,
697+
expect![[r#"
698+
7..12 'tuple': (u8, i16, f32)
699+
30..224 '{ ... } }': ()
700+
36..222 'match ... }': ()
701+
42..47 'tuple': (u8, i16, f32)
702+
58..68 '(.., b, c)': (u8, i16, f32)
703+
63..64 'b': i16
704+
66..67 'c': f32
705+
72..74 '{}': ()
706+
84..94 '(a, .., c)': (u8, i16, f32)
707+
85..86 'a': u8
708+
92..93 'c': f32
709+
98..100 '{}': ()
710+
110..120 '(a, b, ..)': (u8, i16, f32)
711+
111..112 'a': u8
712+
114..115 'b': i16
713+
124..126 '{}': ()
714+
136..142 '(a, b)': (u8, i16, f32)
715+
137..138 'a': u8
716+
140..141 'b': i16
717+
146..161 '{/*too short*/}': ()
718+
170..182 '(a, b, c, d)': (u8, i16, f32, {unknown})
719+
171..172 'a': u8
720+
174..175 'b': i16
721+
177..178 'c': f32
722+
180..181 'd': {unknown}
723+
186..200 '{/*too long*/}': ()
724+
209..210 '_': (u8, i16, f32)
725+
214..216 '{}': ()
726+
"#]],
727+
);
728+
}
729+
730+
#[test]
731+
fn tuple_struct_ellipsis_pattern() {
732+
check_infer(
733+
r#"
734+
struct Tuple(u8, i16, f32);
735+
fn foo(tuple: Tuple) {
736+
match tuple {
737+
Tuple(.., b, c) => {},
738+
Tuple(a, .., c) => {},
739+
Tuple(a, b, ..) => {},
740+
Tuple(a, b) => {/*too short*/}
741+
Tuple(a, b, c, d) => {/*too long*/}
742+
_ => {}
743+
}
744+
}"#,
745+
expect![[r#"
746+
35..40 'tuple': Tuple
747+
49..268 '{ ... } }': ()
748+
55..266 'match ... }': ()
749+
61..66 'tuple': Tuple
750+
77..92 'Tuple(.., b, c)': Tuple
751+
87..88 'b': i16
752+
90..91 'c': f32
753+
96..98 '{}': ()
754+
108..123 'Tuple(a, .., c)': Tuple
755+
114..115 'a': u8
756+
121..122 'c': f32
757+
127..129 '{}': ()
758+
139..154 'Tuple(a, b, ..)': Tuple
759+
145..146 'a': u8
760+
148..149 'b': i16
761+
158..160 '{}': ()
762+
170..181 'Tuple(a, b)': Tuple
763+
176..177 'a': u8
764+
179..180 'b': i16
765+
185..200 '{/*too short*/}': ()
766+
209..226 'Tuple(... c, d)': Tuple
767+
215..216 'a': u8
768+
218..219 'b': i16
769+
221..222 'c': f32
770+
224..225 'd': {unknown}
771+
230..244 '{/*too long*/}': ()
772+
253..254 '_': Tuple
773+
258..260 '{}': ()
774+
"#]],
775+
);
776+
}

0 commit comments

Comments
 (0)