load: &'ll Value,
scalar: &abi::Scalar,
) {
- let vr = scalar.valid_range.clone();
+ let vr = scalar.valid_range;
match scalar.value {
abi::Int(..) => {
let range = scalar.valid_range_exclusive(bx);
bx.range_metadata(load, range);
}
}
- abi::Pointer if vr.start() < vr.end() && !vr.contains(&0) => {
+ abi::Pointer if vr.start < vr.end && !vr.contains(0) => {
bx.nonnull_metadata(load);
}
_ => {}
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::{bug, span_bug};
-use rustc_target::abi::{AddressSpace, Align, HasDataLayout, LayoutOf, Primitive, Scalar, Size};
+use rustc_target::abi::{
+ AddressSpace, Align, AllocationRange, HasDataLayout, LayoutOf, Primitive, Scalar, Size,
+};
use tracing::debug;
pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll Value {
Pointer::new(alloc_id, Size::from_bytes(ptr_offset)),
&cx.tcx,
),
- &Scalar { value: Primitive::Pointer, valid_range: 0..=!0 },
+ &Scalar {
+ value: Primitive::Pointer,
+ valid_range: AllocationRange { start: 0, end: !0 },
+ },
cx.type_i8p_ext(address_space),
));
next_offset = offset + pointer_size;
let dataful_discriminant_range =
&dataful_variant_layout.largest_niche.as_ref().unwrap().scalar.valid_range;
- let min = dataful_discriminant_range.start();
- let min = tag.value.size(&tcx).truncate(*min);
+ let min = dataful_discriminant_range.start;
+ let min = tag.value.size(&tcx).truncate(min);
- let max = dataful_discriminant_range.end();
- let max = tag.value.size(&tcx).truncate(*max);
+ let max = dataful_discriminant_range.end;
+ let max = tag.value.size(&tcx).truncate(max);
let dataful_variant_name = def.variants[*dataful_variant].ident.as_str();
let er = scalar.valid_range_exclusive(bx.cx());
if er.end != er.start
- && scalar.valid_range.end() >= scalar.valid_range.start()
+ && scalar.valid_range.end >= scalar.valid_range.start
{
// We want `table[e as usize ± k]` to not
// have bound checks, and this is the most
// convenient place to put the `assume`s.
- if *scalar.valid_range.start() > 0 {
+ if scalar.valid_range.start > 0 {
let enum_value_lower_bound = bx
.cx()
- .const_uint_big(ll_t_in, *scalar.valid_range.start());
+ .const_uint_big(ll_t_in, scalar.valid_range.start);
let cmp_start = bx.icmp(
IntPredicate::IntUGE,
llval,
}
let enum_value_upper_bound =
- bx.cx().const_uint_big(ll_t_in, *scalar.valid_range.end());
+ bx.cx().const_uint_big(ll_t_in, scalar.valid_range.end);
let cmp_end = bx.icmp(
IntPredicate::IntULE,
llval,
// Return the nullable type this Option-like enum can be safely represented with.
let field_ty_abi = &cx.layout_of(field_ty).unwrap().abi;
if let Abi::Scalar(field_ty_scalar) = field_ty_abi {
- match (field_ty_scalar.valid_range.start(), field_ty_scalar.valid_range.end()) {
+ match (field_ty_scalar.valid_range.start, field_ty_scalar.valid_range.end) {
(0, _) => unreachable!("Non-null optimisation extended to a non-zero value."),
(1, _) => {
return Some(get_nullable_type(cx, field_ty).unwrap());
let scalar_unit = |value: Primitive| {
let bits = value.size(dl).bits();
assert!(bits <= 128);
- Scalar { value, valid_range: 0..=(!0 >> (128 - bits)) }
+ Scalar { value, valid_range: AllocationRange { start: 0, end: (!0 >> (128 - bits)) } }
};
let scalar = |value: Primitive| tcx.intern_layout(Layout::scalar(self, scalar_unit(value)));
// Basic scalars.
ty::Bool => tcx.intern_layout(Layout::scalar(
self,
- Scalar { value: Int(I8, false), valid_range: 0..=1 },
+ Scalar { value: Int(I8, false), valid_range: AllocationRange { start: 0, end: 1 } },
)),
ty::Char => tcx.intern_layout(Layout::scalar(
self,
- Scalar { value: Int(I32, false), valid_range: 0..=0x10FFFF },
+ Scalar {
+ value: Int(I32, false),
+ valid_range: AllocationRange { start: 0, end: 0x10FFFF },
+ },
)),
ty::Int(ity) => scalar(Int(Integer::from_int_ty(dl, ity), true)),
ty::Uint(ity) => scalar(Int(Integer::from_uint_ty(dl, ity), false)),
}),
ty::FnPtr(_) => {
let mut ptr = scalar_unit(Pointer);
- ptr.valid_range = 1..=*ptr.valid_range.end();
+ ptr.valid_range = AllocationRange { start: 1, end: ptr.valid_range.end };
tcx.intern_layout(Layout::scalar(self, ptr))
}
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
let mut data_ptr = scalar_unit(Pointer);
if !ty.is_unsafe_ptr() {
- data_ptr.valid_range = 1..=*data_ptr.valid_range.end();
+ data_ptr.valid_range =
+ AllocationRange { start: 1, end: data_ptr.valid_range.end };
}
let pointee = tcx.normalize_erasing_regions(param_env, pointee);
ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
ty::Dynamic(..) => {
let mut vtable = scalar_unit(Pointer);
- vtable.valid_range = 1..=*vtable.valid_range.end();
+ vtable.valid_range =
+ AllocationRange { start: 1, end: vtable.valid_range.end };
vtable
}
_ => return Err(LayoutError::Unknown(unsized_part)),
if let Bound::Included(start) = start {
// FIXME(eddyb) this might be incorrect - it doesn't
// account for wrap-around (end < start) ranges.
- assert!(*scalar.valid_range.start() <= start);
- scalar.valid_range = start..=*scalar.valid_range.end();
+ assert!(scalar.valid_range.start <= start);
+ // scalar.valid_range =
+ // AllocationRange { start, end: scalar.valid_range.end };
+ scalar.valid_range.start = start;
}
if let Bound::Included(end) = end {
// FIXME(eddyb) this might be incorrect - it doesn't
// account for wrap-around (end < start) ranges.
- assert!(*scalar.valid_range.end() >= end);
- scalar.valid_range = *scalar.valid_range.start()..=end;
+ assert!(scalar.valid_range.end >= end);
+ // scalar.valid_range =
+ // AllocationRange { start: scalar.valid_range.start, end };
+ scalar.valid_range.end = end;
}
// Update `largest_niche` if we have introduced a larger niche.
let tag_mask = !0u128 >> (128 - ity.size().bits());
let tag = Scalar {
value: Int(ity, signed),
- valid_range: (min as u128 & tag_mask)..=(max as u128 & tag_mask),
+ valid_range: AllocationRange {
+ start: (min as u128 & tag_mask),
+ end: (max as u128 & tag_mask),
+ },
};
let mut abi = Abi::Aggregate { sized: true };
if tag.value.size(dl) == size {
let max_discr = (info.variant_fields.len() - 1) as u128;
let discr_int = Integer::fit_unsigned(max_discr);
let discr_int_ty = discr_int.to_ty(tcx, false);
- let tag = Scalar { value: Primitive::Int(discr_int, false), valid_range: 0..=max_discr };
+ let tag = Scalar {
+ value: Primitive::Int(discr_int, false),
+ valid_range: AllocationRange { start: 0, end: max_discr },
+ };
let tag_layout = self.tcx.intern_layout(Layout::scalar(self, tag.clone()));
let tag_layout = TyAndLayout { ty: discr_int_ty, layout: tag_layout };
return;
}
- if scalar.valid_range.start() < scalar.valid_range.end() {
- if *scalar.valid_range.start() > 0 {
+ if scalar.valid_range.start < scalar.valid_range.end {
+ if scalar.valid_range.start > 0 {
attrs.set(ArgAttribute::NonNull);
}
}
use std::convert::TryFrom;
use std::fmt::Write;
use std::num::NonZeroUsize;
-use std::ops::RangeInclusive;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_middle::ty;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_span::symbol::{sym, Symbol};
-use rustc_target::abi::{Abi, LayoutOf, Scalar as ScalarAbi, Size, VariantIdx, Variants};
+use rustc_target::abi::{
+ Abi, AllocationRange, LayoutOf, Scalar as ScalarAbi, Size, VariantIdx, Variants,
+};
use std::hash::Hash;
}
}
-// Test if a range that wraps at overflow contains `test`
-fn wrapping_range_contains(r: &RangeInclusive<u128>, test: u128) -> bool {
- let (lo, hi) = r.clone().into_inner();
- if lo > hi {
- // Wrapped
- (..=hi).contains(&test) || (lo..).contains(&test)
- } else {
- // Normal
- r.contains(&test)
- }
-}
-
// Formats such that a sentence like "expected something {}" to mean
// "expected something <in the given range>" makes sense.
-fn wrapping_range_format(r: &RangeInclusive<u128>, max_hi: u128) -> String {
- let (lo, hi) = r.clone().into_inner();
+fn wrapping_range_format(r: AllocationRange, max_hi: u128) -> String {
+ let AllocationRange { start: lo, end: hi } = r;
assert!(hi <= max_hi);
if lo > hi {
format!("less or equal to {}, or greater or equal to {}", hi, lo)
scalar_layout: &ScalarAbi,
) -> InterpResult<'tcx> {
let value = self.read_scalar(op)?;
- let valid_range = &scalar_layout.valid_range;
- let (lo, hi) = valid_range.clone().into_inner();
+ let valid_range = scalar_layout.valid_range;
+ let AllocationRange { start: lo, end: hi } = valid_range;
// Determine the allowed range
// `max_hi` is as big as the size fits
let max_hi = u128::MAX >> (128 - op.layout.size.bits());
Ok(int) => int.assert_bits(op.layout.size),
};
// Now compare. This is slightly subtle because this is a special "wrap-around" range.
- if wrapping_range_contains(&valid_range, bits) {
+ if valid_range.contains(bits) {
Ok(())
} else {
throw_validation_failure!(self.path,
}
}
+/// Inclusive wrap-around range of valid values, that is, if
+/// start > end, it represents `start..=MAX`,
+/// followed by `0..=end`.
+///
+/// That is, for an i8 primitive, a range of `254..=2` means following
+/// sequence:
+///
+/// 254 (-2), 255 (-1), 0, 1, 2
+///
+/// This is intended specifically to mirror LLVM’s `!range` metadata,
+/// semantics.
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+#[derive(HashStable_Generic)]
+pub struct AllocationRange {
+ pub start: u128,
+ pub end: u128,
+}
+
+impl AllocationRange {
+ /// Returns `true` if `v` is contained in the range.
+ #[inline]
+ pub fn contains(&self, v: u128) -> bool {
+ if self.start <= self.end {
+ self.start <= v && v <= self.end
+ } else {
+ self.start <= v || v <= self.end
+ }
+ }
+}
+
/// Information about one scalar component of a Rust type.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[derive(HashStable_Generic)]
pub struct Scalar {
pub value: Primitive,
- /// Inclusive wrap-around range of valid values, that is, if
- /// start > end, it represents `start..=MAX`,
- /// followed by `0..=end`.
- ///
- /// That is, for an i8 primitive, a range of `254..=2` means following
- /// sequence:
- ///
- /// 254 (-2), 255 (-1), 0, 1, 2
- ///
- /// This is intended specifically to mirror LLVM’s `!range` metadata,
- /// semantics.
// FIXME(eddyb) always use the shortest range, e.g., by finding
// the largest space between two consecutive valid values and
// taking everything else as the (shortest) valid range.
- pub valid_range: RangeInclusive<u128>,
+ pub valid_range: AllocationRange,
}
impl Scalar {
pub fn is_bool(&self) -> bool {
- matches!(self.value, Int(I8, false)) && self.valid_range == (0..=1)
+ matches!(self.value, Int(I8, false))
+ && matches!(self.valid_range, AllocationRange { start: 0, end: 1 })
}
/// Returns the valid range as a `x..y` range.
let bits = self.value.size(cx).bits();
assert!(bits <= 128);
let mask = !0u128 >> (128 - bits);
- let start = *self.valid_range.start();
- let end = *self.valid_range.end();
+ let start = self.valid_range.start;
+ let end = self.valid_range.end;
assert_eq!(start, start & mask);
assert_eq!(end, end & mask);
start..(end.wrapping_add(1) & mask)
}
pub fn available<C: HasDataLayout>(&self, cx: &C) -> u128 {
- let Scalar { value, valid_range: ref v } = self.scalar;
+ let Scalar { value, valid_range: v } = self.scalar;
let bits = value.size(cx).bits();
assert!(bits <= 128);
let max_value = !0u128 >> (128 - bits);
// Find out how many values are outside the valid range.
- let niche = v.end().wrapping_add(1)..*v.start();
+ let niche = v.end.wrapping_add(1)..v.start;
niche.end.wrapping_sub(niche.start) & max_value
}
pub fn reserve<C: HasDataLayout>(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> {
assert!(count > 0);
- let Scalar { value, valid_range: ref v } = self.scalar;
+ let Scalar { value, valid_range: v } = self.scalar;
let bits = value.size(cx).bits();
assert!(bits <= 128);
let max_value = !0u128 >> (128 - bits);
}
// Compute the range of invalid values being reserved.
- let start = v.end().wrapping_add(1) & max_value;
- let end = v.end().wrapping_add(count) & max_value;
+ let start = v.end.wrapping_add(1) & max_value;
+ let end = v.end.wrapping_add(count) & max_value;
// If the `end` of our range is inside the valid range,
// then we ran out of invalid values.
// FIXME(eddyb) abstract this with a wraparound range type.
- let valid_range_contains = |x| {
- if v.start() <= v.end() {
- *v.start() <= x && x <= *v.end()
- } else {
- *v.start() <= x || x <= *v.end()
- }
- };
- if valid_range_contains(end) {
+ if v.contains(end) {
return None;
}
- Some((start, Scalar { value, valid_range: *v.start()..=end }))
+ Some((start, Scalar { value, valid_range: AllocationRange { start: v.start, end } }))
}
}
if zero {
let range = &s.valid_range;
// The range must contain 0.
- range.contains(&0) || (*range.start() > *range.end()) // wrap-around allows 0
+ range.contains(0) || (range.start > range.end) // wrap-around allows 0
} else {
// The range must include all values. `valid_range_exclusive` handles
// the wrap-around using target arithmetic; with wrap-around then the full
--> $DIR/ub-nonnull.rs:41:1
|
LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 42, but expected something in the range 10..=30
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 42, but expected something in the range AllocationRange { start: 10, end: 30 }
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 4, align: 4) {
I32,
false,
),
- valid_range: 0..=0,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 0,
+ },
},
tag_encoding: Direct,
tag_field: 0,
I32,
false,
),
- valid_range: 0..=0,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 0,
+ },
},
},
),
I32,
true,
),
- valid_range: 0..=4294967295,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 4294967295,
+ },
},
Scalar {
value: Int(
I32,
true,
),
- valid_range: 0..=4294967295,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 4294967295,
+ },
},
),
largest_niche: None,
I32,
false,
),
- valid_range: 0..=1,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 1,
+ },
},
tag_encoding: Direct,
tag_field: 0,
I32,
false,
),
- valid_range: 0..=1,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 1,
+ },
},
Scalar {
value: Int(
I32,
true,
),
- valid_range: 0..=4294967295,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 4294967295,
+ },
},
),
largest_niche: Some(
I32,
false,
),
- valid_range: 0..=1,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 1,
+ },
},
},
),
I32,
true,
),
- valid_range: 0..=4294967295,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 4294967295,
+ },
},
),
largest_niche: None,
I8,
false,
),
- valid_range: 0..=0,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 0,
+ },
},
tag_encoding: Direct,
tag_field: 0,
I8,
false,
),
- valid_range: 0..=0,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 0,
+ },
},
),
largest_niche: Some(
I8,
false,
),
- valid_range: 0..=0,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 0,
+ },
},
},
),
I8,
false,
),
- valid_range: 255..=255,
+ valid_range: AllocationRange {
+ start: 255,
+ end: 255,
+ },
},
tag_encoding: Direct,
tag_field: 0,
I8,
false,
),
- valid_range: 255..=255,
+ valid_range: AllocationRange {
+ start: 255,
+ end: 255,
+ },
},
),
largest_niche: Some(
I8,
false,
),
- valid_range: 255..=255,
+ valid_range: AllocationRange {
+ start: 255,
+ end: 255,
+ },
},
},
),
I16,
false,
),
- valid_range: 256..=256,
+ valid_range: AllocationRange {
+ start: 256,
+ end: 256,
+ },
},
tag_encoding: Direct,
tag_field: 0,
I16,
false,
),
- valid_range: 256..=256,
+ valid_range: AllocationRange {
+ start: 256,
+ end: 256,
+ },
},
),
largest_niche: Some(
I16,
false,
),
- valid_range: 256..=256,
+ valid_range: AllocationRange {
+ start: 256,
+ end: 256,
+ },
},
},
),
I32,
false,
),
- valid_range: 268435456..=268435456,
+ valid_range: AllocationRange {
+ start: 268435456,
+ end: 268435456,
+ },
},
tag_encoding: Direct,
tag_field: 0,
I32,
false,
),
- valid_range: 268435456..=268435456,
+ valid_range: AllocationRange {
+ start: 268435456,
+ end: 268435456,
+ },
},
),
largest_niche: Some(
I32,
false,
),
- valid_range: 268435456..=268435456,
+ valid_range: AllocationRange {
+ start: 268435456,
+ end: 268435456,
+ },
},
},
),
I32,
true,
),
- valid_range: 2164260864..=2164260864,
+ valid_range: AllocationRange {
+ start: 2164260864,
+ end: 2164260864,
+ },
},
tag_encoding: Direct,
tag_field: 0,
I32,
true,
),
- valid_range: 2164260864..=2164260864,
+ valid_range: AllocationRange {
+ start: 2164260864,
+ end: 2164260864,
+ },
},
),
largest_niche: Some(
I32,
true,
),
- valid_range: 2164260864..=2164260864,
+ valid_range: AllocationRange {
+ start: 2164260864,
+ end: 2164260864,
+ },
},
},
),
I8,
false,
),
- valid_range: 0..=0,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 0,
+ },
},
tag_encoding: Direct,
tag_field: 0,
I8,
false,
),
- valid_range: 0..=0,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 0,
+ },
},
),
largest_niche: Some(
I8,
false,
),
- valid_range: 0..=0,
+ valid_range: AllocationRange {
+ start: 0,
+ end: 0,
+ },
},
},
),
I8,
false,
),
- valid_range: 255..=255,
+ valid_range: AllocationRange {
+ start: 255,
+ end: 255,
+ },
},
tag_encoding: Direct,
tag_field: 0,
I8,
false,
),
- valid_range: 255..=255,
+ valid_range: AllocationRange {
+ start: 255,
+ end: 255,
+ },
},
),
largest_niche: Some(
I8,
false,
),
- valid_range: 255..=255,
+ valid_range: AllocationRange {
+ start: 255,
+ end: 255,
+ },
},
},
),
I16,
false,
),
- valid_range: 256..=256,
+ valid_range: AllocationRange {
+ start: 256,
+ end: 256,
+ },
},
tag_encoding: Direct,
tag_field: 0,
I16,
false,
),
- valid_range: 256..=256,
+ valid_range: AllocationRange {
+ start: 256,
+ end: 256,
+ },
},
),
largest_niche: Some(
I16,
false,
),
- valid_range: 256..=256,
+ valid_range: AllocationRange {
+ start: 256,
+ end: 256,
+ },
},
},
),
I32,
false,
),
- valid_range: 268435456..=268435456,
+ valid_range: AllocationRange {
+ start: 268435456,
+ end: 268435456,
+ },
},
tag_encoding: Direct,
tag_field: 0,
I32,
false,
),
- valid_range: 268435456..=268435456,
+ valid_range: AllocationRange {
+ start: 268435456,
+ end: 268435456,
+ },
},
),
largest_niche: Some(
I32,
false,
),
- valid_range: 268435456..=268435456,
+ valid_range: AllocationRange {
+ start: 268435456,
+ end: 268435456,
+ },
},
},
),
I32,
true,
),
- valid_range: 2164260864..=2164260864,
+ valid_range: AllocationRange {
+ start: 2164260864,
+ end: 2164260864,
+ },
},
tag_encoding: Direct,
tag_field: 0,
I32,
true,
),
- valid_range: 2164260864..=2164260864,
+ valid_range: AllocationRange {
+ start: 2164260864,
+ end: 2164260864,
+ },
},
),
largest_niche: Some(
I32,
true,
),
- valid_range: 2164260864..=2164260864,
+ valid_range: AllocationRange {
+ start: 2164260864,
+ end: 2164260864,
+ },
},
},
),