@@ -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