Rollup merge of #142339 - oli-obk:not-null-pattern-types, r=BoxyUwU

Add NonNull pattern types

These are the final piece missing for

* https://github.com/rust-lang/rust/pull/136006

We cannot use the previous scheme of using an integer range for raw pointers, as we're not just changing the layout of raw pointers anymore, but also the type representation. And we can't represent "any provenance or NonZero<usize>" natively as patterns. So I created a new `!null` pattern. Since this is all unstable representation stuff for replacing rustc_layout_scalar_range_start with pattern types, the divergence from normal patterns is fine, especially since T-lang seems interested in exploring general negation patterns

r? `@BoxyUwU`
This commit is contained in:
Matthias Krüger
2025-10-22 07:12:09 +02:00
committed by GitHub
48 changed files with 548 additions and 33 deletions
+3
View File
@@ -2579,6 +2579,9 @@ pub enum TyPatKind {
/// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`).
Range(Option<Box<AnonConst>>, Option<Box<AnonConst>>, Spanned<RangeEnd>),
/// A `!null` pattern for raw pointers.
NotNull,
Or(ThinVec<TyPat>),
/// Placeholder for a pattern that wasn't syntactically well formed in some way.
+4 -1
View File
@@ -143,7 +143,9 @@ fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> {
}
// return inner to be processed in next loop
PatKind::Paren(inner) => pattern = inner,
PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span),
PatKind::MacCall(_) => {
panic!("{pattern:#?} shouldn't exist here")
}
PatKind::Err(guar) => break hir::PatKind::Err(*guar),
}
};
@@ -460,6 +462,7 @@ fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'
)
}),
),
TyPatKind::NotNull => hir::TyPatKind::NotNull,
TyPatKind::Or(variants) => {
hir::TyPatKind::Or(self.arena.alloc_from_iter(
variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)),
@@ -1232,6 +1232,7 @@ pub fn print_ty_pat(&mut self, pat: &ast::TyPat) {
self.print_expr_anon_const(end, &[]);
}
}
rustc_ast::TyPatKind::NotNull => self.word("!null"),
rustc_ast::TyPatKind::Or(variants) => {
let mut first = true;
for pat in variants {
@@ -30,15 +30,21 @@ fn parse_pat_ty<'a>(
let ty = parser.parse_ty()?;
parser.expect_keyword(exp!(Is))?;
let pat = pat_to_ty_pat(
cx,
parser.parse_pat_no_top_guard(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe,
)?,
);
let start = parser.token.span;
let pat = if parser.eat(exp!(Bang)) {
parser.expect_keyword(exp!(Null))?;
ty_pat(TyPatKind::NotNull, start.to(parser.token.span))
} else {
pat_to_ty_pat(
cx,
parser.parse_pat_no_top_guard(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe,
)?,
)
};
if parser.token != token::Eof {
parser.unexpected()?;
@@ -131,6 +131,11 @@ pub(crate) fn coerce_unsized_into<'tcx>(
dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout()));
};
match (&src_ty.kind(), &dst_ty.kind()) {
(ty::Pat(a, _), ty::Pat(b, _)) => {
let src = src.cast_pat_ty_to_base(fx.layout_of(*a));
let dst = dst.place_transmute_type(fx, *b);
return coerce_unsized_into(fx, src, dst);
}
(&ty::Ref(..), &ty::Ref(..))
| (&ty::Ref(..), &ty::RawPtr(..))
| (&ty::RawPtr(..), &ty::RawPtr(..)) => coerce_ptr(),
@@ -342,6 +342,14 @@ pub(crate) fn cast_pointer_to(self, layout: TyAndLayout<'tcx>) -> Self {
assert_eq!(self.layout().backend_repr, layout.backend_repr);
CValue(self.0, layout)
}
pub(crate) fn cast_pat_ty_to_base(self, layout: TyAndLayout<'tcx>) -> Self {
let ty::Pat(base, _) = *self.layout().ty.kind() else {
panic!("not a pattern type: {:#?}", self.layout())
};
assert_eq!(layout.ty, base);
CValue(self.0, layout)
}
}
/// A place where you can write a value to or read a value from
+1
View File
@@ -228,6 +228,7 @@ pub(crate) fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
) -> (Bx::Value, Bx::Value) {
debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty);
match (src_ty.kind(), dst_ty.kind()) {
(&ty::Pat(a, _), &ty::Pat(b, _)) => unsize_ptr(bx, src, a, b, old_info),
(&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _))
| (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => {
assert_eq!(bx.cx().type_is_sized(a), old_info.is_none());
@@ -466,6 +466,12 @@ pub fn unsize_into(
) -> InterpResult<'tcx> {
trace!("Unsizing {:?} of type {} into {}", *src, src.layout.ty, cast_ty.ty);
match (src.layout.ty.kind(), cast_ty.ty.kind()) {
(&ty::Pat(_, s_pat), &ty::Pat(cast_ty, c_pat)) if s_pat == c_pat => {
let src = self.project_field(src, FieldIdx::ZERO)?;
let dest = self.project_field(dest, FieldIdx::ZERO)?;
let cast_ty = self.layout_of(cast_ty)?;
self.unsize_into(&src, cast_ty, &dest)
}
(&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(c, _))
| (&ty::RawPtr(s, _), &ty::RawPtr(c, _)) => self.unsize_into_ptr(src, dest, s, c),
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
@@ -1261,9 +1261,10 @@ fn visit_value(&mut self, val: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'t
// When you extend this match, make sure to also add tests to
// tests/ui/type/pattern_types/validity.rs((
match **pat {
// Range patterns are precisely reflected into `valid_range` and thus
// Range and non-null patterns are precisely reflected into `valid_range` and thus
// handled fully by `visit_scalar` (called below).
ty::PatternKind::Range { .. } => {},
ty::PatternKind::NotNull => {},
// FIXME(pattern_types): check that the value is covered by one of the variants.
// For now, we rely on layout computation setting the scalar's `valid_range` to
+3
View File
@@ -1854,6 +1854,9 @@ pub enum TyPatKind<'hir> {
/// A range pattern (e.g., `1..=2` or `1..2`).
Range(&'hir ConstArg<'hir>, &'hir ConstArg<'hir>),
/// A pattern that excludes null pointers
NotNull,
/// A list of patterns where only one needs to be satisfied
Or(&'hir [TyPat<'hir>]),
+1 -1
View File
@@ -725,7 +725,7 @@ pub fn walk_ty_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v TyPat<'v>)
try_visit!(visitor.visit_const_arg_unambig(upper_bound));
}
TyPatKind::Or(patterns) => walk_list!(visitor, visit_pattern_type_pattern, patterns),
TyPatKind::Err(_) => (),
TyPatKind::NotNull | TyPatKind::Err(_) => (),
}
V::Result::output()
}
+2
View File
@@ -103,6 +103,8 @@ hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applica
hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
hir_analysis_coerce_same_pat_kind = only pattern types with the same pattern can be coerced between each other
hir_analysis_coerce_unsized_field_validity = for `{$ty}` to have a valid implementation of `{$trait_name}`, it must be possible to coerce the field of type `{$field_ty}`
.label = `{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized
@@ -243,6 +243,18 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
// in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent)
// even if they do not carry that attribute.
match (source.kind(), target.kind()) {
(&ty::Pat(_, pat_a), &ty::Pat(_, pat_b)) => {
if pat_a != pat_b {
return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {
span,
trait_name,
pat_a: pat_a.to_string(),
pat_b: pat_b.to_string(),
}));
}
Ok(())
}
(&ty::Ref(r_a, _, mutbl_a), ty::Ref(r_b, _, mutbl_b))
if r_a == *r_b && mutbl_a == *mutbl_b =>
{
@@ -408,6 +420,18 @@ pub(crate) fn coerce_unsized_info<'tcx>(
(mt_a.ty, mt_b.ty, unsize_trait, None, span)
};
let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) {
(&ty::Pat(ty_a, pat_a), &ty::Pat(ty_b, pat_b)) => {
if pat_a != pat_b {
return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {
span,
trait_name,
pat_a: pat_a.to_string(),
pat_b: pat_b.to_string(),
}));
}
(ty_a, ty_b, coerce_unsized_trait, None, span)
}
(&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
infcx.sub_regions(SubregionOrigin::RelateObjectBound(span), r_b, r_a);
let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
@@ -206,12 +206,8 @@ enum NonlocalImpl {
(LocalImpl::Disallow { problematic_kind }, NonlocalImpl::DisallowOther)
}
ty::Pat(..) => (
LocalImpl::Disallow { problematic_kind: "pattern type" },
NonlocalImpl::DisallowOther,
),
ty::Bool
| ty::Pat(..)
| ty::Char
| ty::Int(..)
| ty::Uint(..)
+10
View File
@@ -1258,6 +1258,16 @@ pub(crate) struct CoerceUnsizedNonStruct {
pub trait_name: &'static str,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_coerce_same_pat_kind)]
pub(crate) struct CoerceSamePatKind {
#[primary_span]
pub span: Span,
pub trait_name: &'static str,
pub pat_a: String,
pub pat_b: String,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_coerce_unsized_may, code = E0377)]
pub(crate) struct CoerceSameStruct {
@@ -2611,6 +2611,7 @@ fn lower_pat_ty_pat(
.span_delayed_bug(ty_span, "invalid base type for range pattern")),
}
}
hir::TyPatKind::NotNull => Ok(ty::PatternKind::NotNull),
hir::TyPatKind::Or(patterns) => {
self.tcx()
.mk_patterns_from_iter(patterns.iter().map(|pat| {
@@ -340,6 +340,7 @@ fn add_constraints_from_pat(
self.add_constraints_from_const(current, start, variance);
self.add_constraints_from_const(current, end, variance);
}
ty::PatternKind::NotNull => {}
ty::PatternKind::Or(patterns) => {
for pat in patterns {
self.add_constraints_from_pat(current, variance, pat)
+4
View File
@@ -1888,6 +1888,10 @@ fn print_ty_pat(&mut self, pat: &hir::TyPat<'_>) {
self.word("..=");
self.print_const_arg(end);
}
TyPatKind::NotNull => {
self.word_space("not");
self.word("null");
}
TyPatKind::Or(patterns) => {
self.popen();
let mut first = true;
+4 -1
View File
@@ -758,6 +758,7 @@ fn pat_ty_is_known_nonnull<'tcx>(
// to ensure we aren't wrapping over zero.
start > 0 && end >= start
}
ty::PatternKind::NotNull => true,
ty::PatternKind::Or(patterns) => {
patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat))
}
@@ -918,7 +919,9 @@ fn get_nullable_type_from_pat<'tcx>(
pat: ty::Pattern<'tcx>,
) -> Option<Ty<'tcx>> {
match *pat {
ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, base),
ty::PatternKind::NotNull | ty::PatternKind::Range { .. } => {
get_nullable_type(tcx, typing_env, base)
}
ty::PatternKind::Or(patterns) => {
let first = get_nullable_type_from_pat(tcx, typing_env, base, patterns[0])?;
for &pat in &patterns[1..] {
@@ -160,6 +160,9 @@ pub enum SelectionCandidate<'tcx> {
/// types generated for a fn pointer type (e.g., `fn(int) -> int`)
FnPointerCandidate,
/// Builtin impl of the `PointerLike` trait.
PointerLikeCandidate,
TraitAliasCandidate,
/// Matching `dyn Trait` with a supertrait of `Trait`. The index is the
+5 -1
View File
@@ -839,11 +839,15 @@ fn field_ty_or_layout<'tcx>(
| ty::FnDef(..)
| ty::CoroutineWitness(..)
| ty::Foreign(..)
| ty::Pat(_, _)
| ty::Dynamic(_, _) => {
bug!("TyAndLayout::field({:?}): not applicable", this)
}
ty::Pat(base, _) => {
assert_eq!(i, 0);
TyMaybeWithLayout::Ty(base)
}
ty::UnsafeBinder(bound_ty) => {
let ty = tcx.instantiate_bound_regions_with_erased(bound_ty.into());
field_ty_or_layout(TyAndLayout { ty, ..this }, cx, i)
+3
View File
@@ -30,6 +30,7 @@ fn flags(&self) -> rustc_type_ir::TypeFlags {
}
flags
}
ty::PatternKind::NotNull => rustc_type_ir::TypeFlags::empty(),
}
}
@@ -45,6 +46,7 @@ fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
}
idx
}
ty::PatternKind::NotNull => rustc_type_ir::INNERMOST,
}
}
}
@@ -91,6 +93,7 @@ fn print(t: &PatternKind<'tcx>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "..={end}")
}
PatternKind::NotNull => write!(f, "!null"),
PatternKind::Or(patterns) => {
write!(f, "(")?;
let mut first = true;
+5 -1
View File
@@ -35,6 +35,7 @@ fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
let end = relation.relate(end_a, end_b)?;
Ok(tcx.mk_pat(ty::PatternKind::Range { start, end }))
}
(ty::PatternKind::NotNull, ty::PatternKind::NotNull) => Ok(a),
(&ty::PatternKind::Or(a), &ty::PatternKind::Or(b)) => {
if a.len() != b.len() {
return Err(TypeError::Mismatch);
@@ -43,7 +44,10 @@ fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
let patterns = tcx.mk_patterns_from_iter(v)?;
Ok(tcx.mk_pat(ty::PatternKind::Or(patterns)))
}
(ty::PatternKind::Range { .. } | ty::PatternKind::Or(_), _) => Err(TypeError::Mismatch),
(
ty::PatternKind::NotNull | ty::PatternKind::Range { .. } | ty::PatternKind::Or(_),
_,
) => Err(TypeError::Mismatch),
}
}
}
@@ -695,6 +695,8 @@ fn visit_projection_elem(
};
check_equal(self, location, *f_ty);
}
// Debug info is allowed to project into pattern types
ty::Pat(base, _) => check_equal(self, location, *base),
ty::Adt(adt_def, args) => {
// see <https://github.com/rust-lang/rust/blob/7601adcc764d42c9f2984082b49948af652df986/compiler/rustc_middle/src/ty/layout.rs#L861-L864>
if self.tcx.is_lang_item(adt_def.did(), LangItem::DynMetadata) {
@@ -1145,6 +1145,7 @@ fn find_tails_for_unsizing<'tcx>(
debug_assert!(!target_ty.has_param(), "{target_ty} should be fully monomorphic");
match (source_ty.kind(), target_ty.kind()) {
(&ty::Pat(source, _), &ty::Pat(target, _)) => find_tails_for_unsizing(tcx, source, target),
(
&ty::Ref(_, source_pointee, _),
&ty::Ref(_, target_pointee, _) | &ty::RawPtr(target_pointee, _),
@@ -139,6 +139,7 @@ pub enum TokenType {
SymNomem,
SymNoreturn,
SymNostack,
SymNull,
SymOptions,
SymOut,
SymPreservesFlags,
@@ -273,6 +274,7 @@ fn from_u32(val: u32) -> TokenType {
SymNomem,
SymNoreturn,
SymNostack,
SymNull,
SymOptions,
SymOut,
SymPreservesFlags,
@@ -348,6 +350,7 @@ pub(super) fn is_keyword(&self) -> Option<Symbol> {
TokenType::SymNomem => Some(sym::nomem),
TokenType::SymNoreturn => Some(sym::noreturn),
TokenType::SymNostack => Some(sym::nostack),
TokenType::SymNull => Some(sym::null),
TokenType::SymOptions => Some(sym::options),
TokenType::SymOut => Some(sym::out),
TokenType::SymPreservesFlags => Some(sym::preserves_flags),
@@ -562,6 +565,7 @@ macro_rules! exp {
(Nomem) => { exp!(@sym, nomem, SymNomem) };
(Noreturn) => { exp!(@sym, noreturn, SymNoreturn) };
(Nostack) => { exp!(@sym, nostack, SymNostack) };
(Null) => { exp!(@sym, null, SymNull) };
(Options) => { exp!(@sym, options, SymOptions) };
(Out) => { exp!(@sym, out, SymOut) };
(PreservesFlags) => { exp!(@sym, preserves_flags, SymPreservesFlags) };
@@ -485,6 +485,7 @@ fn stable<'cx>(
end: Some(end.stable(tables, cx)),
include_end: true,
},
ty::PatternKind::NotNull => todo!(),
ty::PatternKind::Or(_) => todo!(),
}
}
+1 -1
View File
@@ -968,7 +968,7 @@ fn visit_ty_pat(&mut self, t: &'ast TyPat) -> Self::Result {
self.visit_ty_pat(pat)
}
}
TyPatKind::Err(_) => {}
TyPatKind::NotNull | TyPatKind::Err(_) => {}
}
}
+1
View File
@@ -1554,6 +1554,7 @@
not,
notable_trait,
note,
null,
nvptx_target_feature,
object_safe_for_dispatch,
of,
+3
View File
@@ -266,6 +266,9 @@ fn print_pat(&mut self, pat: ty::Pattern<'tcx>) -> Result<(), std::fmt::Error> {
self.print_const(start)?;
self.print_const(end)?;
}
ty::PatternKind::NotNull => {
self.tcx.types.unit.print(self)?;
}
ty::PatternKind::Or(patterns) => {
self.push("O");
for pat in patterns {
@@ -115,6 +115,11 @@ pub(super) fn confirm_candidate(
ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
PointerLikeCandidate => {
let data = self.confirm_pointer_like_candidate(obligation);
ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
TraitAliasCandidate => {
let data = self.confirm_trait_alias_candidate(obligation);
ImplSource::Builtin(BuiltinImplSource::Misc, data)
@@ -631,6 +636,25 @@ fn confirm_fn_pointer_candidate(
Ok(nested)
}
fn confirm_pointer_like_candidate(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
) -> PredicateObligations<'tcx> {
debug!(?obligation, "confirm_pointer_like_candidate");
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
let ty::Pat(base, _) = *self_ty.kind() else { bug!() };
let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
self.collect_predicates_for_types(
obligation.param_env,
cause,
obligation.recursion_depth + 1,
placeholder_predicate.def_id(),
vec![base],
)
}
fn confirm_trait_alias_candidate(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
@@ -2036,6 +2036,7 @@ fn winnow_candidates(
| TraitUpcastingUnsizeCandidate(_)
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate
| PointerLikeCandidate
| BikeshedGuaranteedNoDropCandidate => false,
// Non-global param candidates have already been handled, global
// where-bounds get ignored.
@@ -705,6 +705,7 @@ fn add_wf_preds_for_pat_ty(&mut self, base_ty: Ty<'tcx>, pat: ty::Pattern<'tcx>)
check(start);
check(end);
}
ty::PatternKind::NotNull => {}
ty::PatternKind::Or(patterns) => {
for pat in patterns {
self.add_wf_preds_for_pat_ty(base_ty, pat)
+30 -4
View File
@@ -216,9 +216,7 @@ fn layout_of_uncached<'tcx>(
let mut layout = LayoutData::clone(&layout.0);
match *pat {
ty::PatternKind::Range { start, end } => {
if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
&mut layout.backend_repr
{
if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
scalar.valid_range_mut().start = extract_const_value(cx, ty, start)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
@@ -266,6 +264,25 @@ fn layout_of_uncached<'tcx>(
bug!("pattern type with range but not scalar layout: {ty:?}, {layout:?}")
}
}
ty::PatternKind::NotNull => {
if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
&mut layout.backend_repr
{
scalar.valid_range_mut().start = 1;
let niche = Niche {
offset: Size::ZERO,
value: scalar.primitive(),
valid_range: scalar.valid_range(cx),
};
layout.largest_niche = Some(niche);
} else {
bug!(
"pattern type with `!null` pattern but not scalar/pair layout: {ty:?}, {layout:?}"
)
}
}
ty::PatternKind::Or(variants) => match *variants[0] {
ty::PatternKind::Range { .. } => {
if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
@@ -282,7 +299,7 @@ fn layout_of_uncached<'tcx>(
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?,
)),
ty::PatternKind::Or(_) => {
ty::PatternKind::NotNull | ty::PatternKind::Or(_) => {
unreachable!("mixed or patterns are not allowed")
}
})
@@ -347,9 +364,18 @@ fn layout_of_uncached<'tcx>(
)
}
}
ty::PatternKind::NotNull => bug!("or patterns can't contain `!null` patterns"),
ty::PatternKind::Or(..) => bug!("patterns cannot have nested or patterns"),
},
}
// Pattern types contain their base as their sole field.
// This allows the rest of the compiler to process pattern types just like
// single field transparent Adts, and only the parts of the compiler that
// specifically care about pattern types will have to handle it.
layout.fields = FieldsShape::Arbitrary {
offsets: [Size::ZERO].into_iter().collect(),
memory_index: [0].into_iter().collect(),
};
tcx.mk_layout(layout)
}
+1
View File
@@ -14,6 +14,7 @@
pub enum PatternKind<I: Interner> {
Range { start: I::Const, end: I::Const },
Or(I::PatList),
NotNull,
}
impl<I: Interner> Eq for PatternKind<I> {}
+1
View File
@@ -178,5 +178,6 @@ fn push_ty_pat<I: Interner>(stack: &mut TypeWalkerStack<I>, pat: I::Pat) {
push_ty_pat::<I>(stack, pat)
}
}
ty::PatternKind::NotNull => {}
}
}
+2
View File
@@ -116,6 +116,7 @@
#![feature(link_cfg)]
#![feature(offset_of_enum)]
#![feature(panic_internals)]
#![feature(pattern_type_macro)]
#![feature(ptr_alignment_type)]
#![feature(ptr_metadata)]
#![feature(set_ptr_value)]
@@ -171,6 +172,7 @@
#![feature(never_type)]
#![feature(no_core)]
#![feature(optimize_attribute)]
#![feature(pattern_types)]
#![feature(prelude_import)]
#![feature(reborrow)]
#![feature(repr_simd)]
+16
View File
@@ -1,5 +1,8 @@
//! Helper module for exporting the `pattern_type` macro
use crate::marker::{Freeze, PointeeSized, Unsize};
use crate::ops::{CoerceUnsized, DispatchFromDyn};
/// Creates a pattern type.
/// ```ignore (cannot test this from within core yet)
/// type Positive = std::pat::pattern_type!(i32 is 1..);
@@ -73,3 +76,16 @@ fn sub_one(self) -> Self {
}
}
}
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) {}
impl<T: PointeeSized> Unpin for pattern_type!(*const T is !null) {}
unsafe impl<T: PointeeSized> Freeze for pattern_type!(*const T is !null) {}
unsafe impl<T: PointeeSized> Freeze for pattern_type!(*mut T is !null) {}
@@ -1123,7 +1123,7 @@ pub fn hash_ty_pat(&mut self, pat: &TyPat<'_>) {
self.hash_ty_pat(variant);
}
},
TyPatKind::Err(_) => {},
TyPatKind::NotNull | TyPatKind::Err(_) => {},
}
}
@@ -0,0 +1,18 @@
#![feature(pattern_types, pattern_type_macro, sized_hierarchy)]
#![allow(dead_code)]
use std::marker::PointeeSized;
use std::mem::transmute;
pub struct NonNull<T: PointeeSized> {
pointer: std::pat::pattern_type!(*const T is !null),
}
trait Trait {}
impl Trait for () {}
fn main() {
unsafe {
let _: NonNull<dyn Trait> = NonNull { pointer: transmute(&mut () as *mut dyn Trait) };
}
}
+1 -1
View File
@@ -1099,7 +1099,7 @@ fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteR
}
Ok(s)
}
ast::TyPatKind::Err(_) => Err(RewriteError::Unknown),
ast::TyPatKind::NotNull | ast::TyPatKind::Err(_) => Err(RewriteError::Unknown),
}
}
}
+11
View File
@@ -10,4 +10,15 @@
type Wild = pattern_type!(() is _);
//~^ ERROR: pattern not supported in pattern types
// FIXME: confusing diagnostic because `not` can be a binding
type NonNull = pattern_type!(*const () is not null);
//~^ ERROR: expected one of `@` or `|`, found `null`
//~| ERROR: pattern not supported in pattern types
type NonNull2 = pattern_type!(*const () is !nil);
//~^ ERROR: expected `null`, found `nil`
// FIXME: reject with a type mismatch
type Mismatch2 = pattern_type!(() is !null);
fn main() {}
+19 -1
View File
@@ -30,6 +30,24 @@ error: pattern not supported in pattern types
LL | type Wild = pattern_type!(() is _);
| ^
error: aborting due to 3 previous errors
error: pattern not supported in pattern types
--> $DIR/bad_pat.rs:14:43
|
LL | type NonNull = pattern_type!(*const () is not null);
| ^^^
error: expected one of `@` or `|`, found `null`
--> $DIR/bad_pat.rs:14:47
|
LL | type NonNull = pattern_type!(*const () is not null);
| ^^^^ expected one of `@` or `|`
error: expected `null`, found `nil`
--> $DIR/bad_pat.rs:18:45
|
LL | type NonNull2 = pattern_type!(*const () is !nil);
| ^^^ expected `null`
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0586`.
+21
View File
@@ -0,0 +1,21 @@
//! Show that pattern-types non-null is the same as libstd's
//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED"
//@ only-64bit
#![feature(pattern_type_macro, pattern_types, rustc_attrs)]
use std::pat::pattern_type;
#[rustc_layout(debug)]
type NonNull<T> = pattern_type!(*const T is !null); //~ ERROR layout_of
#[rustc_layout(debug)]
type Test = Option<NonNull<()>>; //~ ERROR layout_of
#[rustc_layout(debug)]
type Wide = pattern_type!(*const [u8] is !null); //~ ERROR layout_of
const _: () = assert!(size_of::<NonNull<()>>() == size_of::<Option<NonNull<()>>>());
fn main() {}
+218
View File
@@ -0,0 +1,218 @@
error: layout_of((*const T) is !null) = Layout {
size: Size(8 bytes),
align: AbiAlign {
abi: Align(8 bytes),
},
backend_repr: Scalar(
Initialized {
value: Pointer(
AddressSpace(
0,
),
),
valid_range: 1..=18446744073709551615,
},
),
fields: Arbitrary {
offsets: [
Size(0 bytes),
],
memory_index: [
0,
],
},
largest_niche: Some(
Niche {
offset: Size(0 bytes),
value: Pointer(
AddressSpace(
0,
),
),
valid_range: 1..=18446744073709551615,
},
),
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(8 bytes),
randomization_seed: $SEED,
}
--> $DIR/non_null.rs:11:1
|
LL | type NonNull<T> = pattern_type!(*const T is !null);
| ^^^^^^^^^^^^^^^
error: layout_of(Option<(*const ()) is !null>) = Layout {
size: Size(8 bytes),
align: AbiAlign {
abi: Align(8 bytes),
},
backend_repr: Scalar(
Initialized {
value: Pointer(
AddressSpace(
0,
),
),
valid_range: (..=0) | (1..),
},
),
fields: Arbitrary {
offsets: [
Size(0 bytes),
],
memory_index: [
0,
],
},
largest_niche: None,
uninhabited: false,
variants: Multiple {
tag: Initialized {
value: Pointer(
AddressSpace(
0,
),
),
valid_range: (..=0) | (1..),
},
tag_encoding: Niche {
untagged_variant: 1,
niche_variants: 0..=0,
niche_start: 0,
},
tag_field: 0,
variants: [
Layout {
size: Size(0 bytes),
align: AbiAlign {
abi: Align(1 bytes),
},
backend_repr: Memory {
sized: true,
},
fields: Arbitrary {
offsets: [],
memory_index: [],
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
randomization_seed: $SEED,
},
Layout {
size: Size(8 bytes),
align: AbiAlign {
abi: Align(8 bytes),
},
backend_repr: Scalar(
Initialized {
value: Pointer(
AddressSpace(
0,
),
),
valid_range: 1..=18446744073709551615,
},
),
fields: Arbitrary {
offsets: [
Size(0 bytes),
],
memory_index: [
0,
],
},
largest_niche: Some(
Niche {
offset: Size(0 bytes),
value: Pointer(
AddressSpace(
0,
),
),
valid_range: 1..=18446744073709551615,
},
),
uninhabited: false,
variants: Single {
index: 1,
},
max_repr_align: None,
unadjusted_abi_align: Align(8 bytes),
randomization_seed: $SEED,
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(8 bytes),
randomization_seed: $SEED,
}
--> $DIR/non_null.rs:14:1
|
LL | type Test = Option<NonNull<()>>;
| ^^^^^^^^^
error: layout_of((*const [u8]) is !null) = Layout {
size: Size(16 bytes),
align: AbiAlign {
abi: Align(8 bytes),
},
backend_repr: ScalarPair(
Initialized {
value: Pointer(
AddressSpace(
0,
),
),
valid_range: 1..=18446744073709551615,
},
Initialized {
value: Int(
I64,
false,
),
valid_range: 0..=18446744073709551615,
},
),
fields: Arbitrary {
offsets: [
Size(0 bytes),
],
memory_index: [
0,
],
},
largest_niche: Some(
Niche {
offset: Size(0 bytes),
value: Pointer(
AddressSpace(
0,
),
),
valid_range: 1..=18446744073709551615,
},
),
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(8 bytes),
randomization_seed: $SEED,
}
--> $DIR/non_null.rs:17:1
|
LL | type Wide = pattern_type!(*const [u8] is !null);
| ^^^^^^^^^
error: aborting due to 3 previous errors
+16 -2
View File
@@ -53,7 +53,14 @@ error: layout_of((i8) is (i8::MIN..=-1 | 1..)) = Layout {
valid_range: 1..=255,
},
),
fields: Primitive,
fields: Arbitrary {
offsets: [
Size(0 bytes),
],
memory_index: [
0,
],
},
largest_niche: Some(
Niche {
offset: Size(0 bytes),
@@ -91,7 +98,14 @@ error: layout_of((i8) is (i8::MIN..=-2 | 0..)) = Layout {
valid_range: 0..=254,
},
),
fields: Primitive,
fields: Arbitrary {
offsets: [
Size(0 bytes),
],
memory_index: [
0,
],
},
largest_niche: Some(
Niche {
offset: Size(0 bytes),
@@ -57,7 +57,14 @@ error: layout_of((u32) is 1..) = Layout {
valid_range: 1..=4294967295,
},
),
fields: Primitive,
fields: Arbitrary {
offsets: [
Size(0 bytes),
],
memory_index: [
0,
],
},
largest_niche: Some(
Niche {
offset: Size(0 bytes),
@@ -390,7 +397,14 @@ error: layout_of((i8) is -10..=10) = Layout {
valid_range: (..=10) | (246..),
},
),
fields: Primitive,
fields: Arbitrary {
offsets: [
Size(0 bytes),
],
memory_index: [
0,
],
},
largest_niche: Some(
Niche {
offset: Size(0 bytes),
@@ -428,7 +442,14 @@ error: layout_of((i8) is i8::MIN..=0) = Layout {
valid_range: (..=0) | (128..),
},
),
fields: Primitive,
fields: Arbitrary {
offsets: [
Size(0 bytes),
],
memory_index: [
0,
],
},
largest_niche: Some(
Niche {
offset: Size(0 bytes),
+18
View File
@@ -0,0 +1,18 @@
//! Show that pattern-types with pointer base types can be part of unsizing coercions
//@ check-pass
#![feature(pattern_type_macro, pattern_types)]
use std::pat::pattern_type;
type NonNull<T> = pattern_type!(*const T is !null);
trait Trait {}
impl Trait for u32 {}
impl Trait for i32 {}
fn main() {
let x: NonNull<u32> = unsafe { std::mem::transmute(std::ptr::dangling::<u32>()) };
let x: NonNull<dyn Trait> = x;
}