@@ -289,7 +289,7 @@ impl PatStack {
289289 Self :: from_slice ( & self . 0 [ 1 ..] )
290290 }
291291
292- fn replace_head_with ( & self , pat_ids : & [ PatId ] ) -> PatStack {
292+ fn replace_head_with < T : Into < PatIdOrWild > + Copy > ( & self , pat_ids : & [ T ] ) -> PatStack {
293293 let mut patterns: PatStackInner = smallvec ! [ ] ;
294294 for pat in pat_ids {
295295 patterns. push ( ( * pat) . into ( ) ) ;
@@ -320,12 +320,14 @@ impl PatStack {
320320 constructor : & Constructor ,
321321 ) -> MatchCheckResult < Option < PatStack > > {
322322 let result = match ( self . head ( ) . as_pat ( cx) , constructor) {
323- ( Pat :: Tuple ( ref pat_ids) , Constructor :: Tuple { arity } ) => {
324- debug_assert_eq ! (
325- pat_ids. len( ) ,
326- * arity,
327- "we type check before calling this code, so we should never hit this case" ,
328- ) ;
323+ ( Pat :: Tuple { args : ref pat_ids, ellipsis } , Constructor :: Tuple { arity : _ } ) => {
324+ if ellipsis. is_some ( ) {
325+ // If there are ellipsis here, we should add the correct number of
326+ // Pat::Wild patterns to `pat_ids`. We should be able to use the
327+ // constructors arity for this, but at the time of writing we aren't
328+ // correctly calculating this arity when ellipsis are present.
329+ return Err ( MatchCheckErr :: NotImplemented ) ;
330+ }
329331
330332 Some ( self . replace_head_with ( pat_ids) )
331333 }
@@ -351,19 +353,47 @@ impl PatStack {
351353 Some ( self . to_tail ( ) )
352354 }
353355 }
354- ( Pat :: TupleStruct { args : ref pat_ids, .. } , Constructor :: Enum ( enum_constructor) ) => {
356+ (
357+ Pat :: TupleStruct { args : ref pat_ids, ellipsis, .. } ,
358+ Constructor :: Enum ( enum_constructor) ,
359+ ) => {
355360 let pat_id = self . head ( ) . as_id ( ) . expect ( "we know this isn't a wild" ) ;
356361 if !enum_variant_matches ( cx, pat_id, * enum_constructor) {
357362 None
358363 } else {
359- // If the enum variant matches, then we need to confirm
360- // that the number of patterns aligns with the expected
361- // number of patterns for that enum variant.
362- if pat_ids. len ( ) != constructor. arity ( cx) ? {
363- return Err ( MatchCheckErr :: MalformedMatchArm ) ;
364+ let constructor_arity = constructor. arity ( cx) ?;
365+ if let Some ( ellipsis_position) = ellipsis {
366+ // If there are ellipsis in the pattern, the ellipsis must take the place
367+ // of at least one sub-pattern, so `pat_ids` should be smaller than the
368+ // constructor arity.
369+ if pat_ids. len ( ) < constructor_arity {
370+ let mut new_patterns: Vec < PatIdOrWild > = vec ! [ ] ;
371+
372+ for pat_id in & pat_ids[ 0 ..ellipsis_position] {
373+ new_patterns. push ( ( * pat_id) . into ( ) ) ;
374+ }
375+
376+ for _ in 0 ..( constructor_arity - pat_ids. len ( ) ) {
377+ new_patterns. push ( PatIdOrWild :: Wild ) ;
378+ }
379+
380+ for pat_id in & pat_ids[ ellipsis_position..pat_ids. len ( ) ] {
381+ new_patterns. push ( ( * pat_id) . into ( ) ) ;
382+ }
383+
384+ Some ( self . replace_head_with ( & new_patterns) )
385+ } else {
386+ return Err ( MatchCheckErr :: MalformedMatchArm ) ;
387+ }
388+ } else {
389+ // If there is no ellipsis in the tuple pattern, the number
390+ // of patterns must equal the constructor arity.
391+ if pat_ids. len ( ) == constructor_arity {
392+ Some ( self . replace_head_with ( pat_ids) )
393+ } else {
394+ return Err ( MatchCheckErr :: MalformedMatchArm ) ;
395+ }
364396 }
365-
366- Some ( self . replace_head_with ( pat_ids) )
367397 }
368398 }
369399 ( Pat :: Or ( _) , _) => return Err ( MatchCheckErr :: NotImplemented ) ,
@@ -644,7 +674,11 @@ impl Constructor {
644674fn pat_constructor ( cx : & MatchCheckCtx , pat : PatIdOrWild ) -> MatchCheckResult < Option < Constructor > > {
645675 let res = match pat. as_pat ( cx) {
646676 Pat :: Wild => None ,
647- Pat :: Tuple ( pats) => Some ( Constructor :: Tuple { arity : pats. len ( ) } ) ,
677+ // FIXME somehow create the Tuple constructor with the proper arity. If there are
678+ // ellipsis, the arity is not equal to the number of patterns.
679+ Pat :: Tuple { args : pats, ellipsis } if ellipsis. is_none ( ) => {
680+ Some ( Constructor :: Tuple { arity : pats. len ( ) } )
681+ }
648682 Pat :: Lit ( lit_expr) => match cx. body . exprs [ lit_expr] {
649683 Expr :: Literal ( Literal :: Bool ( val) ) => Some ( Constructor :: Bool ( val) ) ,
650684 _ => return Err ( MatchCheckErr :: NotImplemented ) ,
@@ -1506,6 +1540,67 @@ mod tests {
15061540 check_no_diagnostic ( content) ;
15071541 }
15081542
1543+ #[ test]
1544+ fn enum_tuple_partial_ellipsis_2_no_diagnostic ( ) {
1545+ let content = r"
1546+ enum Either {
1547+ A(bool, bool, bool, bool),
1548+ B,
1549+ }
1550+ fn test_fn() {
1551+ match Either::B {
1552+ Either::A(true, .., true) => {},
1553+ Either::A(true, .., false) => {},
1554+ Either::A(.., true) => {},
1555+ Either::A(.., false) => {},
1556+ Either::B => {},
1557+ }
1558+ }
1559+ " ;
1560+
1561+ check_no_diagnostic ( content) ;
1562+ }
1563+
1564+ #[ test]
1565+ fn enum_tuple_partial_ellipsis_missing_arm ( ) {
1566+ let content = r"
1567+ enum Either {
1568+ A(bool, bool, bool, bool),
1569+ B,
1570+ }
1571+ fn test_fn() {
1572+ match Either::B {
1573+ Either::A(true, .., true) => {},
1574+ Either::A(true, .., false) => {},
1575+ Either::A(false, .., false) => {},
1576+ Either::B => {},
1577+ }
1578+ }
1579+ " ;
1580+
1581+ check_diagnostic ( content) ;
1582+ }
1583+
1584+ #[ test]
1585+ fn enum_tuple_partial_ellipsis_2_missing_arm ( ) {
1586+ let content = r"
1587+ enum Either {
1588+ A(bool, bool, bool, bool),
1589+ B,
1590+ }
1591+ fn test_fn() {
1592+ match Either::B {
1593+ Either::A(true, .., true) => {},
1594+ Either::A(true, .., false) => {},
1595+ Either::A(.., true) => {},
1596+ Either::B => {},
1597+ }
1598+ }
1599+ " ;
1600+
1601+ check_diagnostic ( content) ;
1602+ }
1603+
15091604 #[ test]
15101605 fn enum_tuple_ellipsis_no_diagnostic ( ) {
15111606 let content = r"
@@ -1645,11 +1740,7 @@ mod false_negatives {
16451740 " ;
16461741
16471742 // This is a false negative.
1648- // The `..` pattern is currently lowered to a single `Pat::Wild`
1649- // no matter how many fields the `..` pattern is covering. This
1650- // causes the match arm in this test not to type check against
1651- // the match expression, which causes this diagnostic not to
1652- // fire.
1743+ // We don't currently handle tuple patterns with ellipsis.
16531744 check_no_diagnostic ( content) ;
16541745 }
16551746
@@ -1664,32 +1755,7 @@ mod false_negatives {
16641755 " ;
16651756
16661757 // This is a false negative.
1667- // See comments on `tuple_of_bools_with_ellipsis_at_end_missing_arm`.
1668- check_no_diagnostic ( content) ;
1669- }
1670-
1671- #[ test]
1672- fn enum_tuple_partial_ellipsis_missing_arm ( ) {
1673- let content = r"
1674- enum Either {
1675- A(bool, bool, bool, bool),
1676- B,
1677- }
1678- fn test_fn() {
1679- match Either::B {
1680- Either::A(true, .., true) => {},
1681- Either::A(true, .., false) => {},
1682- Either::A(false, .., false) => {},
1683- Either::B => {},
1684- }
1685- }
1686- " ;
1687-
1688- // This is a false negative.
1689- // The `..` pattern is currently lowered to a single `Pat::Wild`
1690- // no matter how many fields the `..` pattern is covering. This
1691- // causes us to return a `MatchCheckErr::MalformedMatchArm` in
1692- // `Pat::specialize_constructor`.
1758+ // We don't currently handle tuple patterns with ellipsis.
16931759 check_no_diagnostic ( content) ;
16941760 }
16951761}
0 commit comments