Skip to content
Merged
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
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/pattern_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> TyPat {
TyPatKind::Or(variants.into_iter().map(|pat| pat_to_ty_pat(cx, pat)).collect())
}
ast::PatKind::Err(guar) => TyPatKind::Err(guar),
ast::PatKind::Paren(p) => pat_to_ty_pat(cx, *p).kind,
_ => TyPatKind::Err(cx.dcx().span_err(pat.span, "pattern not supported in pattern types")),
};
ty_pat(kind, pat.span)
Expand Down
60 changes: 55 additions & 5 deletions compiler/rustc_codegen_cranelift/example/mini_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,38 @@
rustc_attrs,
rustc_private,
transparent_unions,
pattern_types,
auto_traits,
freeze_impls
)]
#![cfg_attr(not(all(windows, target_env = "gnu")), feature(thread_local))]
#![no_core]
#![allow(dead_code, internal_features, ambiguous_wide_pointer_comparisons)]

#[lang = "pointee_trait"]
pub trait Pointee: PointeeSized {
#[lang = "metadata_type"]
// needed so that layout_of will return `TooGeneric` instead of `Unknown`
// when asked for the layout of `*const T`. Which is important for making
// transmutes between raw pointers (and especially pattern types of raw pointers)
// work.
type Metadata: Copy + Sync + Unpin + Freeze;
}

#[lang = "dyn_metadata"]
pub struct DynMetadata<Dyn: PointeeSized> {
_vtable_ptr: NonNull<VTable>,
_phantom: PhantomData<Dyn>,
}

unsafe extern "C" {
/// Opaque type for accessing vtables.
///
/// Private implementation detail of `DynMetadata::size_of` etc.
/// There is conceptually not actually any Abstract Machine memory behind this pointer.
type VTable;
}

#[lang = "pointee_sized"]
pub trait PointeeSized {}

Expand Down Expand Up @@ -105,7 +130,7 @@ unsafe impl<'a, T: PointeeSized> Sync for &'a T {}
unsafe impl<T: Sync, const N: usize> Sync for [T; N] {}

#[lang = "freeze"]
unsafe auto trait Freeze {}
pub unsafe auto trait Freeze {}

unsafe impl<T: PointeeSized> Freeze for PhantomData<T> {}
unsafe impl<T: PointeeSized> Freeze for *const T {}
Expand Down Expand Up @@ -570,10 +595,24 @@ pub trait Deref {
fn deref(&self) -> &Self::Target;
}

#[rustc_builtin_macro(pattern_type)]
#[macro_export]
macro_rules! pattern_type {
($($arg:tt)*) => {
/* compiler built-in */
};
}

impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<pattern_type!(*const U is !null)> for pattern_type!(*const T is !null) where
T: Unsize<U>
{
}

impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<pattern_type!(U is !null)> for pattern_type!(T is !null) {}

#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
pub struct NonNull<T: PointeeSized>(pub *const T);
pub struct NonNull<T: PointeeSized>(pub pattern_type!(*const T is !null));

impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
Expand All @@ -600,7 +639,16 @@ impl<T> Box<T> {
let size = size_of::<T>();
let ptr = libc::malloc(size);
intrinsics::copy(&val as *const T as *const u8, ptr, size);
Box(Unique { pointer: NonNull(ptr as *const T), _marker: PhantomData }, Global)
Box(
Unique {
pointer: NonNull(intrinsics::transmute::<
Comment thread
wesleywiser marked this conversation as resolved.
*mut u8,
pattern_type!(*const T is !null),
>(ptr)),
_marker: PhantomData,
},
Global,
)
}
}
}
Expand All @@ -609,7 +657,9 @@ impl<T: ?Sized, A> Drop for Box<T, A> {
fn drop(&mut self) {
// inner value is dropped by compiler
unsafe {
libc::free(self.0.pointer.0 as *mut u8);
libc::free(intrinsics::transmute::<pattern_type!(*const T is !null), *const T>(
self.0.pointer.0,
) as *mut u8);
}
}
}
Expand Down
23 changes: 20 additions & 3 deletions compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
#![feature(no_core, lang_items, never_type, extern_types, thread_local, repr_simd, rustc_private)]
#![feature(
no_core,
lang_items,
never_type,
extern_types,
thread_local,
repr_simd,
pattern_types,
rustc_private
)]
#![cfg_attr(not(any(jit, target_vendor = "apple", windows)), feature(linkage))]
#![no_core]
#![allow(dead_code, non_camel_case_types, internal_features)]
Expand Down Expand Up @@ -153,7 +162,10 @@ extern "C" fn bool_struct_in_11(_arg0: bool_11) {}

#[allow(unreachable_code)] // FIXME false positive
fn main() {
take_unique(Unique { pointer: unsafe { NonNull(1 as *mut ()) }, _marker: PhantomData });
take_unique(Unique {
pointer: unsafe { NonNull(intrinsics::transmute(1 as *mut ())) },
_marker: PhantomData,
});
take_f32(0.1);

call_return_u128_pair();
Expand Down Expand Up @@ -219,7 +231,12 @@ fn main() {
let noisy_unsized_drop = const { intrinsics::needs_drop::<NoisyDropUnsized>() };
assert!(noisy_unsized_drop);

Unique { pointer: NonNull(1 as *mut &str), _marker: PhantomData } as Unique<dyn SomeTrait>;
Unique {
pointer: NonNull(intrinsics::transmute::<_, pattern_type!(*const &str is !null)>(
1 as *mut &str,
)),
_marker: PhantomData,
} as Unique<dyn SomeTrait>;

struct MyDst<T: ?Sized>(T);

Expand Down
12 changes: 10 additions & 2 deletions compiler/rustc_codegen_gcc/example/mini_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
transparent_unions,
auto_traits,
freeze_impls,
pattern_types,
thread_local
)]
#![no_core]
Expand Down Expand Up @@ -580,9 +581,16 @@ pub struct Global;
impl Allocator for Global {}

#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
pub struct NonNull<T: PointeeSized>(pub *const T);
pub struct NonNull<T: PointeeSized>(pub pattern_type!(*const T is !null));

#[rustc_builtin_macro(pattern_type)]
#[macro_export]
macro_rules! pattern_type {
($($arg:tt)*) => {
/* compiler built-in */
};
}

impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
Expand Down
9 changes: 8 additions & 1 deletion compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem, MonoItemPartitions};
use rustc_middle::query::Providers;
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_middle::ty::{self, Instance, PatternKind, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::Session;
use rustc_session::config::{self, CrateType, EntryFnType};
Expand Down Expand Up @@ -273,6 +273,13 @@ pub(crate) fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let src_ty = src.layout.ty;
let dst_ty = dst.layout.ty;
match (src_ty.kind(), dst_ty.kind()) {
(&ty::Pat(s, sp), &ty::Pat(d, dp))
if let (PatternKind::NotNull, PatternKind::NotNull) = (*sp, *dp) =>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unsure: does the specific kind of pattern really matter here? Would just

Suggested change
if let (PatternKind::NotNull, PatternKind::NotNull) = (*sp, *dp) =>
if sp == dp =>

be ok?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, if we ever end up with anything but a NotNull, I'm worried, so I like running into the bug! case at the end. Maybe in the future we will support other things, but I'm not sure they should go down the same code path here

{
let src = src.project_type(bx, s);
let dst = dst.project_type(bx, d);
coerce_unsized_into(bx, src, dst)
}
(&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => {
let (base, info) = match bx.load_operand(src).val {
OperandValue::Pair(base, info) => unsize_ptr(bx, base, src_ty, dst_ty, Some(info)),
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_const_eval/src/interpret/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,12 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {

// ... that contains a `NonNull`... (gladly, only a single field here)
assert_eq!(nonnull_ptr.layout().fields.count(), 1);
let raw_ptr = self.ecx().project_field(&nonnull_ptr, FieldIdx::ZERO)?; // the actual raw ptr
let pat_ty = self.ecx().project_field(&nonnull_ptr, FieldIdx::ZERO)?; // `*mut T is !null`
let base = match *pat_ty.layout().ty.kind() {
ty::Pat(base, _) => self.ecx().layout_of(base)?,
_ => unreachable!(),
};
let raw_ptr = pat_ty.transmute(base, self.ecx())?; // The actual raw pointer

// ... whose only field finally is a raw ptr we can dereference.
self.visit_box(ty, &raw_ptr)?;
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_transform/src/add_retag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> b
| ty::Str
| ty::FnDef(..)
| ty::Never => false,
ty::Pat(base, ..) => may_contain_reference(*base, depth, tcx),
// References and Boxes (`noalias` sources)
ty::Ref(..) => true,
ty::Adt(..) if ty.is_box() => true,
Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc_abi::FieldIdx;
use rustc_middle::mir::visit::MutVisitor;
use rustc_middle::mir::*;
use rustc_middle::span_bug;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{self, PatternKind, Ty, TyCtxt};

use crate::patch::MirPatch;

Expand Down Expand Up @@ -137,8 +137,10 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs {
build_ptr_tys(tcx, boxed_ty, unique_def, nonnull_def);

new_projections.extend_from_slice(&build_projection(unique_ty, nonnull_ty));
// While we can't project into `NonNull<_>` in a basic block
// due to MCP#807, this is debug info where it's fine.
// While we can't project into a pattern type in a basic block,
// this is debug info where it's fine.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"it's fine" because the pattern type is erased in the debug info?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because debug info has no UB, we can just do whatever works instead of having to worry about soundness

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's less about soundness and more that debug info doesn't need to worry about losing the validity restriction information. We don't want to load an i32 from an i32 is 1.. because that loses the !range metadata, but in debug info whatever.

let pat_ty = Ty::new_pat(tcx, ptr_ty, tcx.mk_pat(PatternKind::NotNull));
new_projections.push(PlaceElem::Field(FieldIdx::ZERO, pat_ty));
new_projections.push(PlaceElem::Field(FieldIdx::ZERO, ptr_ty));
new_projections.push(PlaceElem::Deref);
} else if let Some(new_projections) = new_projections.as_mut() {
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,8 @@ marker_impls! {
{T: PointeeSized} *mut T,
{T: PointeeSized} &T,
{T: PointeeSized} &mut T,
{T: PointeeSized} pattern_type!(*const T is !null),
{T: PointeeSized} pattern_type!(*mut T is !null),
}

/// Types that do not require any pinning guarantees.
Expand Down
5 changes: 1 addition & 4 deletions library/core/src/ptr/non_null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,10 @@ use crate::{fmt, hash, intrinsics, mem, ptr};
/// [null pointer optimization]: crate::option#representation
#[stable(feature = "nonnull", since = "1.25.0")]
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
#[rustc_diagnostic_item = "NonNull"]
pub struct NonNull<T: PointeeSized> {
// Remember to use `.as_ptr()` instead of `.pointer`, as field projecting to
// this is banned by <https://github.com/rust-lang/compiler-team/issues/807>.
pointer: *const T,
pointer: crate::pattern_type!(*const T is !null),
Comment thread
scottmcm marked this conversation as resolved.
}

/// `NonNull` pointers are not `Send` because the data they reference may be aliased.
Expand Down
3 changes: 1 addition & 2 deletions library/std/src/os/unix/io/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use crate::os::unix::io::RawFd;

#[test]
fn test_raw_fd_layout() {
// `OwnedFd` and `BorrowedFd` use `rustc_layout_scalar_valid_range_start`
// and `rustc_layout_scalar_valid_range_end`, with values that depend on
// `OwnedFd` and `BorrowedFd` use pattern types, with ranges that depend on
Comment thread
scottmcm marked this conversation as resolved.
// the bit width of `RawFd`. If this ever changes, those values will need
// to be updated.
assert_eq!(size_of::<RawFd>(), 4);
Expand Down
3 changes: 1 addition & 2 deletions library/std/src/os/wasi/io/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use crate::os::wasi::io::RawFd;

#[test]
fn test_raw_fd_layout() {
// `OwnedFd` and `BorrowedFd` use `rustc_layout_scalar_valid_range_start`
// and `rustc_layout_scalar_valid_range_end`, with values that depend on
// `OwnedFd` and `BorrowedFd` use pattern types with ranges that depend on
// the bit width of `RawFd`. If this ever changes, those values will need
// to be updated.
assert_eq!(size_of::<RawFd>(), 4);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
loop {
ty = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty);
return match *ty.kind() {
ty::Pat(base, _) => {
ty = base;
continue;
},
ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => {
ReducedTy::TypeErasure { raw_ptr_only: false }
},
Expand Down
50 changes: 46 additions & 4 deletions tests/auxiliary/minicore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
auto_traits,
freeze_impls,
negative_impls,
pattern_types,
rustc_attrs,
decl_macro,
f16,
Expand Down Expand Up @@ -127,17 +128,42 @@ pub struct ManuallyDrop<T: PointeeSized> {
impl<T: Copy + PointeeSized> Copy for ManuallyDrop<T> {}

#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
pub struct NonNull<T: ?Sized> {
pointer: *const T,
pointer: pattern_type!(*const T is !null),
}
impl<T: ?Sized> Copy for NonNull<T> {}

#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
pub struct NonZero<T>(T);
pub struct NonZero<T: ZeroablePrimitive>(T::NonZeroInner);

pub trait ZeroablePrimitive {
type NonZeroInner;
}

macro_rules! define_valid_range_type {
($(
$name:ident($int:ident is $pat:pat);
)+) => {$(
#[repr(transparent)]
pub struct $name(pattern_type!($int is $pat));

impl ZeroablePrimitive for $int {
type NonZeroInner = $name;
}
)+};
}

define_valid_range_type! {
NonZeroU8Inner(u8 is 1..=0xFF);
NonZeroU16Inner(u16 is 1..=0xFFFF);
NonZeroU32Inner(u32 is 1..=0xFFFF_FFFF);
NonZeroU64Inner(u64 is 1..=0xFFFF_FFFF_FFFF_FFFF);

NonZeroI8Inner(i8 is (-128..=-1 | 1..=0x7F));
NonZeroI32Inner(i32 is (-0x8000_0000..=-1 | 1..=0x7FFF_FFFF));
}

pub struct Unique<T: ?Sized> {
pub pointer: NonNull<T>,
Expand Down Expand Up @@ -225,6 +251,14 @@ impl Neg for i8 {
}
}

impl Neg for i32 {
type Output = i32;

fn neg(self) -> i32 {
loop {}
}
}

#[lang = "sync"]
pub trait Sync {}
impl_marker_trait!(
Expand Down Expand Up @@ -314,6 +348,14 @@ pub enum c_void {
__variant2,
}

#[rustc_builtin_macro(pattern_type)]
#[macro_export]
macro_rules! pattern_type {
($($arg:tt)*) => {
/* compiler built-in */
};
}

#[lang = "Ordering"]
#[repr(i8)]
pub enum Ordering {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
StorageLive(_1);
- _1 = const 1_usize as std::boxed::Box<Never> (Transmute);
- _2 = copy ((_1.0: std::ptr::Unique<Never>).0: std::ptr::NonNull<Never>) as *const Never (Transmute);
+ _1 = const Box::<Never>(std::ptr::Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
+ _2 = const std::ptr::NonNull::<Never> {{ pointer: {0x1 as *const Never} }} as *const Never (Transmute);
+ _1 = const Box::<Never>(std::ptr::Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
+ _2 = const std::ptr::NonNull::<Never> {{ pointer: {0x1 as *const Never} is !null }} as *const Never (Transmute);
unreachable;
}
}
Expand Down
Loading
Loading