11use std:: iter;
22
3- use rustc_abi:: { BackendRepr , TagEncoding , Variants , WrappingRange } ;
3+ use rustc_abi:: { BackendRepr , Size , TagEncoding , Variants , WrappingRange } ;
44use rustc_hir:: { Expr , ExprKind , HirId , LangItem } ;
55use rustc_middle:: bug;
66use rustc_middle:: ty:: layout:: { LayoutOf , SizeSkeleton } ;
7- use rustc_middle:: ty:: { self , AdtKind , Ty , TyCtxt , TypeVisitableExt } ;
7+ use rustc_middle:: ty:: { self , AdtKind , Const , ScalarInt , Ty , TyCtxt , TypeVisitableExt } ;
88use rustc_session:: { declare_lint, declare_lint_pass, impl_lint_pass} ;
99use rustc_span:: { Span , Symbol , sym} ;
1010use tracing:: debug;
@@ -858,13 +858,14 @@ fn is_niche_optimization_candidate<'tcx>(
858858}
859859
860860/// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
861- /// can, return the type that `ty` can be safely converted to, otherwise return `None`.
861+ /// can, return the type that `ty` can be safely converted to/from , otherwise return `None`.
862862/// Currently restricted to function pointers, boxes, references, `core::num::NonZero`,
863- /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes.
863+ /// `core::ptr::NonNull`, `#[repr(transparent)]` newtypes, and int-range pattern types .
864864pub ( crate ) fn repr_nullable_ptr < ' tcx > (
865865 tcx : TyCtxt < ' tcx > ,
866866 typing_env : ty:: TypingEnv < ' tcx > ,
867867 ty : Ty < ' tcx > ,
868+ checked_conversion_is_from : bool ,
868869) -> Option < Ty < ' tcx > > {
869870 debug ! ( "is_repr_nullable_ptr(tcx, ty = {:?})" , ty) ;
870871 match ty. kind ( ) {
@@ -889,6 +890,20 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
889890 _ => return None ,
890891 } ;
891892
893+ if let ty:: Pat ( base, pat) = field_ty. kind ( ) {
894+ return if let Some ( disallowed) = get_pat_disallowed_value_count ( * pat) {
895+ if disallowed != 1 && checked_conversion_is_from {
896+ // if there are values not taken into account by the optionlike Enum
897+ // then we can't safely convert from the base type, only the pattern type
898+ Some ( field_ty)
899+ } else {
900+ get_nullable_type_from_pat ( tcx, typing_env, * base, * pat)
901+ }
902+ } else {
903+ None
904+ } ;
905+ }
906+
892907 if !ty_is_known_nonnull ( tcx, typing_env, field_ty) {
893908 return None ;
894909 }
@@ -925,11 +940,191 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
925940 }
926941 None
927942 }
928- ty:: Pat ( base, pat) => get_nullable_type_from_pat ( tcx, typing_env, * base, * pat) ,
943+ ty:: Pat ( base, pat) => {
944+ if checked_conversion_is_from && get_pat_disallowed_value_count ( * pat) . is_some ( ) {
945+ // if there are values not taken into account by the pattern (the usual case)
946+ // then we can't safely convert from the base type
947+ None
948+ } else {
949+ get_nullable_type_from_pat ( tcx, typing_env, * base, * pat)
950+ }
951+ }
929952 _ => None ,
930953 }
931954}
932955
956+ /// return the number of disallowed values in a pattern type
957+ /// note that Some(0) actually maps to 2^128 rather than 0
958+ pub ( crate ) fn get_pat_disallowed_value_count < ' tcx > ( pat : ty:: Pattern < ' tcx > ) -> Option < u128 > {
959+ // note the logic in this function assumes that signed ints use one's complement representation,
960+ // which I believe is a requirement for rust
961+
962+ /// find numeric metadata on a pair of range bounds
963+ /// if None, assume that there are no bounds specified
964+ /// and that this is a usize. in other words, all values are allowed
965+ fn unwrap_start_end < ' tcx > (
966+ start : Const < ' tcx > ,
967+ end : Const < ' tcx > ,
968+ ) -> ( bool , Size , ScalarInt , ScalarInt ) {
969+ let usable_bound = match ( start. try_to_value ( ) , end. try_to_value ( ) ) {
970+ ( Some ( ty) , _) | ( _, Some ( ty) ) => ty,
971+ ( None , None ) => bug ! (
972+ "pattern range should have at least one defined value: {:?} - {:?}" ,
973+ start,
974+ end,
975+ ) ,
976+ } ;
977+ let usable_size = usable_bound. valtree . unwrap_leaf ( ) . size ( ) ;
978+ let is_signed = match usable_bound. ty . kind ( ) {
979+ ty:: Int ( _) => true ,
980+ ty:: Uint ( _) | ty:: Char => false ,
981+ kind @ _ => bug ! ( "unexpected non-scalar base for pattern bounds: {:?}" , kind) ,
982+ } ;
983+
984+ let end = match end. try_to_value ( ) {
985+ Some ( end) => end. valtree . unwrap_leaf ( ) ,
986+ None => {
987+ let max_val = if is_signed {
988+ usable_size. signed_int_max ( ) as u128
989+ } else {
990+ usable_size. unsigned_int_max ( )
991+ } ;
992+ ScalarInt :: try_from_uint ( max_val, usable_size) . unwrap ( )
993+ }
994+ } ;
995+ let start = match start. try_to_value ( ) {
996+ Some ( start) => start. valtree . unwrap_leaf ( ) ,
997+ None => {
998+ let min_val = if is_signed {
999+ ( usable_size. signed_int_min ( ) as u128 ) & usable_size. unsigned_int_max ( )
1000+ } else {
1001+ 0_u128
1002+ } ;
1003+ ScalarInt :: try_from_uint ( min_val, usable_size) . unwrap ( )
1004+ }
1005+ } ;
1006+ ( is_signed, usable_size, start, end)
1007+ }
1008+
1009+ match * pat {
1010+ ty:: PatternKind :: Range { start, end } => {
1011+ let ( is_signed, scalar_size, start, end) = unwrap_start_end ( start, end) ;
1012+ let ( scalar_min, scalar_max) = if is_signed {
1013+ (
1014+ ( scalar_size. signed_int_min ( ) as u128 ) & scalar_size. unsigned_int_max ( ) ,
1015+ scalar_size. signed_int_max ( ) as u128 ,
1016+ )
1017+ } else {
1018+ ( 0 , scalar_size. unsigned_int_max ( ) )
1019+ } ;
1020+
1021+ if ( start. to_bits ( scalar_size) , end. to_bits ( scalar_size) ) == ( scalar_min, scalar_max) {
1022+ return None ;
1023+ }
1024+
1025+ // note: allow overflow here because negative values are allowed in the scalars represented here
1026+ let allowed_value_count_minus1 =
1027+ u128:: overflowing_sub ( end. to_bits ( scalar_size) , start. to_bits ( scalar_size) ) . 0
1028+ & scalar_size. unsigned_int_max ( ) ;
1029+ let disallowed_value_count =
1030+ u128:: overflowing_sub ( scalar_size. unsigned_int_max ( ) , allowed_value_count_minus1) . 0 ;
1031+ Some ( disallowed_value_count)
1032+ }
1033+ ty:: PatternKind :: Or ( patterns) => {
1034+ // first, get a simplified an sorted view of the ranges
1035+ let ( is_signed, scalar_size, mut ranges) = {
1036+ let ( is_signed, size, start, end) = match & * patterns[ 0 ] {
1037+ ty:: PatternKind :: Range { start, end } => unwrap_start_end ( * start, * end) ,
1038+ ty:: PatternKind :: Or ( _) => bug ! ( "recursive \" or\" patterns?" ) ,
1039+ } ;
1040+ ( is_signed, size, vec ! [ ( start, end) ] )
1041+ } ;
1042+ let scalar_max = if is_signed {
1043+ scalar_size. signed_int_max ( ) as u128
1044+ } else {
1045+ scalar_size. unsigned_int_max ( )
1046+ } ;
1047+ ranges. reserve ( patterns. len ( ) - 1 ) ;
1048+ for pat in patterns. iter ( ) . skip ( 1 ) {
1049+ match * pat {
1050+ ty:: PatternKind :: Range { start, end } => {
1051+ let ( is_this_signed, this_scalar_size, start, end) =
1052+ unwrap_start_end ( start, end) ;
1053+ assert_eq ! ( is_signed, is_this_signed) ;
1054+ assert_eq ! ( scalar_size, this_scalar_size) ;
1055+ ranges. push ( ( start, end) )
1056+ }
1057+ ty:: PatternKind :: Or ( _) => bug ! ( "recursive \" or\" patterns?" ) ,
1058+ }
1059+ }
1060+ ranges. sort_by_key ( |( start, _end) | {
1061+ let is_positive =
1062+ if is_signed { start. to_bits ( scalar_size) <= scalar_max } else { true } ;
1063+ ( is_positive, start. to_bits ( scalar_size) )
1064+ } ) ;
1065+
1066+ // then, range per range, look at the sizes of the gaps left in between
1067+ // (`prev_tail` is the highest value currently accounted for by the ranges,
1068+ // unless the first range has not been dealt with yet)
1069+ let mut prev_tail = scalar_max;
1070+ let mut disallowed_value_count = 0_u128 ;
1071+ let mut only_had_overlaps = true ;
1072+
1073+ for ( range_i, ( start, end) ) in ranges. into_iter ( ) . enumerate ( ) {
1074+ let ( start, end) = ( start. to_bits ( scalar_size) , end. to_bits ( scalar_size) ) ;
1075+
1076+ // if the start of the current range is lower
1077+ // than the current-highest-range-end, ...
1078+ let current_range_overlap =
1079+ if is_signed && prev_tail > scalar_max && start <= scalar_max {
1080+ false
1081+ } else if start <= u128:: overflowing_add ( prev_tail, 1 ) . 0 {
1082+ range_i > 0 // no overlap possible when dealing with the first range
1083+ } else {
1084+ false
1085+ } ;
1086+ if current_range_overlap {
1087+ // update the curent-highest-range-end, if the current range has a higher end
1088+ if is_signed {
1089+ if prev_tail > scalar_max && end <= scalar_max {
1090+ prev_tail = end;
1091+ } else if prev_tail <= scalar_max && end > scalar_max {
1092+ // nothing to do here
1093+ } else {
1094+ // prev_tail and end have the same sign
1095+ prev_tail = u128:: max ( prev_tail, end)
1096+ }
1097+ } else {
1098+ // prev_tail and end have the same sign
1099+ prev_tail = u128:: max ( prev_tail, end)
1100+ }
1101+ } else {
1102+ // no range overlap: first, add the newfound disallowed values to the count
1103+ only_had_overlaps = false ;
1104+ let new_gap = u128:: overflowing_sub (
1105+ start,
1106+ u128:: overflowing_add ( prev_tail, 1 ) . 0 & scalar_size. unsigned_int_max ( ) ,
1107+ )
1108+ . 0 & scalar_size. unsigned_int_max ( ) ;
1109+ disallowed_value_count =
1110+ u128:: overflowing_add ( disallowed_value_count, new_gap) . 0 ;
1111+ prev_tail = end;
1112+ }
1113+ }
1114+ if prev_tail != scalar_max {
1115+ disallowed_value_count = u128:: overflowing_add (
1116+ disallowed_value_count,
1117+ u128:: overflowing_sub ( scalar_max, prev_tail) . 0 ,
1118+ )
1119+ . 0 ;
1120+ only_had_overlaps = false ;
1121+ }
1122+
1123+ if only_had_overlaps { None } else { Some ( disallowed_value_count) }
1124+ }
1125+ }
1126+ }
1127+
9331128fn get_nullable_type_from_pat < ' tcx > (
9341129 tcx : TyCtxt < ' tcx > ,
9351130 typing_env : ty:: TypingEnv < ' tcx > ,
@@ -959,19 +1154,19 @@ fn is_outer_optionlike_around_ty<'tcx>(
9591154 // That outer_ty is an enum, that this enum doesn't have a defined discriminant representation,
9601155 // and the the outer_ty's size is that of ty.
9611156 if let ty:: Adt ( def, _) = outer_ty. kind ( ) {
962- if !matches ! ( def. adt_kind( ) , AdtKind :: Enum )
1157+ if ( !matches ! ( def. adt_kind( ) , AdtKind :: Enum ) )
9631158 || def. repr ( ) . c ( )
9641159 || def. repr ( ) . transparent ( )
965- || def. repr ( ) . int . is_none ( )
1160+ || def. repr ( ) . int . is_some ( )
9661161 {
9671162 false
9681163 } else {
9691164 let ( tcx, typing_env) = ( cx. tcx , cx. typing_env ( ) ) ;
9701165
9711166 // see the insides of super::repr_nullable_ptr()
972- let compute_size_skeleton = |t| SizeSkeleton :: compute ( t, tcx, typing_env) . ok ( ) ;
1167+ let compute_size_skeleton = |t| SizeSkeleton :: compute ( t, tcx, typing_env) ;
9731168 match ( compute_size_skeleton ( ty) , compute_size_skeleton ( outer_ty) ) {
974- ( Some ( sk1) , Some ( sk2) ) => sk1. same_size ( sk2) ,
1169+ ( Ok ( sk1) , Ok ( sk2) ) => sk1. same_size ( sk2) ,
9751170 _ => false ,
9761171 }
9771172 }
0 commit comments