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