Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a Ranged wrapper struct to replace the rustc_scalar_range_valid attributes #103724

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 28 additions & 16 deletions compiler/rustc_codegen_cranelift/example/mini_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
pub trait DispatchFromDyn<T> {}

// &T -> &U
impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
// &mut T -> &mut U
impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
// *const T -> *const U
impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
// *mut T -> *mut U
impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T> {}

#[lang = "receiver"]
Expand Down Expand Up @@ -285,7 +285,6 @@ impl PartialEq for u32 {
}
}


impl PartialEq for u64 {
fn eq(&self, other: &u64) -> bool {
(*self) == (*other)
Expand Down Expand Up @@ -358,7 +357,7 @@ impl<T: ?Sized> PartialEq for *const T {
}
}

impl <T: PartialEq> PartialEq for Option<T> {
impl<T: PartialEq> PartialEq for Option<T> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Some(lhs), Some(rhs)) => *lhs == *rhs,
Expand Down Expand Up @@ -469,7 +468,11 @@ pub fn panic(_msg: &'static str) -> ! {
#[track_caller]
fn panic_bounds_check(index: usize, len: usize) -> ! {
unsafe {
libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
libc::printf(
"index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8,
len,
index,
);
intrinsics::abort();
}
}
Expand All @@ -495,9 +498,8 @@ pub trait Deref {
}

#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
pub struct NonNull<T: ?Sized>(pub *const T);
pub struct NonNull<T: ?Sized>(pub Ranged<*const T, { 1..=(usize::MAX as u128) }>);

impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
Expand Down Expand Up @@ -585,7 +587,7 @@ pub mod libc {
// functions. legacy_stdio_definitions.lib which provides the printf wrapper functions as normal
// symbols to link against.
#[cfg_attr(unix, link(name = "c"))]
#[cfg_attr(target_env="msvc", link(name="legacy_stdio_definitions"))]
#[cfg_attr(target_env = "msvc", link(name = "legacy_stdio_definitions"))]
extern "C" {
pub fn printf(format: *const i8, ...) -> i32;
}
Expand Down Expand Up @@ -624,7 +626,7 @@ impl<T> Index<usize> for [T] {
}
}

extern {
extern "C" {
type VaListImpl;
}

Expand All @@ -634,23 +636,33 @@ pub struct VaList<'a>(&'a mut VaListImpl);

#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro stringify($($t:tt)*) { /* compiler built-in */ }
pub macro stringify($($t:tt)*) {
/* compiler built-in */
}

#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro file() { /* compiler built-in */ }
pub macro file() {
/* compiler built-in */
}

#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro line() { /* compiler built-in */ }
pub macro line() {
/* compiler built-in */
}

#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro cfg() { /* compiler built-in */ }
pub macro cfg() {
/* compiler built-in */
}

#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro global_asm() { /* compiler built-in */ }
pub macro global_asm() {
/* compiler built-in */
}

pub static A_STATIC: u8 = 42;

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>

// *After* all of this, check the ABI. We need to check the ABI to handle
// types like `NonNull` where the `Scalar` info is more restrictive than what
// the fields say (`rustc_layout_scalar_valid_range_start`).
// the fields say (`Ranged<T, ...>`).
// But in most cases, this will just propagate what the fields say,
// and then we want the error to point at the field -- so, first recurse,
// then check ABI.
Expand All @@ -783,6 +783,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
}
}
Abi::ScalarPair(a_layout, b_layout) => {
// FIXME: range restrictions work on the first element of a pair.
// There is no `rustc_layout_scalar_valid_range_start` for pairs, so
// we would validate these things as we descend into the fields,
// but that can miss bugs in layout computation. Layout computation
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_const_eval/src/interpret/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,10 @@ macro_rules! make_value_visitor {
);
// ... that contains a `NonNull`... (gladly, only a single field here)
assert_eq!(nonnull_ptr.layout().fields.count(), 1);
let raw_ptr = nonnull_ptr.project_field(self.ecx(), 0)?; // the actual raw ptr
let ranged = nonnull_ptr.project_field(self.ecx(), 0)?; // the Ranged struct
// ... which then contains a `Ranged<*const T>` ...
assert_eq!(ranged.layout().fields.count(), 1);
let raw_ptr = ranged.project_field(self.ecx(), 0)?; // the actual raw ptr
// ... whose only field finally is a raw ptr we can dereference.
self.visit_box(&raw_ptr)?;

Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_const_eval/src/transform/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ struct TypeChecker<'a, 'tcx> {
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
#[track_caller]
fn fail(&self, location: Location, msg: impl AsRef<str>) {
let span = self.body.source_info(location).span;
// We use `delay_span_bug` as we might see broken MIR when other errors have already
Expand Down Expand Up @@ -281,12 +282,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
let check_equal = |this: &Self, location, f_ty| {
if !this.mir_assign_valid_types(ty, f_ty) {
this.fail(
location,
format!(
"Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`",
parent, f, ty, f_ty
location,
format!(
"Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`",
parent, f, ty, f_ty
)
)
)
}
};

Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_error_messages/locales/en-US/passes.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,6 @@ passes_export_name =
attribute should be applied to a free function, impl method or static
.label = not a free function, impl method or static

passes_rustc_layout_scalar_valid_range_not_struct =
attribute should be applied to a struct
.label = not a struct

passes_rustc_layout_scalar_valid_range_arg =
expected exactly one integer literal argument

passes_rustc_legacy_const_generics_only =
#[rustc_legacy_const_generics] functions must only have const generics
.label = non-const generic parameter
Expand Down
10 changes: 0 additions & 10 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -659,16 +659,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// Internal attributes, Layout related:
// ==========================================================================

rustc_attr!(
rustc_layout_scalar_valid_range_start, Normal, template!(List: "value"), ErrorFollowing,
"the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \
niche optimizations in libcore and libstd and will never be stable",
),
rustc_attr!(
rustc_layout_scalar_valid_range_end, Normal, template!(List: "value"), ErrorFollowing,
"the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \
niche optimizations in libcore and libstd and will never be stable",
),
rustc_attr!(
rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing,
"the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to enable \
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ language_item_table! {
Index, sym::index, index_trait, Target::Trait, GenericRequirement::Exact(1);
IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1);

Ranged, sym::ranged , ranged_type, Target::Struct, GenericRequirement::None;

UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None;

Expand Down
45 changes: 20 additions & 25 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ use rustc_span::edition::Edition;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, InnerSpan, Span};
use rustc_target::abi::VariantIdx;
use rustc_target::abi::{Abi, VariantIdx};
use rustc_trait_selection::traits::{self, misc::can_type_implement_copy};

use crate::nonstandard_style::{method_context, MethodLateContext};
Expand Down Expand Up @@ -2524,32 +2524,27 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
}
// Recurse and checks for some compound types. (but not unions)
Adt(adt_def, substs) if !adt_def.is_union() => {
// First check if this ADT has a layout attribute (like `NonNull` and friends).
use std::ops::Bound;
match cx.tcx.layout_scalar_valid_range(adt_def.did()) {
// We exploit here that `layout_scalar_valid_range` will never
// return `Bound::Excluded`. (And we have tests checking that we
// handle the attribute correctly.)
// We don't add a span since users cannot declare such types anyway.
(Bound::Included(lo), Bound::Included(hi)) if 0 < lo && lo < hi => {
return Some((format!("`{}` must be non-null", ty), None));
}
(Bound::Included(lo), Bound::Unbounded) if 0 < lo => {
return Some((format!("`{}` must be non-null", ty), None));
}
(Bound::Included(_), _) | (_, Bound::Included(_))
if init == InitKind::Uninit =>
{
return Some((
format!(
"`{}` must be initialized inside its custom valid range",
ty,
),
None,
));
// First check if this ADT has a constrained layout (like `NonNull` and friends).
if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) {
match &layout.abi {
Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) => {
let range = scalar.valid_range(cx);
if !range.contains(0) {
return Some((format!("`{}` must be non-null", ty), None));
} else if init == InitKind::Uninit && !scalar.is_always_valid(cx) {
return Some((
format!(
"`{}` must be initialized inside its custom valid range",
ty,
),
None,
));
}
}
_ => {}
}
_ => {}
}

// Handle structs.
if adt_def.is_struct() {
return variant_find_init_error(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub fn symbols(input: TokenStream) -> TokenStream {
/// `u32::MAX`. You can also customize things like the `Debug` impl,
/// what traits are derived, and so forth via the macro.
#[proc_macro]
#[allow_internal_unstable(step_trait, rustc_attrs, trusted_step)]
#[allow_internal_unstable(step_trait, rustc_attrs, ranged_int, trusted_step)]
pub fn newtype_index(input: TokenStream) -> TokenStream {
newtype::newtype(input)
}
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_macros/src/newtype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl Parse for Newtype {
}
impl<E: ::rustc_serialize::Encoder> ::rustc_serialize::Encodable<E> for #name {
fn encode(&self, e: &mut E) {
e.emit_u32(self.private);
e.emit_u32(self.as_u32());
}
}
}
Expand Down Expand Up @@ -196,10 +196,10 @@ impl Parse for Newtype {
Ok(Self(quote! {
#(#attrs)*
#[derive(Clone, Copy, PartialEq, Eq, Hash, #(#derive_paths),*)]
#[rustc_layout_scalar_valid_range_end(#max)]
#[cfg_attr(bootstrap, rustc_layout_scalar_valid_range_end(#max))]
#[rustc_pass_by_value]
#vis struct #name {
private: u32,
private: std::num::Ranged<u32, {0..=#max}>,
}

#(#consts)*
Expand Down Expand Up @@ -249,7 +249,7 @@ impl Parse for Newtype {
/// Prefer using `from_u32`.
#[inline]
#vis const unsafe fn from_u32_unchecked(value: u32) -> Self {
Self { private: value }
Self { private: std::num::Ranged::new_unchecked(value) }
}

/// Extracts the value of this index as a `usize`.
Expand All @@ -261,7 +261,7 @@ impl Parse for Newtype {
/// Extracts the value of this index as a `u32`.
#[inline]
#vis const fn as_u32(self) -> u32 {
self.private
self.private.get()
}

/// Extracts the value of this index as a `usize`.
Expand Down
17 changes: 0 additions & 17 deletions compiler/rustc_middle/src/mir/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,11 @@ pub enum UnsafetyViolationKind {
pub enum UnsafetyViolationDetails {
CallToUnsafeFunction,
UseOfInlineAssembly,
InitializingTypeWith,
CastOfPointerToInt,
UseOfMutableStatic,
UseOfExternStatic,
DerefOfRawPointer,
AccessToUnionField,
MutationOfLayoutConstrainedField,
BorrowOfLayoutConstrainedField,
CallToFunctionWith,
}

Expand All @@ -54,11 +51,6 @@ impl UnsafetyViolationDetails {
"use of inline assembly",
"inline assembly is entirely unchecked and can cause undefined behavior",
),
InitializingTypeWith => (
"initializing type with `rustc_layout_scalar_valid_range` attr",
"initializing a layout restricted type's field with a value outside the valid \
range is undefined behavior",
),
CastOfPointerToInt => {
("cast of pointer to int", "casting pointers to integers in constants")
}
Expand All @@ -82,15 +74,6 @@ impl UnsafetyViolationDetails {
"the field may not be properly initialized: using uninitialized data will cause \
undefined behavior",
),
MutationOfLayoutConstrainedField => (
"mutation of layout constrained field",
"mutating layout constrained fields cannot statically be checked for valid values",
),
BorrowOfLayoutConstrainedField => (
"borrow of layout constrained field with interior mutability",
"references to fields of layout constrained fields lose the constraints. Coupled \
with interior mutability, the field can be changed to invalid values",
),
CallToFunctionWith => (
"call to function with `#[target_feature]`",
"can only be called if the required target features are available",
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_middle/src/ty/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ bitflags! {
const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8;
/// Indicates whether the type is `UnsafeCell`.
const IS_UNSAFE_CELL = 1 << 9;
/// Indicates whether the type is `Ranged`.
const IS_RANGED = 1 << 10;
}
}

Expand Down Expand Up @@ -249,6 +251,9 @@ impl AdtDefData {
if Some(did) == tcx.lang_items().unsafe_cell_type() {
flags |= AdtFlags::IS_UNSAFE_CELL;
}
if Some(did) == tcx.lang_items().ranged_type() {
flags |= AdtFlags::IS_RANGED;
}

AdtDefData { did, variants, flags, repr }
}
Expand Down Expand Up @@ -341,6 +346,12 @@ impl<'tcx> AdtDef<'tcx> {
self.flags().contains(AdtFlags::IS_UNSAFE_CELL)
}

/// Returns `true` if this is `Ranged<T, RANGE>`.
#[inline]
pub fn is_ranged(self) -> bool {
self.flags().contains(AdtFlags::IS_RANGED)
}

/// Returns `true` if this is `ManuallyDrop<T>`.
#[inline]
pub fn is_manually_drop(self) -> bool {
Expand Down
Loading