Add barest-bones deref patterns

Co-authored-by: Deadbeef <ent3rm4n@gmail.com>
This commit is contained in:
Nadrieril 2024-03-08 19:17:23 +01:00
parent a128516cf9
commit 120d3570aa
16 changed files with 139 additions and 17 deletions

View File

@ -413,7 +413,10 @@ fn visit_pat(&mut self, pattern: &'a ast::Pat) {
}
}
PatKind::Box(..) => {
gate!(&self, box_patterns, pattern.span, "box pattern syntax is experimental");
if !self.features.deref_patterns {
// Allow box patterns under `deref_patterns`.
gate!(&self, box_patterns, pattern.span, "box pattern syntax is experimental");
}
}
PatKind::Range(_, Some(_), Spanned { node: RangeEnd::Excluded, .. }) => {
gate!(
@ -607,13 +610,16 @@ macro_rules! gate_all_legacy_dont_use {
};
}
if !visitor.features.deref_patterns {
// Allow box patterns under `deref_patterns`.
gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental");
}
gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental");
// Despite being a new feature, `where T: Trait<Assoc(): Sized>`, which is RTN syntax now,
// used to be gated under associated_type_bounds, which are right above, so RTN needs to
// be too.
gate_all_legacy_dont_use!(return_type_notation, "return type notation is experimental");
gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental");
gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental");
gate_all_legacy_dont_use!(
exclusive_range_pattern,
"exclusive range pattern syntax is experimental"

View File

@ -436,6 +436,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
(unstable, deprecated_safe, "1.61.0", Some(94978)),
/// Allows having using `suggestion` in the `#[deprecated]` attribute.
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
/// Allows deref patterns.
(incomplete, deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121)),
/// Controls errors in trait implementations.
(unstable, do_not_recommend, "1.67.0", Some(51992)),
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.

View File

@ -18,8 +18,7 @@
use rustc_span::hygiene::DesugaringKind;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Span;
use rustc_span::{BytePos, DUMMY_SP};
use rustc_span::{BytePos, Span, DUMMY_SP};
use rustc_target::abi::FieldIdx;
use rustc_trait_selection::traits::{ObligationCause, Pattern};
use ty::VariantDef;
@ -211,6 +210,9 @@ fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<
PatKind::Tuple(elements, ddpos) => {
self.check_pat_tuple(pat.span, elements, ddpos, expected, pat_info)
}
PatKind::Box(inner) if self.tcx.features().deref_patterns => {
self.check_pat_deref(pat.span, inner, expected, pat_info)
}
PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
PatKind::Slice(before, slice, after) => {
@ -1975,6 +1977,28 @@ fn check_pat_box(
box_ty
}
fn check_pat_deref(
&self,
span: Span,
inner: &'tcx Pat<'tcx>,
expected: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>,
) -> Ty<'tcx> {
let tcx = self.tcx;
// FIXME(deref_patterns): use `DerefPure` for soundness
// FIXME(deref_patterns): use `DerefMut` when required
// <expected as Deref>::Target
let ty = Ty::new_projection(
tcx,
tcx.require_lang_item(hir::LangItem::DerefTarget, Some(span)),
[expected],
);
let ty = self.normalize(span, ty);
let ty = self.try_structurally_resolve_type(span, ty);
self.check_pat(inner, ty, pat_info);
expected
}
// Precondition: Pat is Ref(inner)
fn check_pat_ref(
&self,

View File

@ -647,6 +647,7 @@ fn walk_(&self, it: &mut impl FnMut(&Pat<'tcx>) -> bool) {
AscribeUserType { subpattern, .. }
| Binding { subpattern: Some(subpattern), .. }
| Deref { subpattern }
| DerefPattern { subpattern }
| InlineConstant { subpattern, .. } => subpattern.walk_(it),
Leaf { subpatterns } | Variant { subpatterns, .. } => {
subpatterns.iter().for_each(|field| field.pattern.walk_(it))
@ -762,6 +763,11 @@ pub enum PatKind<'tcx> {
subpattern: Box<Pat<'tcx>>,
},
/// Deref pattern, written `box P` for now.
DerefPattern {
subpattern: Box<Pat<'tcx>>,
},
/// One of the following:
/// * `&str` (represented as a valtree), which will be handled as a string pattern and thus
/// exhaustiveness checking will detect if you use the same string twice in different
@ -1172,6 +1178,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
}
write!(f, "{subpattern}")
}
PatKind::DerefPattern { ref subpattern } => {
write!(f, "k#deref {subpattern}")
}
PatKind::Constant { value } => write!(f, "{value}"),
PatKind::InlineConstant { def: _, ref subpattern } => {
write!(f, "{} (from inline const)", subpattern)

View File

@ -229,6 +229,7 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
match &pat.kind {
AscribeUserType { subpattern, ascription: _ }
| Deref { subpattern }
| DerefPattern { subpattern }
| Binding {
subpattern: Some(subpattern),
mutability: _,

View File

@ -879,6 +879,10 @@ pub(super) fn visit_primary_bindings(
self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f);
}
PatKind::DerefPattern { ref subpattern } => {
self.visit_primary_bindings(subpattern, UserTypeProjections::none(), f);
}
PatKind::AscribeUserType {
ref subpattern,
ascription: thir::Ascription { ref annotation, variance: _ },

View File

@ -256,6 +256,12 @@ pub(in crate::build) fn new(
subpairs.push(MatchPair::new(place_builder, subpattern, cx));
default_irrefutable()
}
PatKind::DerefPattern { .. } => {
// FIXME(deref_patterns)
// Treat it like a wildcard for now.
default_irrefutable()
}
};
MatchPair { place, test_case, subpairs, pattern }

View File

@ -250,6 +250,7 @@ fn visit_pat(&mut self, pat: &'a Pat<'tcx>) {
| PatKind::Variant { .. }
| PatKind::Leaf { .. }
| PatKind::Deref { .. }
| PatKind::DerefPattern { .. }
| PatKind::Range { .. }
| PatKind::Slice { .. }
| PatKind::Array { .. } => {
@ -310,7 +311,7 @@ fn visit_pat(&mut self, pat: &'a Pat<'tcx>) {
}
visit::walk_pat(self, pat);
}
PatKind::Deref { .. } => {
PatKind::Deref { .. } | PatKind::DerefPattern { .. } => {
let old_inside_adt = std::mem::replace(&mut self.inside_adt, false);
visit::walk_pat(self, pat);
self.inside_adt = old_inside_adt;

View File

@ -257,6 +257,9 @@ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tc
return self.lower_path(qpath, pat.hir_id, pat.span);
}
hir::PatKind::Box(subpattern) if self.tcx.features().deref_patterns => {
PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern) }
}
hir::PatKind::Ref(subpattern, _) | hir::PatKind::Box(subpattern) => {
PatKind::Deref { subpattern: self.lower_pattern(subpattern) }
}

View File

@ -688,6 +688,12 @@ fn print_pat_kind(&mut self, pat_kind: &PatKind<'tcx>, depth_lvl: usize) {
self.print_pat(subpattern, depth_lvl + 2);
print_indented!(self, "}", depth_lvl + 1);
}
PatKind::DerefPattern { subpattern } => {
print_indented!(self, "DerefPattern { ", depth_lvl + 1);
print_indented!(self, "subpattern:", depth_lvl + 2);
self.print_pat(subpattern, depth_lvl + 2);
print_indented!(self, "}", depth_lvl + 1);
}
PatKind::Constant { value } => {
print_indented!(self, "Constant {", depth_lvl + 1);
print_indented!(self, format!("value: {:?}", value), depth_lvl + 2);

View File

@ -462,6 +462,12 @@ pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> {
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty),
};
}
PatKind::DerefPattern { .. } => {
// FIXME(deref_patterns): At least detect that `box _` is irrefutable.
fields = vec![];
arity = 0;
ctor = Opaque(OpaqueId::new());
}
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
match ty.kind() {
ty::Tuple(fs) => {

View File

@ -674,6 +674,7 @@
deref_method,
deref_mut,
deref_mut_method,
deref_patterns,
deref_target,
derive,
derive_const,

View File

@ -1,15 +1,3 @@
warning: trait aliases are experimental
--> $DIR/cfg-false-feature.rs:12:1
|
LL | trait A = Clone;
| ^^^^^^^^^^^^^^^^
|
= note: see issue #41517 <https://github.com/rust-lang/rust/issues/41517> for more information
= help: add `#![feature(trait_alias)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= warning: unstable syntax can change at any point in the future, causing a hard error!
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
warning: box pattern syntax is experimental
--> $DIR/cfg-false-feature.rs:16:9
|
@ -22,5 +10,17 @@ LL | let box _ = Box::new(0);
= warning: unstable syntax can change at any point in the future, causing a hard error!
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
warning: trait aliases are experimental
--> $DIR/cfg-false-feature.rs:12:1
|
LL | trait A = Clone;
| ^^^^^^^^^^^^^^^^
|
= note: see issue #41517 <https://github.com/rust-lang/rust/issues/41517> for more information
= help: add `#![feature(trait_alias)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= warning: unstable syntax can change at any point in the future, causing a hard error!
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
warning: 2 warnings emitted

View File

@ -0,0 +1,9 @@
fn main() {
// We reuse the `box` syntax so this doesn't actually test the feature gate but eh.
let box x = Box::new('c'); //~ ERROR box pattern syntax is experimental
println!("x: {}", x);
// `box` syntax is allowed to be cfg-ed out for historical reasons (#65742).
#[cfg(FALSE)]
let box _x = Box::new('c');
}

View File

@ -0,0 +1,13 @@
error[E0658]: box pattern syntax is experimental
--> $DIR/feature-gate-deref_patterns.rs:3:9
|
LL | let box x = Box::new('c');
| ^^^^^
|
= note: see issue #29641 <https://github.com/rust-lang/rust/issues/29641> for more information
= help: add `#![feature(box_patterns)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,31 @@
//@ check-pass
#![feature(deref_patterns)]
#![allow(incomplete_features)]
use std::rc::Rc;
fn main() {
let vec: Vec<u32> = Vec::new();
match vec {
box [..] => {}
_ => {}
}
match Box::new(true) {
box true => {}
_ => {}
}
match &Box::new(true) {
box true => {}
_ => {}
}
match &Rc::new(0) {
box (1..) => {}
_ => {}
}
// FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a
// place of type `str`.
// match "foo".to_string() {
// box "foo" => {}
// _ => {}
// }
}