-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Start using pattern types in libcore #136006
Open
oli-obk
wants to merge
8
commits into
rust-lang:master
Choose a base branch
from
oli-obk:push-tzonluoyuwkq
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,145
−215
Open
Changes from 1 commit
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
a464513
Allow const patterns of matches to contain pattern types
oli-obk a38427e
Prevent pattern type macro invocations from having trailing tokens
oli-obk 7693fc0
Pull ast pattern type parsing out into a separate function
oli-obk c9d886e
Directly generate TyPat instead of TyPatKind
oli-obk a6cbf7a
Separate pattern lowering from pattern type lowering
oli-obk cb22e52
Split out various pattern type matches into their own function
oli-obk 9957a3c
Add or-patterns to pattern types
oli-obk e65f76e
Start using pattern types in libcore
oli-obk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev
Previous commit
Start using pattern types in libcore
commit e65f76e6c161f770b5bfbcc22d8c1dba4243c885
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -5,60 +5,48 @@ | |||||
)] | ||||||
|
||||||
use crate::cmp::Ordering; | ||||||
use crate::fmt; | ||||||
use crate::hash::{Hash, Hasher}; | ||||||
use crate::marker::StructuralPartialEq; | ||||||
use crate::{fmt, pattern_type}; | ||||||
|
||||||
macro_rules! define_valid_range_type { | ||||||
($( | ||||||
$(#[$m:meta])* | ||||||
$vis:vis struct $name:ident($int:ident as $uint:ident in $low:literal..=$high:literal); | ||||||
$vis:vis struct $name:ident($int:ident is $pat:pat); | ||||||
)+) => {$( | ||||||
#[derive(Clone, Copy, Eq)] | ||||||
#[derive(Clone, Copy)] | ||||||
#[repr(transparent)] | ||||||
#[rustc_layout_scalar_valid_range_start($low)] | ||||||
#[rustc_layout_scalar_valid_range_end($high)] | ||||||
$(#[$m])* | ||||||
$vis struct $name($int); | ||||||
|
||||||
const _: () = { | ||||||
// With the `valid_range` attributes, it's always specified as unsigned | ||||||
assert!(<$uint>::MIN == 0); | ||||||
let ulow: $uint = $low; | ||||||
let uhigh: $uint = $high; | ||||||
assert!(ulow <= uhigh); | ||||||
|
||||||
assert!(size_of::<$int>() == size_of::<$uint>()); | ||||||
}; | ||||||
|
||||||
$vis struct $name(pattern_type!($int is $pat)); | ||||||
impl $name { | ||||||
#[inline] | ||||||
pub const fn new(val: $int) -> Option<Self> { | ||||||
if (val as $uint) >= ($low as $uint) && (val as $uint) <= ($high as $uint) { | ||||||
#[allow(non_contiguous_range_endpoints)] | ||||||
if let $pat = val { | ||||||
// SAFETY: just checked the inclusive range | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
Some(unsafe { $name(val) }) | ||||||
Some(unsafe { $name(crate::mem::transmute(val)) }) | ||||||
} else { | ||||||
None | ||||||
} | ||||||
} | ||||||
|
||||||
/// Constructs an instance of this type from the underlying integer | ||||||
/// primitive without checking whether its zero. | ||||||
/// primitive without checking whether its valid. | ||||||
/// | ||||||
/// # Safety | ||||||
/// Immediate language UB if `val == 0`, as it violates the validity | ||||||
/// Immediate language UB if `val` is not in the range of the pattern type, | ||||||
/// as it violates the validity | ||||||
/// invariant of this type. | ||||||
#[inline] | ||||||
pub const unsafe fn new_unchecked(val: $int) -> Self { | ||||||
// SAFETY: Caller promised that `val` is non-zero. | ||||||
unsafe { $name(val) } | ||||||
// SAFETY: Caller promised that `val` is in the valid range. | ||||||
unsafe { $name(crate::mem::transmute(val)) } | ||||||
} | ||||||
|
||||||
#[inline] | ||||||
pub const fn as_inner(self) -> $int { | ||||||
// SAFETY: This is a transparent wrapper, so unwrapping it is sound | ||||||
// (Not using `.0` due to MCP#807.) | ||||||
unsafe { crate::mem::transmute(self) } | ||||||
// SAFETY: pattern types are always legal values of their base type | ||||||
unsafe { crate::mem::transmute(self.0) } | ||||||
} | ||||||
} | ||||||
|
||||||
|
@@ -67,6 +55,8 @@ macro_rules! define_valid_range_type { | |||||
// by <https://github.com/rust-lang/compiler-team/issues/807>. | ||||||
impl StructuralPartialEq for $name {} | ||||||
|
||||||
impl Eq for $name {} | ||||||
|
||||||
impl PartialEq for $name { | ||||||
#[inline] | ||||||
fn eq(&self, other: &Self) -> bool { | ||||||
|
@@ -104,7 +94,7 @@ macro_rules! define_valid_range_type { | |||||
} | ||||||
|
||||||
define_valid_range_type! { | ||||||
pub struct Nanoseconds(u32 as u32 in 0..=999_999_999); | ||||||
pub struct Nanoseconds(u32 is 0..=999_999_999); | ||||||
} | ||||||
|
||||||
impl Nanoseconds { | ||||||
|
@@ -119,45 +109,30 @@ impl Default for Nanoseconds { | |||||
} | ||||||
} | ||||||
|
||||||
define_valid_range_type! { | ||||||
pub struct NonZeroU8Inner(u8 as u8 in 1..=0xff); | ||||||
pub struct NonZeroU16Inner(u16 as u16 in 1..=0xff_ff); | ||||||
pub struct NonZeroU32Inner(u32 as u32 in 1..=0xffff_ffff); | ||||||
pub struct NonZeroU64Inner(u64 as u64 in 1..=0xffffffff_ffffffff); | ||||||
pub struct NonZeroU128Inner(u128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); | ||||||
|
||||||
pub struct NonZeroI8Inner(i8 as u8 in 1..=0xff); | ||||||
pub struct NonZeroI16Inner(i16 as u16 in 1..=0xff_ff); | ||||||
pub struct NonZeroI32Inner(i32 as u32 in 1..=0xffff_ffff); | ||||||
pub struct NonZeroI64Inner(i64 as u64 in 1..=0xffffffff_ffffffff); | ||||||
pub struct NonZeroI128Inner(i128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); | ||||||
} | ||||||
|
||||||
#[cfg(target_pointer_width = "16")] | ||||||
define_valid_range_type! { | ||||||
pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff); | ||||||
pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff); | ||||||
pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff); | ||||||
} | ||||||
#[cfg(target_pointer_width = "32")] | ||||||
define_valid_range_type! { | ||||||
pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff); | ||||||
pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff); | ||||||
pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff); | ||||||
} | ||||||
#[cfg(target_pointer_width = "64")] | ||||||
define_valid_range_type! { | ||||||
pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff_ffff_ffff); | ||||||
pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff_ffff_ffff); | ||||||
pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff_ffff_ffff); | ||||||
} | ||||||
const HALF_USIZE: usize = usize::MAX >> 1; | ||||||
|
||||||
define_valid_range_type! { | ||||||
pub struct U32NotAllOnes(u32 as u32 in 0..=0xffff_fffe); | ||||||
pub struct I32NotAllOnes(i32 as u32 in 0..=0xffff_fffe); | ||||||
|
||||||
pub struct U64NotAllOnes(u64 as u64 in 0..=0xffff_ffff_ffff_fffe); | ||||||
pub struct I64NotAllOnes(i64 as u64 in 0..=0xffff_ffff_ffff_fffe); | ||||||
pub struct NonZeroU8Inner(u8 is 1..); | ||||||
pub struct NonZeroU16Inner(u16 is 1..); | ||||||
pub struct NonZeroU32Inner(u32 is 1..); | ||||||
pub struct NonZeroU64Inner(u64 is 1..); | ||||||
pub struct NonZeroU128Inner(u128 is 1..); | ||||||
|
||||||
pub struct NonZeroI8Inner(i8 is ..0 | 1..); | ||||||
pub struct NonZeroI16Inner(i16 is ..0 | 1..); | ||||||
pub struct NonZeroI32Inner(i32 is ..0 | 1..); | ||||||
pub struct NonZeroI64Inner(i64 is ..0 | 1..); | ||||||
pub struct NonZeroI128Inner(i128 is ..0 | 1..); | ||||||
|
||||||
pub struct UsizeNoHighBit(usize is 0..=HALF_USIZE); | ||||||
pub struct NonZeroUsizeInner(usize is 1..); | ||||||
pub struct NonZeroIsizeInner(isize is ..0 | 1..); | ||||||
|
||||||
pub struct U32NotAllOnes(u32 is 0..u32::MAX); | ||||||
pub struct I32NotAllOnes(i32 is ..-1 | 0..); | ||||||
|
||||||
pub struct U64NotAllOnes(u64 is 0..u64::MAX); | ||||||
pub struct I64NotAllOnes(i64 is ..-1 | 0..); | ||||||
} | ||||||
|
||||||
pub trait NotAllOnesHelper { | ||||||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
#![unstable( | ||
feature = "temporary_niche_types", | ||
issue = "none", | ||
reason = "for core, alloc, and std internals until pattern types are further along" | ||
)] | ||
|
||
use crate::cmp::Ordering; | ||
use crate::fmt; | ||
use crate::hash::{Hash, Hasher}; | ||
use crate::marker::StructuralPartialEq; | ||
|
||
macro_rules! define_valid_range_type { | ||
($( | ||
$(#[$m:meta])* | ||
$vis:vis struct $name:ident($int:ident as $uint:ident in $low:literal..=$high:literal); | ||
)+) => {$( | ||
#[derive(Clone, Copy, Eq)] | ||
#[repr(transparent)] | ||
#[rustc_layout_scalar_valid_range_start($low)] | ||
#[rustc_layout_scalar_valid_range_end($high)] | ||
$(#[$m])* | ||
$vis struct $name($int); | ||
|
||
const _: () = { | ||
// With the `valid_range` attributes, it's always specified as unsigned | ||
assert!(<$uint>::MIN == 0); | ||
let ulow: $uint = $low; | ||
let uhigh: $uint = $high; | ||
assert!(ulow <= uhigh); | ||
|
||
assert!(size_of::<$int>() == size_of::<$uint>()); | ||
}; | ||
|
||
impl $name { | ||
#[inline] | ||
pub const fn new(val: $int) -> Option<Self> { | ||
if (val as $uint) >= ($low as $uint) && (val as $uint) <= ($high as $uint) { | ||
// SAFETY: just checked the inclusive range | ||
Some(unsafe { $name(val) }) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
/// Constructs an instance of this type from the underlying integer | ||
/// primitive without checking whether its zero. | ||
/// | ||
/// # Safety | ||
/// Immediate language UB if `val == 0`, as it violates the validity | ||
/// invariant of this type. | ||
#[inline] | ||
pub const unsafe fn new_unchecked(val: $int) -> Self { | ||
// SAFETY: Caller promised that `val` is non-zero. | ||
unsafe { $name(val) } | ||
} | ||
|
||
#[inline] | ||
pub const fn as_inner(self) -> $int { | ||
// SAFETY: This is a transparent wrapper, so unwrapping it is sound | ||
// (Not using `.0` due to MCP#807.) | ||
unsafe { crate::mem::transmute(self) } | ||
} | ||
} | ||
|
||
// This is required to allow matching a constant. We don't get it from a derive | ||
// because the derived `PartialEq` would do a field projection, which is banned | ||
// by <https://github.com/rust-lang/compiler-team/issues/807>. | ||
impl StructuralPartialEq for $name {} | ||
|
||
impl PartialEq for $name { | ||
#[inline] | ||
fn eq(&self, other: &Self) -> bool { | ||
self.as_inner() == other.as_inner() | ||
} | ||
} | ||
|
||
impl Ord for $name { | ||
#[inline] | ||
fn cmp(&self, other: &Self) -> Ordering { | ||
Ord::cmp(&self.as_inner(), &other.as_inner()) | ||
} | ||
} | ||
|
||
impl PartialOrd for $name { | ||
#[inline] | ||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
Some(Ord::cmp(self, other)) | ||
} | ||
} | ||
|
||
impl Hash for $name { | ||
// Required method | ||
fn hash<H: Hasher>(&self, state: &mut H) { | ||
Hash::hash(&self.as_inner(), state); | ||
} | ||
} | ||
|
||
impl fmt::Debug for $name { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
<$int as fmt::Debug>::fmt(&self.as_inner(), f) | ||
} | ||
} | ||
)+}; | ||
} | ||
|
||
define_valid_range_type! { | ||
pub struct Nanoseconds(u32 as u32 in 0..=999_999_999); | ||
} | ||
|
||
impl Nanoseconds { | ||
// SAFETY: 0 is within the valid range | ||
pub const ZERO: Self = unsafe { Nanoseconds::new_unchecked(0) }; | ||
} | ||
|
||
impl Default for Nanoseconds { | ||
#[inline] | ||
fn default() -> Self { | ||
Self::ZERO | ||
} | ||
} | ||
|
||
define_valid_range_type! { | ||
pub struct NonZeroU8Inner(u8 as u8 in 1..=0xff); | ||
pub struct NonZeroU16Inner(u16 as u16 in 1..=0xff_ff); | ||
pub struct NonZeroU32Inner(u32 as u32 in 1..=0xffff_ffff); | ||
pub struct NonZeroU64Inner(u64 as u64 in 1..=0xffffffff_ffffffff); | ||
pub struct NonZeroU128Inner(u128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); | ||
|
||
pub struct NonZeroI8Inner(i8 as u8 in 1..=0xff); | ||
pub struct NonZeroI16Inner(i16 as u16 in 1..=0xff_ff); | ||
pub struct NonZeroI32Inner(i32 as u32 in 1..=0xffff_ffff); | ||
pub struct NonZeroI64Inner(i64 as u64 in 1..=0xffffffff_ffffffff); | ||
pub struct NonZeroI128Inner(i128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); | ||
} | ||
|
||
#[cfg(target_pointer_width = "16")] | ||
define_valid_range_type! { | ||
pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff); | ||
pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff); | ||
pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff); | ||
} | ||
#[cfg(target_pointer_width = "32")] | ||
define_valid_range_type! { | ||
pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff); | ||
pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff); | ||
pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff); | ||
} | ||
#[cfg(target_pointer_width = "64")] | ||
define_valid_range_type! { | ||
pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff_ffff_ffff); | ||
pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff_ffff_ffff); | ||
pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff_ffff_ffff); | ||
} | ||
|
||
define_valid_range_type! { | ||
pub struct U32NotAllOnes(u32 as u32 in 0..=0xffff_fffe); | ||
pub struct I32NotAllOnes(i32 as u32 in 0..=0xffff_fffe); | ||
|
||
pub struct U64NotAllOnes(u64 as u64 in 0..=0xffff_ffff_ffff_fffe); | ||
pub struct I64NotAllOnes(i64 as u64 in 0..=0xffff_ffff_ffff_fffe); | ||
} | ||
|
||
pub trait NotAllOnesHelper { | ||
type Type; | ||
} | ||
pub type NotAllOnes<T> = <T as NotAllOnesHelper>::Type; | ||
impl NotAllOnesHelper for u32 { | ||
type Type = U32NotAllOnes; | ||
} | ||
impl NotAllOnesHelper for i32 { | ||
type Type = I32NotAllOnes; | ||
} | ||
impl NotAllOnesHelper for u64 { | ||
type Type = U64NotAllOnes; | ||
} | ||
impl NotAllOnesHelper for i64 { | ||
type Type = I64NotAllOnes; | ||
} |
4 changes: 2 additions & 2 deletions
4
src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should only be changed here if we also change the docs to guarantee that a pattern type is ABI-compatible with its inner type. And then the ABI compatibility tests should be extended to cover that (in the rustc test suite and the Miri test suite).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, considering it's the only way to guarantee that
NonZeroU8
is still compatible withu8
, I think the answer is yes, and that we already have at least tests for that. I'll add additional tests for bare pattern typesThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make a note on the tracking issue so that before stabilization, we decide whether this is true for all pattern types or only for some.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We unconditionally allow transmuting from pattern types to their base type. For any pattern. I don't see a design that could work differently
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"A can be transmuted to B" does not imply "A and B are ABI-compatible". For instance,
i32
andu32
are not ABI-compatible. The code you changed here is about Miri ensuring ABI compatibility of caller and callee.