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;
@@ -858,13 +858,14 @@ fn is_niche_optimization_candidate<'tcx>(
858
858
}
859
859
860
860
/// 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`.
862
862
/// 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 .
864
864
pub ( crate ) fn repr_nullable_ptr < ' tcx > (
865
865
tcx : TyCtxt < ' tcx > ,
866
866
typing_env : ty:: TypingEnv < ' tcx > ,
867
867
ty : Ty < ' tcx > ,
868
+ checked_conversion_is_from : bool ,
868
869
) -> Option < Ty < ' tcx > > {
869
870
debug ! ( "is_repr_nullable_ptr(tcx, ty = {:?})" , ty) ;
870
871
match ty. kind ( ) {
@@ -889,6 +890,20 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
889
890
_ => return None ,
890
891
} ;
891
892
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
+
892
907
if !ty_is_known_nonnull ( tcx, typing_env, field_ty) {
893
908
return None ;
894
909
}
@@ -925,11 +940,191 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
925
940
}
926
941
None
927
942
}
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
+ }
929
952
_ => None ,
930
953
}
931
954
}
932
955
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
+
933
1128
fn get_nullable_type_from_pat < ' tcx > (
934
1129
tcx : TyCtxt < ' tcx > ,
935
1130
typing_env : ty:: TypingEnv < ' tcx > ,
@@ -959,19 +1154,19 @@ fn is_outer_optionlike_around_ty<'tcx>(
959
1154
// That outer_ty is an enum, that this enum doesn't have a defined discriminant representation,
960
1155
// and the the outer_ty's size is that of ty.
961
1156
if let ty:: Adt ( def, _) = outer_ty. kind ( ) {
962
- if !matches ! ( def. adt_kind( ) , AdtKind :: Enum )
1157
+ if ( !matches ! ( def. adt_kind( ) , AdtKind :: Enum ) )
963
1158
|| def. repr ( ) . c ( )
964
1159
|| def. repr ( ) . transparent ( )
965
- || def. repr ( ) . int . is_none ( )
1160
+ || def. repr ( ) . int . is_some ( )
966
1161
{
967
1162
false
968
1163
} else {
969
1164
let ( tcx, typing_env) = ( cx. tcx , cx. typing_env ( ) ) ;
970
1165
971
1166
// 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) ;
973
1168
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) ,
975
1170
_ => false ,
976
1171
}
977
1172
}
0 commit comments