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