Skip to content

Commit 0867025

Browse files
authored
Rollup merge of #122222 - Nadrieril:deref-pat-feature-gate, r=compiler-errors
deref patterns: bare-bones feature gate and typechecking I am restarting the deref patterns experimentation. This introduces a feature gate under the lang-team [experimental feature](https://github.com/rust-lang/lang-team/blob/master/src/how_to/experiment.md) process, with [````@cramertj```` as lang-team liaison](rust-lang/lang-team#88) (it's been a while though, you still ok with this ````@cramertj?).```` Tracking issue: #87121. This is the barest-bones implementation I could think of: - explicit syntax, reusing `box <pat>` because that saves me a ton of work; - use `Deref` as a marker trait (instead of a yet-to-design `DerefPure`); - no support for mutable patterns with `DerefMut` for now; - MIR lowering will come in the next PR. It's the trickiest part. My goal is to let us figure out the MIR lowering part, which might take some work. And hopefully get something working for std types soon. This is in large part salvaged from ````@fee1-dead's```` #119467. r? ````@compiler-errors````
2 parents df8ac8f + 120d357 commit 0867025

File tree

16 files changed

+139
-17
lines changed

16 files changed

+139
-17
lines changed

compiler/rustc_ast_passes/src/feature_gate.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
413413
}
414414
}
415415
PatKind::Box(..) => {
416-
gate!(&self, box_patterns, pattern.span, "box pattern syntax is experimental");
416+
if !self.features.deref_patterns {
417+
// Allow box patterns under `deref_patterns`.
418+
gate!(&self, box_patterns, pattern.span, "box pattern syntax is experimental");
419+
}
417420
}
418421
PatKind::Range(_, Some(_), Spanned { node: RangeEnd::Excluded, .. }) => {
419422
gate!(
@@ -607,13 +610,16 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
607610
};
608611
}
609612

613+
if !visitor.features.deref_patterns {
614+
// Allow box patterns under `deref_patterns`.
615+
gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental");
616+
}
610617
gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental");
611618
// Despite being a new feature, `where T: Trait<Assoc(): Sized>`, which is RTN syntax now,
612619
// used to be gated under associated_type_bounds, which are right above, so RTN needs to
613620
// be too.
614621
gate_all_legacy_dont_use!(return_type_notation, "return type notation is experimental");
615622
gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental");
616-
gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental");
617623
gate_all_legacy_dont_use!(
618624
exclusive_range_pattern,
619625
"exclusive range pattern syntax is experimental"

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,8 @@ declare_features! (
436436
(unstable, deprecated_safe, "1.61.0", Some(94978)),
437437
/// Allows having using `suggestion` in the `#[deprecated]` attribute.
438438
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
439+
/// Allows deref patterns.
440+
(incomplete, deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121)),
439441
/// Controls errors in trait implementations.
440442
(unstable, do_not_recommend, "1.67.0", Some(51992)),
441443
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.

compiler/rustc_hir_typeck/src/pat.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
1818
use rustc_span::hygiene::DesugaringKind;
1919
use rustc_span::source_map::Spanned;
2020
use rustc_span::symbol::{kw, sym, Ident};
21-
use rustc_span::Span;
22-
use rustc_span::{BytePos, DUMMY_SP};
21+
use rustc_span::{BytePos, Span, DUMMY_SP};
2322
use rustc_target::abi::FieldIdx;
2423
use rustc_trait_selection::traits::{ObligationCause, Pattern};
2524
use ty::VariantDef;
@@ -211,6 +210,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
211210
PatKind::Tuple(elements, ddpos) => {
212211
self.check_pat_tuple(pat.span, elements, ddpos, expected, pat_info)
213212
}
213+
PatKind::Box(inner) if self.tcx.features().deref_patterns => {
214+
self.check_pat_deref(pat.span, inner, expected, pat_info)
215+
}
214216
PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
215217
PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
216218
PatKind::Slice(before, slice, after) => {
@@ -1975,6 +1977,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19751977
box_ty
19761978
}
19771979

1980+
fn check_pat_deref(
1981+
&self,
1982+
span: Span,
1983+
inner: &'tcx Pat<'tcx>,
1984+
expected: Ty<'tcx>,
1985+
pat_info: PatInfo<'tcx, '_>,
1986+
) -> Ty<'tcx> {
1987+
let tcx = self.tcx;
1988+
// FIXME(deref_patterns): use `DerefPure` for soundness
1989+
// FIXME(deref_patterns): use `DerefMut` when required
1990+
// <expected as Deref>::Target
1991+
let ty = Ty::new_projection(
1992+
tcx,
1993+
tcx.require_lang_item(hir::LangItem::DerefTarget, Some(span)),
1994+
[expected],
1995+
);
1996+
let ty = self.normalize(span, ty);
1997+
let ty = self.try_structurally_resolve_type(span, ty);
1998+
self.check_pat(inner, ty, pat_info);
1999+
expected
2000+
}
2001+
19782002
// Precondition: Pat is Ref(inner)
19792003
fn check_pat_ref(
19802004
&self,

compiler/rustc_middle/src/thir.rs

+9
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,7 @@ impl<'tcx> Pat<'tcx> {
647647
AscribeUserType { subpattern, .. }
648648
| Binding { subpattern: Some(subpattern), .. }
649649
| Deref { subpattern }
650+
| DerefPattern { subpattern }
650651
| InlineConstant { subpattern, .. } => subpattern.walk_(it),
651652
Leaf { subpatterns } | Variant { subpatterns, .. } => {
652653
subpatterns.iter().for_each(|field| field.pattern.walk_(it))
@@ -762,6 +763,11 @@ pub enum PatKind<'tcx> {
762763
subpattern: Box<Pat<'tcx>>,
763764
},
764765

766+
/// Deref pattern, written `box P` for now.
767+
DerefPattern {
768+
subpattern: Box<Pat<'tcx>>,
769+
},
770+
765771
/// One of the following:
766772
/// * `&str` (represented as a valtree), which will be handled as a string pattern and thus
767773
/// exhaustiveness checking will detect if you use the same string twice in different
@@ -1172,6 +1178,9 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
11721178
}
11731179
write!(f, "{subpattern}")
11741180
}
1181+
PatKind::DerefPattern { ref subpattern } => {
1182+
write!(f, "k#deref {subpattern}")
1183+
}
11751184
PatKind::Constant { value } => write!(f, "{value}"),
11761185
PatKind::InlineConstant { def: _, ref subpattern } => {
11771186
write!(f, "{} (from inline const)", subpattern)

compiler/rustc_middle/src/thir/visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
229229
match &pat.kind {
230230
AscribeUserType { subpattern, ascription: _ }
231231
| Deref { subpattern }
232+
| DerefPattern { subpattern }
232233
| Binding {
233234
subpattern: Some(subpattern),
234235
mutability: _,

compiler/rustc_mir_build/src/build/matches/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
879879
self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f);
880880
}
881881

882+
PatKind::DerefPattern { ref subpattern } => {
883+
self.visit_primary_bindings(subpattern, UserTypeProjections::none(), f);
884+
}
885+
882886
PatKind::AscribeUserType {
883887
ref subpattern,
884888
ascription: thir::Ascription { ref annotation, variance: _ },

compiler/rustc_mir_build/src/build/matches/util.rs

+6
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,12 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
256256
subpairs.push(MatchPair::new(place_builder, subpattern, cx));
257257
default_irrefutable()
258258
}
259+
260+
PatKind::DerefPattern { .. } => {
261+
// FIXME(deref_patterns)
262+
// Treat it like a wildcard for now.
263+
default_irrefutable()
264+
}
259265
};
260266

261267
MatchPair { place, test_case, subpairs, pattern }

compiler/rustc_mir_build/src/check_unsafety.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
250250
| PatKind::Variant { .. }
251251
| PatKind::Leaf { .. }
252252
| PatKind::Deref { .. }
253+
| PatKind::DerefPattern { .. }
253254
| PatKind::Range { .. }
254255
| PatKind::Slice { .. }
255256
| PatKind::Array { .. } => {
@@ -310,7 +311,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
310311
}
311312
visit::walk_pat(self, pat);
312313
}
313-
PatKind::Deref { .. } => {
314+
PatKind::Deref { .. } | PatKind::DerefPattern { .. } => {
314315
let old_inside_adt = std::mem::replace(&mut self.inside_adt, false);
315316
visit::walk_pat(self, pat);
316317
self.inside_adt = old_inside_adt;

compiler/rustc_mir_build/src/thir/pattern/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
257257
return self.lower_path(qpath, pat.hir_id, pat.span);
258258
}
259259

260+
hir::PatKind::Box(subpattern) if self.tcx.features().deref_patterns => {
261+
PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern) }
262+
}
260263
hir::PatKind::Ref(subpattern, _) | hir::PatKind::Box(subpattern) => {
261264
PatKind::Deref { subpattern: self.lower_pattern(subpattern) }
262265
}

compiler/rustc_mir_build/src/thir/print.rs

+6
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,12 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
688688
self.print_pat(subpattern, depth_lvl + 2);
689689
print_indented!(self, "}", depth_lvl + 1);
690690
}
691+
PatKind::DerefPattern { subpattern } => {
692+
print_indented!(self, "DerefPattern { ", depth_lvl + 1);
693+
print_indented!(self, "subpattern:", depth_lvl + 2);
694+
self.print_pat(subpattern, depth_lvl + 2);
695+
print_indented!(self, "}", depth_lvl + 1);
696+
}
691697
PatKind::Constant { value } => {
692698
print_indented!(self, "Constant {", depth_lvl + 1);
693699
print_indented!(self, format!("value: {:?}", value), depth_lvl + 2);

compiler/rustc_pattern_analysis/src/rustc.rs

+6
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
462462
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty),
463463
};
464464
}
465+
PatKind::DerefPattern { .. } => {
466+
// FIXME(deref_patterns): At least detect that `box _` is irrefutable.
467+
fields = vec![];
468+
arity = 0;
469+
ctor = Opaque(OpaqueId::new());
470+
}
465471
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
466472
match ty.kind() {
467473
ty::Tuple(fs) => {

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,7 @@ symbols! {
674674
deref_method,
675675
deref_mut,
676676
deref_mut_method,
677+
deref_patterns,
677678
deref_target,
678679
derive,
679680
derive_const,

tests/ui/cfg/cfg-false-feature.stderr

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

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

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fn main() {
2+
// We reuse the `box` syntax so this doesn't actually test the feature gate but eh.
3+
let box x = Box::new('c'); //~ ERROR box pattern syntax is experimental
4+
println!("x: {}", x);
5+
6+
// `box` syntax is allowed to be cfg-ed out for historical reasons (#65742).
7+
#[cfg(FALSE)]
8+
let box _x = Box::new('c');
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0658]: box pattern syntax is experimental
2+
--> $DIR/feature-gate-deref_patterns.rs:3:9
3+
|
4+
LL | let box x = Box::new('c');
5+
| ^^^^^
6+
|
7+
= note: see issue #29641 <https://github.com/rust-lang/rust/issues/29641> for more information
8+
= help: add `#![feature(box_patterns)]` to the crate attributes to enable
9+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
10+
11+
error: aborting due to 1 previous error
12+
13+
For more information about this error, try `rustc --explain E0658`.
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ check-pass
2+
#![feature(deref_patterns)]
3+
#![allow(incomplete_features)]
4+
5+
use std::rc::Rc;
6+
7+
fn main() {
8+
let vec: Vec<u32> = Vec::new();
9+
match vec {
10+
box [..] => {}
11+
_ => {}
12+
}
13+
match Box::new(true) {
14+
box true => {}
15+
_ => {}
16+
}
17+
match &Box::new(true) {
18+
box true => {}
19+
_ => {}
20+
}
21+
match &Rc::new(0) {
22+
box (1..) => {}
23+
_ => {}
24+
}
25+
// FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a
26+
// place of type `str`.
27+
// match "foo".to_string() {
28+
// box "foo" => {}
29+
// _ => {}
30+
// }
31+
}

0 commit comments

Comments
 (0)