diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3c576316f6230..39cda360afb14 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2571,6 +2571,9 @@ pub enum TyPatKind { /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`). Range(Option>, Option>, Spanned), + /// A `!null` pattern for raw pointers. + NotNull, + Or(ThinVec>), /// Placeholder for a pattern that wasn't syntactically well formed in some way. diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 37fcc0d2167b2..1f29abb8c682c 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -1208,7 +1208,7 @@ macro_rules! common_visitor_and_walkers { try_visit!(visit_span(vis, span)); } TyPatKind::Or(variants) => walk_list!(vis, visit_ty_pat, variants), - TyPatKind::Err(_) => {} + TyPatKind::NotNull | TyPatKind::Err(_) => {} } visit_span(vis, span) } diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index e444062104813..e7c7471611ee3 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -143,7 +143,9 @@ impl<'a, 'hir> LoweringContext<'a, '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 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) }), ), + 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)), diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index aff98c63bcb41..f3bb40d9dade8 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1226,6 +1226,7 @@ impl<'a> State<'a> { 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 { diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index b61af0b0aaa12..9768646e5f919 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -28,15 +28,21 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P 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()?; diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 2aee0b2e97424..2dba3a0fa6f1e 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -133,6 +133,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(), diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 9d73f200afe2b..0c98c4f33f80d 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -342,6 +342,14 @@ impl<'tcx> CValue<'tcx> { 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 diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 18581f854b664..dc50041203b3e 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -227,6 +227,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()); diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 7a73d70fc85dc..179d6d8d5cfbd 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -459,6 +459,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> 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, _)) => { diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index fc4d13af8c42a..ad2a1a42b9e09 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -1249,9 +1249,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // 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 diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 0f6f81d7964c4..501593929f858 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1823,6 +1823,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>]), diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 1bb8f7ad8945a..77208e7bc40fc 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -726,7 +726,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() } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 529d3578985ae..2c97e23077714 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -106,6 +106,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 diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 8356a0af63c35..68085f095a945 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -248,6 +248,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 => { @@ -413,6 +425,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 }; diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index c75fef9f716d6..6660ed4181084 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -206,12 +206,8 @@ pub(crate) fn orphan_check_impl( (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(..) diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index c1c828392126f..b283ea95e4365 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1279,6 +1279,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 { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 20d165897e2a3..181b440dfdde9 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2579,6 +2579,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .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| { diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 960ec7f66ab15..09da0e2ec6511 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -340,6 +340,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { 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) diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 3a525021f6fc9..30c94a3cfed38 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1874,6 +1874,10 @@ impl<'a> State<'a> { 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; diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index e41bc8f852e58..adbfac7cc7ea8 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -905,6 +905,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)) } @@ -1066,7 +1067,9 @@ fn get_nullable_type_from_pat<'tcx>( pat: ty::Pattern<'tcx>, ) -> Option> { 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..] { diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index c498e6b3c83da..34b5aafc6aec7 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -154,6 +154,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 diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 809717513c795..a99049ff92dcc 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -808,11 +808,21 @@ where | ty::FnDef(..) | ty::CoroutineWitness(..) | ty::Foreign(..) - | ty::Pat(_, _) | ty::Dynamic(_, _, ty::Dyn) => { bug!("TyAndLayout::field({:?}): not applicable", this) } + // May contain wide pointers + ty::Pat(base, pat) => match *pat { + ty::PatternKind::NotNull => { + assert_eq!(i, 0); + TyMaybeWithLayout::Ty(base) + } + ty::PatternKind::Range { .. } | ty::PatternKind::Or(_) => { + bug!("TyAndLayout::field({this:?}): only applicable to !null patterns") + } + }, + ty::UnsafeBinder(bound_ty) => { let ty = tcx.instantiate_bound_regions_with_erased(bound_ty.into()); field_ty_or_layout(TyAndLayout { ty, ..this }, cx, i) diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs index 5af9b17dd7777..335e5c0647435 100644 --- a/compiler/rustc_middle/src/ty/pattern.rs +++ b/compiler/rustc_middle/src/ty/pattern.rs @@ -30,6 +30,7 @@ impl<'tcx> Flags for Pattern<'tcx> { } flags } + ty::PatternKind::NotNull => rustc_type_ir::TypeFlags::empty(), } } @@ -45,6 +46,7 @@ impl<'tcx> Flags for Pattern<'tcx> { } idx } + ty::PatternKind::NotNull => rustc_type_ir::INNERMOST, } } } @@ -91,6 +93,7 @@ impl<'tcx> IrPrint> for TyCtxt<'tcx> { write!(f, "..={end}") } + PatternKind::NotNull => write!(f, "!null"), PatternKind::Or(patterns) => { write!(f, "(")?; let mut first = true; diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index dc1d60f3d43c1..a4d7423818ea2 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -59,6 +59,7 @@ impl<'tcx> Relate> for ty::Pattern<'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); @@ -67,7 +68,10 @@ impl<'tcx> Relate> for ty::Pattern<'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), } } } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index cbb9bbfd12f9f..05847139ddf91 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -707,6 +707,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { }; 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 if self.tcx.is_lang_item(adt_def.did(), LangItem::DynMetadata) { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 131064f9832c0..7d068a6a0ddc3 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1070,6 +1070,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, _), diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index b91548196a305..1d7ff549f1b09 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -139,6 +139,7 @@ pub enum TokenType { SymNomem, SymNoreturn, SymNostack, + SymNull, SymOptions, SymOut, SymPreservesFlags, @@ -273,6 +274,7 @@ impl TokenType { SymNomem, SymNoreturn, SymNostack, + SymNull, SymOptions, SymOut, SymPreservesFlags, @@ -348,6 +350,7 @@ impl TokenType { 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) }; diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 08629090bb109..41f1fa6927124 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -976,7 +976,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc self.visit_ty_pat(pat) } } - TyPatKind::Err(_) => {} + TyPatKind::NotNull | TyPatKind::Err(_) => {} } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 09f01d8704e2a..90a20bca4a504 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1509,6 +1509,7 @@ symbols! { not, notable_trait, note, + null, object_safe_for_dispatch, of, off, diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index fe0f8e6113ef7..86221decf3df0 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -263,6 +263,9 @@ impl<'tcx> SymbolMangler<'tcx> { Ty::new_array_with_const_len(self.tcx, self.tcx.types.unit, ct).print(self)?; } } + ty::PatternKind::NotNull => { + self.tcx.types.unit.print(self)?; + } ty::PatternKind::Or(patterns) => { for pat in patterns { self.print_pat(pat)?; diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 545531f927a95..1cab0e8dc01d6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -115,6 +115,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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) @@ -628,6 +633,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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>, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index c9930c69b32cc..6e1b3d2b58ca8 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2002,6 +2002,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | PointerLikeCandidate | BikeshedGuaranteedNoDropCandidate => false, // Non-global param candidates have already been handled, global // where-bounds get ignored. diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index d4e6a23f0eb6b..bc33aa408950b 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -704,6 +704,7 @@ impl<'a, 'tcx> WfPredicates<'a, '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) diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 163e2b3088374..cfeb35b050cb3 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -213,9 +213,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)))?; @@ -263,6 +261,31 @@ 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); + // Make wide pointer pattern types contain only a single field + // of the wide pointer type itself. + layout.fields = FieldsShape::Arbitrary { + offsets: [Size::ZERO].into_iter().collect(), + memory_index: [0].into_iter().collect(), + } + } 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 { @@ -279,7 +302,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") } }) @@ -344,6 +367,7 @@ 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"), }, } diff --git a/compiler/rustc_type_ir/src/pattern.rs b/compiler/rustc_type_ir/src/pattern.rs index 7e56565917c67..8237254f8c44d 100644 --- a/compiler/rustc_type_ir/src/pattern.rs +++ b/compiler/rustc_type_ir/src/pattern.rs @@ -14,4 +14,5 @@ use crate::Interner; pub enum PatternKind { Range { start: I::Const, end: I::Const }, Or(I::PatList), + NotNull, } diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index 737550eb73e99..d1d707b896f0f 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -178,5 +178,6 @@ fn push_ty_pat(stack: &mut TypeWalkerStack, pat: I::Pat) { push_ty_pat::(stack, pat) } } + ty::PatternKind::NotNull => {} } } diff --git a/compiler/stable_mir/src/unstable/convert/stable/ty.rs b/compiler/stable_mir/src/unstable/convert/stable/ty.rs index 366f7ea01d44e..8b564907a24da 100644 --- a/compiler/stable_mir/src/unstable/convert/stable/ty.rs +++ b/compiler/stable_mir/src/unstable/convert/stable/ty.rs @@ -497,6 +497,7 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> { include_end: true, }, ty::PatternKind::Or(_) => todo!(), + ty::PatternKind::NotNull => todo!(), } } } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 2f701171505c7..adc0883dfeff8 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -115,6 +115,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)] @@ -168,6 +169,7 @@ #![feature(never_type)] #![feature(no_core)] #![feature(optimize_attribute)] +#![feature(pattern_types)] #![feature(prelude_import)] #![feature(repr_simd)] #![feature(rustc_allow_const_fn_unstable)] diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs index 91d015b1bc53f..c818b9f77cd6f 100644 --- a/library/core/src/pat.rs +++ b/library/core/src/pat.rs @@ -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..); @@ -74,3 +77,16 @@ impl const RangePattern for char { } } } + +impl CoerceUnsized for pattern_type!(*const T is !null) where + T: Unsize +{ +} + +impl, U> DispatchFromDyn for pattern_type!(T is !null) {} + +impl Unpin for pattern_type!(*const T is !null) {} + +unsafe impl Freeze for pattern_type!(*const T is !null) {} + +unsafe impl Freeze for pattern_type!(*mut T is !null) {} diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 0ca494f16e31d..743a30b7a5d33 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1122,7 +1122,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty_pat(variant); } }, - TyPatKind::Err(_) => {}, + TyPatKind::NotNull | TyPatKind::Err(_) => {}, } } diff --git a/src/tools/miri/tests/pass/pattern-types.rs b/src/tools/miri/tests/pass/pattern-types.rs new file mode 100644 index 0000000000000..90fe0de546915 --- /dev/null +++ b/src/tools/miri/tests/pass/pattern-types.rs @@ -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 { + pointer: std::pat::pattern_type!(*const T is !null), +} + +trait Trait {} +impl Trait for () {} + +fn main() { + unsafe { + let _: NonNull = NonNull { pointer: transmute(&mut () as *mut dyn Trait) }; + } +} diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 9ee10d8627030..b22a8f31debed 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1100,7 +1100,7 @@ impl Rewrite for ast::TyPat { } Ok(s) } - ast::TyPatKind::Err(_) => Err(RewriteError::Unknown), + ast::TyPatKind::NotNull | ast::TyPatKind::Err(_) => Err(RewriteError::Unknown), } } } diff --git a/tests/ui/type/pattern_types/bad_pat.rs b/tests/ui/type/pattern_types/bad_pat.rs index 549b0d11dd187..7e2b4cb996f27 100644 --- a/tests/ui/type/pattern_types/bad_pat.rs +++ b/tests/ui/type/pattern_types/bad_pat.rs @@ -10,4 +10,15 @@ type Positive2 = pattern_type!(i32 is 0..=); 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() {} diff --git a/tests/ui/type/pattern_types/bad_pat.stderr b/tests/ui/type/pattern_types/bad_pat.stderr index d2a5a20bf89b6..e72279542280c 100644 --- a/tests/ui/type/pattern_types/bad_pat.stderr +++ b/tests/ui/type/pattern_types/bad_pat.stderr @@ -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`. diff --git a/tests/ui/type/pattern_types/non_null.rs b/tests/ui/type/pattern_types/non_null.rs new file mode 100644 index 0000000000000..7e86b8b684d17 --- /dev/null +++ b/tests/ui/type/pattern_types/non_null.rs @@ -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 = pattern_type!(*const T is !null); //~ ERROR layout_of + +#[rustc_layout(debug)] +type Test = Option>; //~ ERROR layout_of + +#[rustc_layout(debug)] +type Wide = pattern_type!(*const [u8] is !null); //~ ERROR layout_of + +const _: () = assert!(size_of::>() == size_of::>>()); + +fn main() {} diff --git a/tests/ui/type/pattern_types/non_null.stderr b/tests/ui/type/pattern_types/non_null.stderr new file mode 100644 index 0000000000000..ad61e9a591473 --- /dev/null +++ b/tests/ui/type/pattern_types/non_null.stderr @@ -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 = 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>; + | ^^^^^^^^^ + +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 + diff --git a/tests/ui/type/pattern_types/unsize.rs b/tests/ui/type/pattern_types/unsize.rs new file mode 100644 index 0000000000000..1020dd8c21544 --- /dev/null +++ b/tests/ui/type/pattern_types/unsize.rs @@ -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 = pattern_type!(*const T is !null); + +trait Trait {} +impl Trait for u32 {} +impl Trait for i32 {} + +fn main() { + let x: NonNull = unsafe { std::mem::transmute(std::ptr::dangling::()) }; + let x: NonNull = x; +}