Skip to content
Open
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,
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::<
*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
13 changes: 11 additions & 2 deletions compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
extern_types,
thread_local,
repr_simd,
pattern_types,
rustc_private
)]
#![no_core]
Expand Down Expand Up @@ -161,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 @@ -227,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::mir::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) =>
{
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
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

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 @@ -940,6 +940,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 @@ -70,13 +70,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),
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'm excited to do this, but it feels odd to me to have both cg_ssa changes and the change to the library type in one PR.

Would it maybe be feasible to split this to have zero library changes, just enough new test coverage to justify the compiler updates, then a follow-up no-compiler-review-needed change to the library part?

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.

hmm, I guess the box elaboration needs to be in the same PR as the library change, doesn't it :/

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.

Yea... I can land some changes ahead of time, but I can only test them with a custom minicore

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.

Gave this another try, I should be able to hit the cg_ssa changes with a test written on rustc today, but I'm missing some necessary component to actually go down this path. I tried https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=72f945c494a52dbb039b2aff32453d41 (and we already have a test that this is a permutation of: https://github.com/rust-lang/rust/blob/main/tests/ui/type/pattern_types/unsize.rs

}

/// `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
// 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
41 changes: 37 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,41 @@ 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));
}

pub struct Unique<T: ?Sized> {
pub pointer: NonNull<T>,
Expand Down Expand Up @@ -306,6 +331,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
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
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@
StorageLive(_3);
StorageLive(_4);
StorageLive(_5);
_5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
_4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
_5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }};
_4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }}, _marker: PhantomData::<[bool; 0]> }};
StorageDead(_5);
_3 = const std::ptr::Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
_3 = const std::ptr::Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }};
StorageDead(_4);
_2 = const Box::<[bool]>(std::ptr::Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
_2 = const Box::<[bool]>(std::ptr::Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
StorageDead(_3);
_1 = const A {{ foo: Box::<[bool]>(std::ptr::Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
_1 = const A {{ foo: Box::<[bool]>(std::ptr::Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
StorageDead(_2);
_0 = const ();
drop(_1) -> [return: bb1, unwind unreachable];
Expand Down
Loading
Loading