@@ -19,7 +19,7 @@ use std::cell::Cell;
1919use std:: iter;
2020use std:: ops:: Bound ;
2121
22- use rustc_abi:: ExternAbi ;
22+ use rustc_abi:: { ExternAbi , Size } ;
2323use rustc_ast:: Recovered ;
2424use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
2525use rustc_data_structures:: unord:: UnordMap ;
@@ -605,32 +605,70 @@ pub(super) fn lower_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) {
605605 tcx. ensure_ok ( ) . predicates_of ( def_id) ;
606606}
607607
608- pub ( super ) fn lower_enum_variant_types ( tcx : TyCtxt < ' _ > , def_id : DefId ) {
608+ pub ( super ) fn lower_enum_variant_types ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) {
609609 let def = tcx. adt_def ( def_id) ;
610610 let repr_type = def. repr ( ) . discr_type ( ) ;
611611 let initial = repr_type. initial_discriminant ( tcx) ;
612612 let mut prev_discr = None :: < Discr < ' _ > > ;
613+ // Some of the logic below relies on `i128` being able to hold all c_int and c_uint values.
614+ assert ! ( tcx. sess. target. c_int_width < 128 ) ;
615+ let mut min_discr = i128:: MAX ;
616+ let mut max_discr = i128:: MIN ;
613617
614618 // fill the discriminant values and field types
615619 for variant in def. variants ( ) {
616620 let wrapped_discr = prev_discr. map_or ( initial, |d| d. wrap_incr ( tcx) ) ;
617- prev_discr = Some (
618- if let ty:: VariantDiscr :: Explicit ( const_def_id) = variant. discr {
619- def. eval_explicit_discr ( tcx, const_def_id) . ok ( )
620- } else if let Some ( discr) = repr_type. disr_incr ( tcx, prev_discr) {
621- Some ( discr)
622- } else {
621+ let cur_discr = if let ty:: VariantDiscr :: Explicit ( const_def_id) = variant. discr {
622+ def. eval_explicit_discr ( tcx, const_def_id) . ok ( )
623+ } else if let Some ( discr) = repr_type. disr_incr ( tcx, prev_discr) {
624+ Some ( discr)
625+ } else {
626+ let span = tcx. def_span ( variant. def_id ) ;
627+ tcx. dcx ( ) . emit_err ( errors:: EnumDiscriminantOverflowed {
628+ span,
629+ discr : prev_discr. unwrap ( ) . to_string ( ) ,
630+ item_name : tcx. item_ident ( variant. def_id ) ,
631+ wrapped_discr : wrapped_discr. to_string ( ) ,
632+ } ) ;
633+ None
634+ }
635+ . unwrap_or ( wrapped_discr) ;
636+
637+ if def. repr ( ) . c ( ) {
638+ let c_int = Size :: from_bits ( tcx. sess . target . c_int_width ) ;
639+ let c_uint_max = i128:: try_from ( c_int. unsigned_int_max ( ) ) . unwrap ( ) ;
640+ // c_int is a signed type, so get a proper signed version of the discriminant
641+ let discr_size = cur_discr. ty . int_size_and_signed ( tcx) . 0 ;
642+ let discr_val = discr_size. sign_extend ( cur_discr. val ) ;
643+ min_discr = min_discr. min ( discr_val) ;
644+ max_discr = max_discr. max ( discr_val) ;
645+
646+ // The discriminant range must either fit into c_int or c_uint.
647+ if !( min_discr >= c_int. signed_int_min ( ) && max_discr <= c_int. signed_int_max ( ) )
648+ && !( min_discr >= 0 && max_discr <= c_uint_max)
649+ {
623650 let span = tcx. def_span ( variant. def_id ) ;
624- tcx. dcx ( ) . emit_err ( errors:: EnumDiscriminantOverflowed {
651+ let msg = if discr_val < c_int. signed_int_min ( ) || discr_val > c_uint_max {
652+ "`repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int`"
653+ } else if discr_val < 0 {
654+ "`repr(C)` enum discriminant does not fit into C `unsigned int`, and a previous discriminant does not fit into C `int`"
655+ } else {
656+ "`repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int`"
657+ } ;
658+ tcx. node_span_lint (
659+ rustc_session:: lint:: builtin:: REPR_C_ENUMS_LARGER_THAN_INT ,
660+ tcx. local_def_id_to_hir_id ( def_id) ,
625661 span,
626- discr : prev_discr. unwrap ( ) . to_string ( ) ,
627- item_name : tcx. item_ident ( variant. def_id ) ,
628- wrapped_discr : wrapped_discr. to_string ( ) ,
629- } ) ;
630- None
662+ |d| {
663+ d. primary_message ( msg)
664+ . note ( "`repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C" )
665+ . help ( "use `repr($int_ty)` instead to explicitly set the size of this enum" ) ;
666+ }
667+ ) ;
631668 }
632- . unwrap_or ( wrapped_discr) ,
633- ) ;
669+ }
670+
671+ prev_discr = Some ( cur_discr) ;
634672
635673 for f in & variant. fields {
636674 tcx. ensure_ok ( ) . generics_of ( f. did ) ;
0 commit comments