@@ -32,7 +32,6 @@ use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
3232use  rustc_trait_selection:: traits:: { self ,  ObligationCtxt } ; 
3333use  rustc_ty_utils:: representability:: { self ,  Representability } ; 
3434
35- use  std:: iter; 
3635use  std:: ops:: ControlFlow ; 
3736
3837pub ( super )  fn  check_abi ( tcx :  TyCtxt < ' _ > ,  hir_id :  hir:: HirId ,  span :  Span ,  abi :  Abi )  { 
@@ -1494,76 +1493,109 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L
14941493        } 
14951494    } 
14961495
1497-     let  mut  disr_vals:  Vec < Discr < ' tcx > >  = Vec :: with_capacity ( vs. len ( ) ) ; 
1498-     // This tracks the previous variant span (in the loop) incase we need it for diagnostics 
1499-     let  mut  prev_variant_span:  Span  = DUMMY_SP ; 
1500-     for  ( ( _,  discr) ,  v)  in  iter:: zip ( def. discriminants ( tcx) ,  vs)  { 
1501-         // Check for duplicate discriminant values 
1502-         if  let  Some ( i)  = disr_vals. iter ( ) . position ( |& x| x. val  == discr. val )  { 
1503-             let  variant_did = def. variant ( VariantIdx :: new ( i) ) . def_id ; 
1504-             let  variant_i_hir_id = tcx. hir ( ) . local_def_id_to_hir_id ( variant_did. expect_local ( ) ) ; 
1505-             let  variant_i = tcx. hir ( ) . expect_variant ( variant_i_hir_id) ; 
1506-             let  i_span = match  variant_i. disr_expr  { 
1507-                 Some ( ref  expr)  => tcx. hir ( ) . span ( expr. hir_id ) , 
1508-                 None  => tcx. def_span ( variant_did) , 
1509-             } ; 
1510-             let  span = match  v. disr_expr  { 
1511-                 Some ( ref  expr)  => tcx. hir ( ) . span ( expr. hir_id ) , 
1512-                 None  => v. span , 
1513-             } ; 
1514-             let  display_discr = format_discriminant_overflow ( tcx,  v,  discr) ; 
1515-             let  display_discr_i = format_discriminant_overflow ( tcx,  variant_i,  disr_vals[ i] ) ; 
1516-             let  no_disr = v. disr_expr . is_none ( ) ; 
1517-             let  mut  err = struct_span_err ! ( 
1518-                 tcx. sess, 
1519-                 sp, 
1520-                 E0081 , 
1521-                 "discriminant value `{}` assigned more than once" , 
1522-                 discr, 
1523-             ) ; 
1524- 
1525-             err. span_label ( i_span,  format ! ( "first assignment of {display_discr_i}" ) ) ; 
1526-             err. span_label ( span,  format ! ( "second assignment of {display_discr}" ) ) ; 
1527- 
1528-             if  no_disr { 
1529-                 err. span_label ( 
1530-                     prev_variant_span, 
1531-                     format ! ( 
1532-                         "assigned discriminant for `{}` was incremented from this discriminant" , 
1533-                         v. ident
1534-                     ) , 
1535-                 ) ; 
1536-             } 
1537-             err. emit ( ) ; 
1538-         } 
1539- 
1540-         disr_vals. push ( discr) ; 
1541-         prev_variant_span = v. span ; 
1542-     } 
1496+     detect_discriminant_duplicate ( tcx,  def. discriminants ( tcx) . collect ( ) ,  vs,  sp) ; 
15431497
15441498    check_representable ( tcx,  sp,  def_id) ; 
15451499    check_transparent ( tcx,  sp,  def) ; 
15461500} 
15471501
1548- /// In the case that a discriminant is both a duplicate and an overflowing literal, 
1549- /// we insert both the assigned discriminant and the literal it overflowed from into the formatted 
1550- /// output. Otherwise we format the discriminant normally. 
1551- fn  format_discriminant_overflow < ' tcx > ( 
1502+ /// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal 
1503+ fn  detect_discriminant_duplicate < ' tcx > ( 
15521504    tcx :  TyCtxt < ' tcx > , 
1553-     variant :  & hir:: Variant < ' _ > , 
1554-     dis :  Discr < ' tcx > , 
1555- )  -> String  { 
1556-     if  let  Some ( expr)  = & variant. disr_expr  { 
1557-         let  body = & tcx. hir ( ) . body ( expr. body ) . value ; 
1558-         if  let  hir:: ExprKind :: Lit ( lit)  = & body. kind 
1559-             && let  rustc_ast:: LitKind :: Int ( lit_value,  _int_kind)  = & lit. node 
1560-             && dis. val  != * lit_value
1561-         { 
1562-                     return  format ! ( "`{dis}` (overflowed from `{lit_value}`)" ) ; 
1505+     mut  discrs :  Vec < ( VariantIdx ,  Discr < ' tcx > ) > , 
1506+     vs :  & ' tcx  [ hir:: Variant < ' tcx > ] , 
1507+     self_span :  Span , 
1508+ )  { 
1509+     // Helper closure to reduce duplicate code. This gets called everytime we detect a duplicate. 
1510+     // Here `idx` refers to the order of which the discriminant appears, and its index in `vs` 
1511+     let  report = |dis :  Discr < ' tcx > , 
1512+                   idx :  usize , 
1513+                   err :  & mut  DiagnosticBuilder < ' _ ,  ErrorGuaranteed > | { 
1514+         let  var = & vs[ idx] ;  // HIR for the duplicate discriminant 
1515+         let  ( span,  display_discr)  = match  var. disr_expr  { 
1516+             Some ( ref  expr)  => { 
1517+                 // In the case the discriminant is both a duplicate and overflowed, let the user know 
1518+                 if  let  hir:: ExprKind :: Lit ( lit)  = & tcx. hir ( ) . body ( expr. body ) . value . kind 
1519+                     && let  rustc_ast:: LitKind :: Int ( lit_value,  _int_kind)  = & lit. node 
1520+                     && * lit_value != dis. val 
1521+                 { 
1522+                     ( tcx. hir ( ) . span ( expr. hir_id ) ,  format ! ( "`{dis}` (overflowed from `{lit_value}`)" ) ) 
1523+                 // Otherwise, format the value as-is 
1524+                 }  else  { 
1525+                     ( tcx. hir ( ) . span ( expr. hir_id ) ,  format ! ( "`{dis}`" ) ) 
1526+                 } 
1527+             } 
1528+             None  => { 
1529+                 // At this point we know this discriminant is a duplicate, and was not explicitly 
1530+                 // assigned by the user. Here we iterate backwards to fetch the HIR for the last 
1531+                 // explictly assigned discriminant, and letting the user know that this was the 
1532+                 // increment startpoint, and how many steps from there leading to the duplicate 
1533+                 if  let  Some ( ( n,  hir:: Variant  {  span,  ident,  .. } ) )  =
1534+                     vs[ ..idx] . iter ( ) . rev ( ) . enumerate ( ) . find ( |v| v. 1 . disr_expr . is_some ( ) ) 
1535+                 { 
1536+                     let  ve_ident = var. ident ; 
1537+                     let  n = n + 1 ; 
1538+                     let  sp = if  n > 1  {  "variants"  }  else  {  "variant"  } ; 
1539+ 
1540+                     err. span_label ( 
1541+                         * span, 
1542+                         format ! ( "discriminant for `{ve_ident}` incremented from this startpoint (`{ident}` + {n} {sp} later => `{ve_ident}` = {dis})" ) , 
1543+                     ) ; 
1544+                 } 
1545+ 
1546+                 ( vs[ idx] . span ,  format ! ( "`{dis}`" ) ) 
1547+             } 
1548+         } ; 
1549+ 
1550+         err. span_label ( span,  format ! ( "{display_discr} assigned here" ) ) ; 
1551+     } ; 
1552+ 
1553+     // Here we loop through the discriminants, comparing each discriminant to another. 
1554+     // When a duplicate is detected, we instatiate an error and point to both 
1555+     // initial and duplicate value. The duplicate discriminant is then discarded by swapping 
1556+     // it with the last element and decrementing the `vec.len` (which is why we have to evaluate 
1557+     // `discrs.len()` anew every iteration, and why this could be tricky to do in a functional 
1558+     // style as we are mutating `discrs` on the fly). 
1559+     let  mut  i = 0 ; 
1560+     while  i < discrs. len ( )  { 
1561+         let  hir_var_i_idx = discrs[ i] . 0 . index ( ) ; 
1562+         let  mut  error:  Option < DiagnosticBuilder < ' _ ,  _ > >  = None ; 
1563+ 
1564+         let  mut  o = i + 1 ; 
1565+         while  o < discrs. len ( )  { 
1566+             let  hir_var_o_idx = discrs[ o] . 0 . index ( ) ; 
1567+ 
1568+             if  discrs[ i] . 1 . val  == discrs[ o] . 1 . val  { 
1569+                 let  err = error. get_or_insert_with ( || { 
1570+                     let  mut  ret = struct_span_err ! ( 
1571+                         tcx. sess, 
1572+                         self_span, 
1573+                         E0081 , 
1574+                         "discriminant value `{}` assigned more than once" , 
1575+                         discrs[ i] . 1 , 
1576+                     ) ; 
1577+ 
1578+                     report ( discrs[ i] . 1 ,  hir_var_i_idx,  & mut  ret) ; 
1579+ 
1580+                     ret
1581+                 } ) ; 
1582+ 
1583+                 report ( discrs[ o] . 1 ,  hir_var_o_idx,  err) ; 
1584+ 
1585+                 // Safe to unwrap here, as we wouldn't reach this point if `discrs` was empty 
1586+                 discrs[ o]  = * discrs. last ( ) . unwrap ( ) ; 
1587+                 discrs. pop ( ) ; 
1588+             }  else  { 
1589+                 o += 1 ; 
1590+             } 
15631591        } 
1564-     } 
15651592
1566-     format ! ( "`{dis}`" ) 
1593+         if  let  Some ( mut  e)  = error { 
1594+             e. emit ( ) ; 
1595+         } 
1596+ 
1597+         i += 1 ; 
1598+     } 
15671599} 
15681600
15691601pub ( super )  fn  check_type_params_are_used < ' tcx > ( 
0 commit comments