|
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, Ty, TyCtxt, TypeVisitableExt}; |
| 7 | +use rustc_middle::ty::{self, 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;
|
@@ -863,7 +863,7 @@ fn is_niche_optimization_candidate<'tcx>(
|
863 | 863 | /// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
|
864 | 864 | /// can, return the type that `ty` can be safely converted to, otherwise return `None`.
|
865 | 865 | /// Currently restricted to function pointers, boxes, references, `core::num::NonZero`,
|
866 |
| -/// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. |
| 866 | +/// `core::ptr::NonNull`, `#[repr(transparent)]` newtypes, and int-range pattern types. |
867 | 867 | pub(crate) fn repr_nullable_ptr<'tcx>(
|
868 | 868 | tcx: TyCtxt<'tcx>,
|
869 | 869 | typing_env: ty::TypingEnv<'tcx>,
|
@@ -892,6 +892,14 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
|
892 | 892 | _ => return None,
|
893 | 893 | };
|
894 | 894 |
|
| 895 | + if let ty::Pat(base, pat) = field_ty.kind() { |
| 896 | + if pattern_has_disallowed_values(*pat) || matches!(base.kind(), ty::Char) { |
| 897 | + return get_nullable_type_from_pat(tcx, typing_env, *base, *pat); |
| 898 | + } else { |
| 899 | + return None; |
| 900 | + } |
| 901 | + } |
| 902 | + |
895 | 903 | if !ty_is_known_nonnull(tcx, typing_env, field_ty) {
|
896 | 904 | return None;
|
897 | 905 | }
|
@@ -933,6 +941,148 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
|
933 | 941 | }
|
934 | 942 | }
|
935 | 943 |
|
| 944 | +/// Returns whether a pattern type actually has disallowed values. |
| 945 | +pub(crate) fn pattern_has_disallowed_values<'tcx>(pat: ty::Pattern<'tcx>) -> bool { |
| 946 | + // note the logic in this function assumes that signed ints use one's complement representation, |
| 947 | + // which I believe is a requirement for rust |
| 948 | + |
| 949 | + /// Find numeric metadata on a pair of range bounds. |
| 950 | + /// If None, assume that there are no bounds specified |
| 951 | + /// and that this is a usize. in other words, all values are allowed. |
| 952 | + fn unwrap_start_end<'tcx>( |
| 953 | + start: Const<'tcx>, |
| 954 | + end: Const<'tcx>, |
| 955 | + ) -> (bool, Size, ScalarInt, ScalarInt) { |
| 956 | + let usable_bound = match (start.try_to_value(), end.try_to_value()) { |
| 957 | + (Some(ty), _) | (_, Some(ty)) => ty, |
| 958 | + (None, None) => bug!( |
| 959 | + "pattern range should have at least one defined value: {:?} - {:?}", |
| 960 | + start, |
| 961 | + end, |
| 962 | + ), |
| 963 | + }; |
| 964 | + let usable_size = usable_bound.valtree.unwrap_leaf().size(); |
| 965 | + let is_signed = match usable_bound.ty.kind() { |
| 966 | + ty::Int(_) => true, |
| 967 | + ty::Uint(_) | ty::Char => false, |
| 968 | + kind @ _ => bug!("unexpected non-scalar base for pattern bounds: {:?}", kind), |
| 969 | + }; |
| 970 | + |
| 971 | + let end = match end.try_to_value() { |
| 972 | + Some(end) => end.valtree.unwrap_leaf(), |
| 973 | + None => { |
| 974 | + let max_val = if is_signed { |
| 975 | + usable_size.signed_int_max() as u128 |
| 976 | + } else { |
| 977 | + usable_size.unsigned_int_max() |
| 978 | + }; |
| 979 | + ScalarInt::try_from_uint(max_val, usable_size).unwrap() |
| 980 | + } |
| 981 | + }; |
| 982 | + let start = match start.try_to_value() { |
| 983 | + Some(start) => start.valtree.unwrap_leaf(), |
| 984 | + None => { |
| 985 | + let min_val = if is_signed { |
| 986 | + (usable_size.signed_int_min() as u128) & usable_size.unsigned_int_max() |
| 987 | + } else { |
| 988 | + 0_u128 |
| 989 | + }; |
| 990 | + ScalarInt::try_from_uint(min_val, usable_size).unwrap() |
| 991 | + } |
| 992 | + }; |
| 993 | + (is_signed, usable_size, start, end) |
| 994 | + } |
| 995 | + |
| 996 | + match *pat { |
| 997 | + ty::PatternKind::Range { start, end } => { |
| 998 | + let (is_signed, scalar_size, start, end) = unwrap_start_end(start, end); |
| 999 | + let (scalar_min, scalar_max) = if is_signed { |
| 1000 | + ( |
| 1001 | + (scalar_size.signed_int_min() as u128) & scalar_size.unsigned_int_max(), |
| 1002 | + scalar_size.signed_int_max() as u128, |
| 1003 | + ) |
| 1004 | + } else { |
| 1005 | + (0, scalar_size.unsigned_int_max()) |
| 1006 | + }; |
| 1007 | + |
| 1008 | + (start.to_bits(scalar_size), end.to_bits(scalar_size)) != (scalar_min, scalar_max) |
| 1009 | + } |
| 1010 | + ty::PatternKind::Or(patterns) => { |
| 1011 | + // first, get a simplified an sorted view of the ranges |
| 1012 | + let (is_signed, scalar_size, mut ranges) = { |
| 1013 | + let (is_signed, size, start, end) = match &*patterns[0] { |
| 1014 | + ty::PatternKind::Range { start, end } => unwrap_start_end(*start, *end), |
| 1015 | + ty::PatternKind::Or(_) => bug!("recursive \"or\" patterns?"), |
| 1016 | + }; |
| 1017 | + (is_signed, size, vec![(start, end)]) |
| 1018 | + }; |
| 1019 | + let scalar_max = if is_signed { |
| 1020 | + scalar_size.signed_int_max() as u128 |
| 1021 | + } else { |
| 1022 | + scalar_size.unsigned_int_max() |
| 1023 | + }; |
| 1024 | + ranges.reserve(patterns.len() - 1); |
| 1025 | + for pat in patterns.iter().skip(1) { |
| 1026 | + match *pat { |
| 1027 | + ty::PatternKind::Range { start, end } => { |
| 1028 | + let (is_this_signed, this_scalar_size, start, end) = |
| 1029 | + unwrap_start_end(start, end); |
| 1030 | + assert_eq!(is_signed, is_this_signed); |
| 1031 | + assert_eq!(scalar_size, this_scalar_size); |
| 1032 | + ranges.push((start, end)) |
| 1033 | + } |
| 1034 | + ty::PatternKind::Or(_) => bug!("recursive \"or\" patterns?"), |
| 1035 | + } |
| 1036 | + } |
| 1037 | + ranges.sort_by_key(|(start, _end)| { |
| 1038 | + let is_positive = |
| 1039 | + if is_signed { start.to_bits(scalar_size) <= scalar_max } else { true }; |
| 1040 | + (is_positive, start.to_bits(scalar_size)) |
| 1041 | + }); |
| 1042 | + |
| 1043 | + // then, range per range, look at the sizes of the gaps left in between |
| 1044 | + // (`prev_tail` is the highest value currently accounted for by the ranges, |
| 1045 | + // unless the first range has not been dealt with yet) |
| 1046 | + let mut prev_tail = scalar_max; |
| 1047 | + |
| 1048 | + for (range_i, (start, end)) in ranges.into_iter().enumerate() { |
| 1049 | + let (start, end) = (start.to_bits(scalar_size), end.to_bits(scalar_size)); |
| 1050 | + |
| 1051 | + // if the start of the current range is lower |
| 1052 | + // than the current-highest-range-end, ... |
| 1053 | + let current_range_overlap = |
| 1054 | + if is_signed && prev_tail > scalar_max && start <= scalar_max { |
| 1055 | + false |
| 1056 | + } else if start <= u128::overflowing_add(prev_tail, 1).0 { |
| 1057 | + range_i > 0 // no overlap possible when dealing with the first range |
| 1058 | + } else { |
| 1059 | + false |
| 1060 | + }; |
| 1061 | + if current_range_overlap { |
| 1062 | + // update the current-highest-range-end, if the current range has a higher end |
| 1063 | + if is_signed { |
| 1064 | + if prev_tail > scalar_max && end <= scalar_max { |
| 1065 | + prev_tail = end; |
| 1066 | + } else if prev_tail <= scalar_max && end > scalar_max { |
| 1067 | + // nothing to do here |
| 1068 | + } else { |
| 1069 | + // prev_tail and end have the same sign |
| 1070 | + prev_tail = u128::max(prev_tail, end) |
| 1071 | + } |
| 1072 | + } else { |
| 1073 | + // prev_tail and end have the same sign |
| 1074 | + prev_tail = u128::max(prev_tail, end) |
| 1075 | + } |
| 1076 | + } else { |
| 1077 | + // no range overlap: there are disallowed values |
| 1078 | + return true; |
| 1079 | + } |
| 1080 | + } |
| 1081 | + prev_tail != scalar_max |
| 1082 | + } |
| 1083 | + } |
| 1084 | +} |
| 1085 | + |
936 | 1086 | fn get_nullable_type_from_pat<'tcx>(
|
937 | 1087 | tcx: TyCtxt<'tcx>,
|
938 | 1088 | typing_env: ty::TypingEnv<'tcx>,
|
|
0 commit comments