Skip to content

Commit

Permalink
Add barest-bones deref patterns
Browse files Browse the repository at this point in the history
Co-authored-by: Deadbeef <[email protected]>
  • Loading branch information
2 people authored and compiler-errors committed Mar 20, 2024
1 parent 94b72d6 commit 1345c72
Show file tree
Hide file tree
Showing 16 changed files with 138 additions and 17 deletions.
10 changes: 8 additions & 2 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
}
}
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!(
Expand Down Expand Up @@ -607,13 +610,16 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
};
}

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"
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,8 @@ declare_features! (
(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(...))]`.
Expand Down
28 changes: 26 additions & 2 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
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;
Expand Down Expand Up @@ -211,6 +210,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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) => {
Expand Down Expand Up @@ -1975,6 +1977,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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,
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@ impl<'tcx> Pat<'tcx> {
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))
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1172,6 +1178,9 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
}
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)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/thir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: _,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_mir_build/src/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
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: _ },
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_mir_build/src/build/matches/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,12 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
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 }
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_mir_build/src/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
| PatKind::Variant { .. }
| PatKind::Leaf { .. }
| PatKind::Deref { .. }
| PatKind::DerefPattern { .. }
| PatKind::Range { .. }
| PatKind::Slice { .. }
| PatKind::Array { .. } => {
Expand Down Expand Up @@ -310,7 +311,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, '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;
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_mir_build/src/thir/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
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) }
}
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_mir_build/src/thir/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,12 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
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);
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_pattern_analysis/src/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,11 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'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![];
ctor = Opaque(OpaqueId::new());
}
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
match ty.kind() {
ty::Tuple(fs) => {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@ symbols! {
deref_method,
deref_mut,
deref_mut_method,
deref_patterns,
deref_target,
derive,
derive_const,
Expand Down
24 changes: 12 additions & 12 deletions tests/ui/cfg/cfg-false-feature.stderr
Original file line number Diff line number Diff line change
@@ -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
|
Expand All @@ -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

9 changes: 9 additions & 0 deletions tests/ui/feature-gates/feature-gate-deref_patterns.rs
Original file line number Diff line number Diff line change
@@ -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');
}
13 changes: 13 additions & 0 deletions tests/ui/feature-gates/feature-gate-deref_patterns.stderr
Original file line number Diff line number Diff line change
@@ -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`.
31 changes: 31 additions & 0 deletions tests/ui/pattern/deref-patterns/typeck.rs
Original file line number Diff line number Diff line change
@@ -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" => {}
// _ => {}
// }
}

0 comments on commit 1345c72

Please sign in to comment.