diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 8e4f4c8e71a2e..c3c2834b6dc0b 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -607,7 +607,7 @@ impl Pat { /// Walk top-down and call `it` in each place where a pattern occurs /// starting with the root pattern `walk` is called on. If `it` returns /// false then we will descend no further but siblings will be processed. - pub fn walk(&self, it: &mut impl FnMut(&Pat) -> bool) { + pub fn walk<'s>(&'s self, it: &mut impl FnMut(&'s Pat) -> bool) { if !it(self) { return; } @@ -626,9 +626,11 @@ impl Pat { | PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)), // Trivial wrappers over inner patterns. - PatKind::Box(s) | PatKind::Deref(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => { - s.walk(it) - } + PatKind::Box(s) + | PatKind::Deref(s) + | PatKind::Ref(s, _) + | PatKind::Paren(s) + | PatKind::Guard(s, _) => s.walk(it), // These patterns do not contain subpatterns, skip. PatKind::Wild @@ -838,6 +840,9 @@ pub enum PatKind { // A never pattern `!`. Never, + /// A guard pattern (e.g., `x if guard(x)`). + Guard(P, P), + /// Parentheses in patterns used for grouping (i.e., `(PAT)`). Paren(P), diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 2afbd979c3023..fefb85570d673 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1366,6 +1366,10 @@ pub fn walk_pat(vis: &mut T, pat: &mut P) { visit_opt(e2, |e| vis.visit_expr(e)); vis.visit_span(span); } + PatKind::Guard(p, e) => { + vis.visit_pat(p); + vis.visit_expr(e); + } PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { visit_thin_vec(elems, |elem| vis.visit_pat(elem)) } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index eb71ec5f4ec93..4db39717dc00d 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -670,6 +670,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res visit_opt!(visitor, visit_expr, lower_bound); visit_opt!(visitor, visit_expr, upper_bound); } + PatKind::Guard(subpattern, guard_condition) => { + try_visit!(visitor.visit_pat(subpattern)); + try_visit!(visitor.visit_expr(guard_condition)); + } PatKind::Wild | PatKind::Rest | PatKind::Never => {} PatKind::Err(_guar) => {} PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index ace7bfb5c73f2..54bd41bd62333 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -114,6 +114,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_range_end(end, e2.is_some()), ); } + PatKind::Guard(inner, cond) => { + break hir::PatKind::Guard(self.lower_pat(inner), self.lower_expr(cond)); + } PatKind::Slice(pats) => break self.lower_pat_slice(pats), PatKind::Rest => { // If we reach here the `..` pattern is not semantically allowed. diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index d646150a620cd..2712fe8093832 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -540,6 +540,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(const_closures, "const closures are experimental"); gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(explicit_tail_calls, "`become` expression is experimental"); + gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); gate_all!(generic_const_items, "generic const items are experimental"); gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); gate_all!(postfix_match, "postfix match is experimental"); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index de9f5187be738..e06012650907d 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1710,6 +1710,12 @@ impl<'a> State<'a> { self.print_expr(e, FixupContext::default()); } } + PatKind::Guard(subpat, condition) => { + self.print_pat(subpat); + self.space(); + self.word_space("if"); + self.print_expr(condition, FixupContext::default()); + } PatKind::Slice(elts) => { self.word("["); self.commasep(Inconsistent, elts, |s, p| s.print_pat(p)); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 5ffafcaa54262..62554ae9ef74a 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -989,7 +989,7 @@ pub fn parse_ast_fragment<'a>( } } AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?), - AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_alt( + AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::Yes, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a99d90488861c..d80b43d2d5767 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -501,6 +501,8 @@ declare_features! ( (incomplete, generic_const_items, "1.73.0", Some(113521)), /// Allows registering static items globally, possibly across crates, to iterate over at runtime. (unstable, global_registration, "1.80.0", Some(125119)), + /// Allows using guards in patterns. + (incomplete, guard_patterns, "CURRENT_RUSTC_VERSION", Some(129967)), /// Allows using `..=X` as a patterns in slices. (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)), /// Allows `if let` guard in match arms. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 1c268c8bbe06b..61e502e68b4af 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1139,7 +1139,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true, - Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it), + Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it), Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)), Slice(before, slice, after) => { @@ -1166,7 +1166,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {} - Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it), + Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it), Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), Slice(before, slice, after) => { @@ -1318,6 +1318,9 @@ pub enum PatKind<'hir> { /// A literal. Lit(&'hir Expr<'hir>), + /// A guard pattern (e.g., `x if guard(x)`). + Guard(&'hir Pat<'hir>, &'hir Expr<'hir>), + /// A range pattern (e.g., `1..=2` or `1..2`). Range(Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>, RangeEnd), diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 322f8e2a517c8..7306d1ec9332d 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -700,6 +700,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: visit_opt!(visitor, visit_pat, slice_pattern); walk_list!(visitor, visit_pat, postpatterns); } + PatKind::Guard(subpat, condition) => { + try_visit!(visitor.visit_pat(subpat)); + try_visit!(visitor.visit_expr(condition)); + } } V::Result::output() } diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 679f6ccb8165d..3495d4a547a7c 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -678,7 +678,9 @@ fn resolve_local<'tcx>( | PatKind::TupleStruct(_, subpats, _) | PatKind::Tuple(subpats, _) => subpats.iter().any(|p| is_binding_pat(p)), - PatKind::Box(subpat) | PatKind::Deref(subpat) => is_binding_pat(subpat), + PatKind::Box(subpat) | PatKind::Deref(subpat) | PatKind::Guard(subpat, _) => { + is_binding_pat(subpat) + } PatKind::Ref(_, _) | PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..) diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 61214b9921533..a24d99e0096d4 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1870,6 +1870,12 @@ impl<'a> State<'a> { self.commasep(Inconsistent, after, |s, p| s.print_pat(p)); self.word("]"); } + PatKind::Guard(inner, cond) => { + self.print_pat(inner); + self.space(); + self.word_space("if"); + self.print_expr(cond); + } PatKind::Err(_) => { self.popen(); self.word("/*ERROR*/"); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d6e5fab610ed6..f1b315a531e56 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -422,6 +422,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { subpats.iter().all(|pat| self.pat_guaranteed_to_constitute_read_for_never(pat)) } + // Passes through to the sub-pattern. + hir::PatKind::Guard(subpat, _) => { + self.pat_guaranteed_to_constitute_read_for_never(subpat) + } + // Does constitute a read, since it is equivalent to a discriminant read. hir::PatKind::Never => true, diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index bb5f351137305..a17d22ab643ef 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -612,6 +612,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx | PatKind::Box(_) | PatKind::Deref(_) | PatKind::Ref(..) + | PatKind::Guard(..) | PatKind::Wild | PatKind::Err(_) => { // If the PatKind is Or, Box, or Ref, the decision is made later @@ -1722,7 +1723,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } } - PatKind::Binding(.., Some(subpat)) => { + PatKind::Binding(.., Some(subpat)) | PatKind::Guard(subpat, _) => { self.cat_pattern(place_with_id, subpat, op)?; } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index cba6586f01dd8..df4ff62196bd9 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -259,6 +259,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Struct(ref qpath, fields, has_rest_pat) => { self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info) } + PatKind::Guard(pat, cond) => { + self.check_pat(pat, expected, pat_info); + self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {}); + expected + } PatKind::Or(pats) => { for pat in pats { self.check_pat(pat, expected, pat_info); @@ -397,7 +402,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // An OR-pattern just propagates to each individual alternative. // This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`. // In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`. - | PatKind::Or(_) => AdjustMode::Pass, + | PatKind::Or(_) + // Like or-patterns, guard patterns just propogate to their subpatterns. + | PatKind::Guard(..) => AdjustMode::Pass, } } @@ -876,6 +883,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Or(..) + | PatKind::Guard(..) | PatKind::Tuple(..) | PatKind::Slice(..) => "binding", diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index bbb290c9459e8..497bef9413699 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1226,7 +1226,7 @@ impl EarlyLintPass for UnusedParens { self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); }, // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. - Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), + Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space), // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index fe865b8a51508..c269d84532c60 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -645,7 +645,8 @@ impl<'tcx> Pat<'tcx> { | Binding { subpattern: Some(subpattern), .. } | Deref { subpattern } | DerefPattern { subpattern, .. } - | InlineConstant { subpattern, .. } => subpattern.walk_(it), + | InlineConstant { subpattern, .. } + | Guard { subpattern, .. } => subpattern.walk_(it), Leaf { subpatterns } | Variant { subpatterns, .. } => { subpatterns.iter().for_each(|field| field.pattern.walk_(it)) } @@ -828,6 +829,13 @@ pub enum PatKind<'tcx> { pats: Box<[Box>]>, }, + /// A guard pattern, e.g. `x if guard(x)`. + Guard { + subpattern: Box>, + #[type_visitable(ignore)] + condition: ExprId, + }, + /// A never pattern `!`. Never, diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 36f0e3d890cfd..5de1d4d90ccb8 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -265,5 +265,9 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( visitor.visit_pat(pat); } } + Guard { subpattern, condition } => { + visitor.visit_pat(subpattern); + visitor.visit_expr(&visitor.thir()[*condition]); + } }; } diff --git a/compiler/rustc_mir_build/src/build/matches/match_pair.rs b/compiler/rustc_mir_build/src/build/matches/match_pair.rs index 6df50057ee83a..375f672c5f80d 100644 --- a/compiler/rustc_mir_build/src/build/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/build/matches/match_pair.rs @@ -1,4 +1,5 @@ use rustc_middle::mir::*; +use rustc_middle::span_bug; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -250,6 +251,10 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { } PatKind::Never => TestCase::Never, + + PatKind::Guard { .. } => { + span_bug!(pattern.span, "MIR lowering is not yet implemented for guard patterns") + } }; MatchPairTree { place, test_case, subpairs, pattern } diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 51ead57020556..927b430f7bc4d 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -921,6 +921,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.visit_primary_bindings(subpattern, pattern_user_ty, f) } + PatKind::Guard { ref subpattern, .. } => { + self.visit_primary_bindings(subpattern, pattern_user_ty, f); + } + PatKind::Leaf { ref subpatterns } => { for subpattern in subpatterns { let subpattern_user_ty = pattern_user_ty.clone().leaf(subpattern.field); diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index f3e6301d9d165..f7e8ae99d0b3d 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -334,6 +334,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { PatKind::Or { .. } | PatKind::InlineConstant { .. } | PatKind::AscribeUserType { .. } | + PatKind::Guard { .. } | PatKind::Error(_) => {} } }; diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 3dbb552cdbbef..c024bde92cc22 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -22,7 +22,7 @@ use rustc_middle::util::Providers; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { - providers.check_match = thir::pattern::check_match; + providers.check_match = thir::check_match::check_match; providers.lit_to_const = thir::constant::lit_to_const; providers.hooks.build_mir = build::mir_build; providers.closure_saved_names_of_captured_variables = diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/check_match.rs similarity index 100% rename from compiler/rustc_mir_build/src/thir/pattern/check_match.rs rename to compiler/rustc_mir_build/src/thir/check_match.rs diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 377931e3be730..e0f3362a76d40 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -2,6 +2,10 @@ //! structures into the THIR. The `builder` is generally ignorant of the tcx, //! etc., and instead goes through the `Cx` for most of its work. +mod block; +mod expr; +mod pattern; + use rustc_data_structures::steal::Steal; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; @@ -13,9 +17,7 @@ use rustc_middle::bug; use rustc_middle::middle::region; use rustc_middle::thir::*; use rustc_middle::ty::{self, RvalueScopes, TyCtxt}; -use tracing::instrument; -use crate::thir::pattern::pat_from_hir; use crate::thir::util::UserAnnotatedTyHelpers; pub(crate) fn thir_body( @@ -109,11 +111,6 @@ impl<'tcx> Cx<'tcx> { } } - #[instrument(level = "debug", skip(self))] - fn pattern_from_hir(&mut self, p: &'tcx hir::Pat<'tcx>) -> Box> { - pat_from_hir(self.tcx, self.param_env, self.typeck_results(), p) - } - fn closure_env_param(&self, owner_def: LocalDefId, expr_id: HirId) -> Option> { if self.tcx.def_kind(owner_def) != DefKind::Closure { return None; @@ -206,6 +203,3 @@ impl<'tcx> UserAnnotatedTyHelpers<'tcx> for Cx<'tcx> { self.typeck_results } } - -mod block; -mod expr; diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/cx/pattern/const_to_pat.rs similarity index 97% rename from compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs rename to compiler/rustc_mir_build/src/thir/cx/pattern/const_to_pat.rs index 0dfa9168f7c80..f086540c325b1 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/cx/pattern/const_to_pat.rs @@ -14,13 +14,13 @@ use rustc_trait_selection::traits::ObligationCause; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use tracing::{debug, instrument, trace}; -use super::PatCtxt; +use super::PatCx; use crate::errors::{ ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern, PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern, }; -impl<'a, 'tcx> PatCtxt<'a, 'tcx> { +impl<'a, 'tcx> PatCx<'a, 'tcx> { /// Converts a constant to a pattern (if possible). /// This means aggregate values (like structs and enums) are converted /// to a pattern that matches the value (as if you'd compared via structural equality). @@ -36,7 +36,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { id: hir::HirId, span: Span, ) -> Box> { - let infcx = self.tcx.infer_ctxt().build(); + let infcx = self.cx.tcx.infer_ctxt().build(); let mut convert = ConstToPat::new(self, id, span, infcx); convert.to_pat(c, ty) } @@ -53,18 +53,14 @@ struct ConstToPat<'tcx> { } impl<'tcx> ConstToPat<'tcx> { - fn new( - pat_ctxt: &PatCtxt<'_, 'tcx>, - id: hir::HirId, - span: Span, - infcx: InferCtxt<'tcx>, - ) -> Self { - trace!(?pat_ctxt.typeck_results.hir_owner); + fn new(pat_ctxt: &PatCx<'_, 'tcx>, id: hir::HirId, span: Span, infcx: InferCtxt<'tcx>) -> Self { + trace!(?pat_ctxt.cx.typeck_results.hir_owner); ConstToPat { span, infcx, - param_env: pat_ctxt.param_env, + param_env: pat_ctxt.cx.param_env, treat_byte_string_as_slice: pat_ctxt + .cx .typeck_results .treat_byte_string_as_slice .contains(&id.local_id), diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/cx/pattern/mod.rs similarity index 83% rename from compiler/rustc_mir_build/src/thir/pattern/mod.rs rename to compiler/rustc_mir_build/src/thir/cx/pattern/mod.rs index 56e5156a91fc0..7adde8447703d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/pattern/mod.rs @@ -1,6 +1,5 @@ //! Validation of patterns/matches. -mod check_match; mod const_to_pat; use std::cmp::Ordering; @@ -10,72 +9,65 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd}; use rustc_index::Idx; -use rustc_lint as lint; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; use rustc_middle::thir::{ Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, }; use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span}; use rustc_target::abi::{FieldIdx, Integer}; use tracing::{debug, instrument}; -pub(crate) use self::check_match::check_match; +use super::Cx; use crate::errors::*; use crate::fluent_generated as fluent; use crate::thir::util::UserAnnotatedTyHelpers; -struct PatCtxt<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - - /// Used by the Rust 2024 migration lint. - rust_2024_migration_suggestion: Option, -} - -pub(super) fn pat_from_hir<'a, 'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - pat: &'tcx hir::Pat<'tcx>, -) -> Box> { - let mut pcx = PatCtxt { - tcx, - param_env, - typeck_results, - rust_2024_migration_suggestion: typeck_results - .rust_2024_migration_desugared_pats() - .get(pat.hir_id) - .map(|&is_hard_error| Rust2024IncompatiblePatSugg { - suggestion: Vec::new(), - is_hard_error, - }), - }; - let result = pcx.lower_pattern(pat); - debug!("pat_from_hir({:?}) = {:?}", pat, result); - if let Some(sugg) = pcx.rust_2024_migration_suggestion { - if sugg.is_hard_error { - let mut err = - tcx.dcx().struct_span_err(pat.span, fluent::mir_build_rust_2024_incompatible_pat); - err.subdiagnostic(sugg); - err.emit(); - } else { - tcx.emit_node_span_lint( - lint::builtin::RUST_2024_INCOMPATIBLE_PAT, - pat.hir_id, - pat.span, - Rust2024IncompatiblePat { sugg }, - ); +impl<'tcx> Cx<'tcx> { + // #[instrument(skip(self), level = "debug")] + pub(super) fn pattern_from_hir(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box> { + let mut pcx = PatCx { + rust_2024_migration_suggestion: self + .typeck_results + .rust_2024_migration_desugared_pats() + .get(pat.hir_id) + .map(|&is_hard_error| Rust2024IncompatiblePatSugg { + suggestion: Vec::new(), + is_hard_error, + }), + cx: self, + }; + let result = pcx.lower_pattern(pat); + debug!("pat_from_hir({:?}) = {:?}", pat, result); + if let Some(sugg) = pcx.rust_2024_migration_suggestion { + if sugg.is_hard_error { + let mut err = self + .tcx + .dcx() + .struct_span_err(pat.span, fluent::mir_build_rust_2024_incompatible_pat); + err.subdiagnostic(sugg); + err.emit(); + } else { + self.tcx.emit_node_span_lint( + rustc_lint::builtin::RUST_2024_INCOMPATIBLE_PAT, + pat.hir_id, + pat.span, + Rust2024IncompatiblePat { sugg }, + ); + } } + result } - result +} +struct PatCx<'c, 'tcx> { + cx: &'c mut Cx<'tcx>, + rust_2024_migration_suggestion: Option, } -impl<'a, 'tcx> PatCtxt<'a, 'tcx> { +impl<'c, 'tcx> PatCx<'c, 'tcx> { fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box> { // When implicit dereferences have been inserted in this pattern, the unadjusted lowered // pattern has the type that results *after* dereferencing. For example, in this code: @@ -98,7 +90,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // gets the least-dereferenced type). let unadjusted_pat = match pat.kind { hir::PatKind::Ref(inner, _) - if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) => + if self.cx.typeck_results.skipped_ref_pats().contains(pat.hir_id) => { self.lower_pattern(inner) } @@ -106,7 +98,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; let adjustments: &[Ty<'tcx>] = - self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); + self.cx.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, ref_ty| { debug!("{:?}: wrapping pattern with type {:?}", thir_pat, ref_ty); Box::new(Pat { @@ -163,7 +155,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let msg = format!( "found bad range pattern endpoint `{expr:?}` outside of error recovery" ); - return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg)); + return Err(self.cx.tcx.dcx().span_delayed_bug(expr.span, msg)); }; Ok((Some(PatRangeBoundary::Finite(value)), ascr, inline_const)) } @@ -204,11 +196,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; let (min, max): (i128, u128) = match ty.kind() { ty::Int(ity) => { - let size = Integer::from_int_ty(&self.tcx, *ity).size(); + let size = Integer::from_int_ty(&self.cx.tcx, *ity).size(); (size.signed_int_min(), size.signed_int_max() as u128) } ty::Uint(uty) => { - let size = Integer::from_uint_ty(&self.tcx, *uty).size(); + let size = Integer::from_uint_ty(&self.cx.tcx, *uty).size(); (0, size.unsigned_int_max()) } _ => { @@ -218,7 +210,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // Detect literal value out of range `[min, max]` inclusive, avoiding use of `-min` to // prevent overflow/panic. if (negated && lit_val > max + 1) || (!negated && lit_val > max) { - return Err(self.tcx.dcx().emit_err(LiteralOutOfRange { span, ty, min, max })); + return Err(self.cx.tcx.dcx().emit_err(LiteralOutOfRange { span, ty, min, max })); } Ok(()) } @@ -233,7 +225,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { ) -> Result, ErrorGuaranteed> { if lo_expr.is_none() && hi_expr.is_none() { let msg = "found twice-open range pattern (`..`) outside of error recovery"; - self.tcx.dcx().span_bug(span, msg); + self.cx.tcx.dcx().span_bug(span, msg); } let (lo, lo_ascr, lo_inline) = self.lower_pattern_range_endpoint(lo_expr)?; @@ -242,7 +234,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let lo = lo.unwrap_or(PatRangeBoundary::NegInfinity); let hi = hi.unwrap_or(PatRangeBoundary::PosInfinity); - let cmp = lo.compare_with(hi, ty, self.tcx, self.param_env); + let cmp = lo.compare_with(hi, ty, self.cx.tcx, self.cx.param_env); let mut kind = PatKind::Range(Box::new(PatRange { lo, hi, end, ty })); match (end, cmp) { // `x..y` where `x < y`. @@ -265,13 +257,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { self.error_on_literal_overflow(hi_expr, ty)?; let e = match end { RangeEnd::Included => { - self.tcx.dcx().emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper { + self.cx.tcx.dcx().emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper { span, - teach: self.tcx.sess.teach(E0030), + teach: self.cx.tcx.sess.teach(E0030), }) } RangeEnd::Excluded => { - self.tcx.dcx().emit_err(LowerRangeBoundMustBeLessThanUpper { span }) + self.cx.tcx.dcx().emit_err(LowerRangeBoundMustBeLessThanUpper { span }) } }; return Err(e); @@ -295,7 +287,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug")] fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box> { - let mut ty = self.typeck_results.node_type(pat.hir_id); + let mut ty = self.cx.typeck_results.node_type(pat.hir_id); let mut span = pat.span; let kind = match pat.kind { @@ -316,7 +308,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } hir::PatKind::Deref(subpattern) => { - let mutable = self.typeck_results.pat_has_ref_mut_binding(subpattern); + let mutable = self.cx.typeck_results.pat_has_ref_mut_binding(subpattern); let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), mutability } } @@ -342,6 +334,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } let mode = *self + .cx .typeck_results .pat_binding_modes() .get(pat.hir_id) @@ -383,7 +376,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } hir::PatKind::TupleStruct(ref qpath, pats, ddpos) => { - let res = self.typeck_results.qpath_res(qpath, pat.hir_id); + let res = self.cx.typeck_results.qpath_res(qpath, pat.hir_id); let ty::Adt(adt_def, _) = ty.kind() else { span_bug!(pat.span, "tuple struct pattern not applied to an ADT {:?}", ty); }; @@ -393,11 +386,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } hir::PatKind::Struct(ref qpath, fields, _) => { - let res = self.typeck_results.qpath_res(qpath, pat.hir_id); + let res = self.cx.typeck_results.qpath_res(qpath, pat.hir_id); let subpatterns = fields .iter() .map(|field| FieldPat { - field: self.typeck_results.field_index(field.hir_id), + field: self.cx.typeck_results.field_index(field.hir_id), pattern: self.lower_pattern(field.pat), }) .collect(); @@ -407,6 +400,15 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) }, + hir::PatKind::Guard(subpat, condition) if self.cx.tcx.features().guard_patterns() => { + PatKind::Guard { + subpattern: self.lower_pattern(subpat), + condition: self.cx.mirror_expr(condition), + } + } + // FIXME(guard_patterns): remove this once MIR lowering doesn't ICE on guard patterns + hir::PatKind::Guard(subpat, _) => self.lower_pattern(subpat).kind, + hir::PatKind::Err(guar) => PatKind::Error(guar), }; @@ -453,7 +455,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // Fixed-length array, `[T; len]`. ty::Array(_, len) => { let len = len - .try_to_target_usize(self.tcx) + .try_to_target_usize(self.cx.tcx) .expect("expected len of array pat to be definite"); assert!(len >= prefix.len() as u64 + suffix.len() as u64); PatKind::Array { prefix, slice, suffix } @@ -472,7 +474,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { ) -> PatKind<'tcx> { let res = match res { Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => { - let variant_id = self.tcx.parent(variant_ctor_id); + let variant_id = self.cx.tcx.parent(variant_ctor_id); Res::Def(DefKind::Variant, variant_id) } res => res, @@ -480,8 +482,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let mut kind = match res { Res::Def(DefKind::Variant, variant_id) => { - let enum_id = self.tcx.parent(variant_id); - let adt_def = self.tcx.adt_def(enum_id); + let enum_id = self.cx.tcx.parent(variant_id); + let adt_def = self.cx.tcx.adt_def(enum_id); if adt_def.is_enum() { let args = match ty.kind() { ty::Adt(_, args) | ty::FnDef(_, args) => args, @@ -516,23 +518,23 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { _ => { let e = match res { Res::Def(DefKind::ConstParam, _) => { - self.tcx.dcx().emit_err(ConstParamInPattern { span }) + self.cx.tcx.dcx().emit_err(ConstParamInPattern { span }) } Res::Def(DefKind::Static { .. }, _) => { - self.tcx.dcx().emit_err(StaticInPattern { span }) + self.cx.tcx.dcx().emit_err(StaticInPattern { span }) } - _ => self.tcx.dcx().emit_err(NonConstPath { span }), + _ => self.cx.tcx.dcx().emit_err(NonConstPath { span }), }; PatKind::Error(e) } }; - if let Some(user_ty) = self.user_args_applied_to_ty_of_hir_id(hir_id) { + if let Some(user_ty) = self.cx.user_args_applied_to_ty_of_hir_id(hir_id) { debug!("lower_variant_or_leaf: kind={:?} user_ty={:?} span={:?}", kind, user_ty, span); let annotation = CanonicalUserTypeAnnotation { user_ty: Box::new(user_ty), span, - inferred_ty: self.typeck_results.node_type(hir_id), + inferred_ty: self.cx.typeck_results.node_type(hir_id), }; kind = PatKind::AscribeUserType { subpattern: Box::new(Pat { span, ty, kind }), @@ -548,8 +550,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// is converted to the corresponding pattern via `lower_variant_or_leaf`. #[instrument(skip(self), level = "debug")] fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Box> { - let ty = self.typeck_results.node_type(id); - let res = self.typeck_results.qpath_res(qpath, id); + let ty = self.cx.typeck_results.node_type(id); + let res = self.cx.typeck_results.qpath_res(qpath, id); let pat_from_kind = |kind| Box::new(Pat { span, ty, kind }); @@ -560,20 +562,20 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { _ => return pat_from_kind(self.lower_variant_or_leaf(res, id, span, ty, vec![])), }; - let args = self.typeck_results.node_args(id); - let c = ty::Const::new_unevaluated(self.tcx, ty::UnevaluatedConst { def: def_id, args }); + let args = self.cx.typeck_results.node_args(id); + let c = ty::Const::new_unevaluated(self.cx.tcx, ty::UnevaluatedConst { def: def_id, args }); let pattern = self.const_to_pat(c, ty, id, span); if !is_associated_const { return pattern; } - let user_provided_types = self.typeck_results().user_provided_types(); + let user_provided_types = self.cx.typeck_results().user_provided_types(); if let Some(&user_ty) = user_provided_types.get(id) { let annotation = CanonicalUserTypeAnnotation { user_ty: Box::new(user_ty), span, - inferred_ty: self.typeck_results().node_type(id), + inferred_ty: self.cx.typeck_results().node_type(id), }; Box::new(Pat { span, @@ -600,7 +602,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { id: hir::HirId, span: Span, ) -> PatKind<'tcx> { - let tcx = self.tcx; + let tcx = self.cx.tcx; let def_id = block.def_id; let body_id = block.body; let expr = &tcx.hir().body(body_id).value; @@ -636,7 +638,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { debug_assert!(!args.has_free_regions()); let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), args }; - let subpattern = self.const_to_pat(ty::Const::new_unevaluated(self.tcx, ct), ty, id, span); + let subpattern = + self.const_to_pat(ty::Const::new_unevaluated(self.cx.tcx, ct), ty, id, span); PatKind::InlineConstant { subpattern, def: def_id } } @@ -662,22 +665,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { _ => span_bug!(expr.span, "not a literal: {:?}", expr), }; - let ct_ty = self.typeck_results.expr_ty(expr); + let ct_ty = self.cx.typeck_results.expr_ty(expr); let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg }; - match self.tcx.at(expr.span).lit_to_const(lit_input) { + match self.cx.tcx.at(expr.span).lit_to_const(lit_input) { Ok(constant) => self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind, Err(LitToConstError::Reported(e)) => PatKind::Error(e), Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), } } } - -impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn typeck_results(&self) -> &ty::TypeckResults<'tcx> { - self.typeck_results - } -} diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs index ca26cc13b5e87..7a9a4cb909f3c 100644 --- a/compiler/rustc_mir_build/src/thir/mod.rs +++ b/compiler/rustc_mir_build/src/thir/mod.rs @@ -4,8 +4,8 @@ //! unit-tested and separated from the Rust source and compiler data //! structures. +pub(crate) mod check_match; pub(crate) mod constant; pub(crate) mod cx; -pub(crate) mod pattern; pub(crate) mod print; mod util; diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index dae13df4054a4..983f11804de13 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -770,6 +770,14 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "]", depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } + PatKind::Guard { subpattern, condition } => { + print_indented!(self, "Guard {", depth_lvl + 1); + print_indented!(self, "subpattern:", depth_lvl + 2); + self.print_pat(subpattern, depth_lvl + 3); + print_indented!(self, "condition:", depth_lvl + 2); + self.print_expr(*condition, depth_lvl + 3); + print_indented!(self, "}", depth_lvl + 1); + } PatKind::Error(_) => { print_indented!(self, "Error", depth_lvl + 1); } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 0ac6133e8289f..703f96786600c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2630,7 +2630,7 @@ impl<'a> Parser<'a> { }; self.bump(); // Eat `let` token let lo = self.prev_token.span; - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -2767,7 +2767,7 @@ impl<'a> Parser<'a> { }; // Try to parse the pattern `for ($PAT) in $EXPR`. let pat = match ( - self.parse_pat_allow_top_alt( + self.parse_pat_allow_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3230,7 +3230,7 @@ impl<'a> Parser<'a> { // then we should recover. let mut snapshot = this.create_snapshot_for_diagnostic(); let pattern_follows = snapshot - .parse_pat_allow_top_alt( + .parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3306,7 +3306,7 @@ impl<'a> Parser<'a> { if self.token == token::OpenDelim(Delimiter::Parenthesis) { // Detect and recover from `($pat if $cond) => $arm`. let left = self.token.span; - match self.parse_pat_allow_top_alt( + match self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3340,7 +3340,7 @@ impl<'a> Parser<'a> { } } else { // Regular parser flow: - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 43c3de90d9d99..ba97e1cf030ef 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -132,7 +132,7 @@ impl<'a> Parser<'a> { NonterminalKind::Pat(pat_kind) => { NtPat(self.collect_tokens_no_attrs(|this| match pat_kind { PatParam { .. } => this.parse_pat_no_top_alt(None, None), - PatWithOr => this.parse_pat_allow_top_alt( + PatWithOr => this.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 7f1140133202d..633365d260a66 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -97,9 +97,33 @@ pub enum PatternLocation { impl<'a> Parser<'a> { /// Parses a pattern. /// - /// Corresponds to `pat` in RFC 2535 and does not admit or-patterns - /// at the top level. Used when parsing the parameters of lambda expressions, - /// functions, function pointers, and `pat` macro fragments. + /// Corresponds to `Pattern` in RFC 3637 and admits guard patterns at the top level. + /// Used when parsing patterns in all cases where neither `PatternNoTopGuard` nor + /// `PatternNoTopAlt` (see below) are used. + pub fn parse_pat_allow_top_guard( + &mut self, + expected: Option, + rc: RecoverComma, + ra: RecoverColon, + rt: CommaRecoveryMode, + ) -> PResult<'a, P> { + let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?; + + if self.eat_keyword(kw::If) { + let cond = self.parse_expr()?; + let span = pat.span.to(cond.span); + self.psess.gated_spans.gate(sym::guard_patterns, span); + Ok(self.mk_pat(span, PatKind::Guard(pat, cond))) + } else { + Ok(pat) + } + } + + /// Parses a pattern. + /// + /// Corresponds to `PatternNoTopAlt` in RFC 3637 and does not admit or-patterns + /// or guard patterns at the top level. Used when parsing the parameters of lambda + /// expressions, functions, function pointers, and `pat_param` macro fragments. pub fn parse_pat_no_top_alt( &mut self, expected: Option, @@ -110,25 +134,26 @@ impl<'a> Parser<'a> { /// Parses a pattern. /// - /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level. - /// Used for parsing patterns in all cases when `pat` is not used. + /// Corresponds to `PatternNoTopGuard` in RFC 3637 and allows or-patterns, but not + /// guard patterns, at the top level. Used for parsing patterns in `pat` fragments (until + /// the next edition) and `let`, `if let`, and `while let` expressions. /// /// Note that after the FCP in , /// a leading vert is allowed in nested or-patterns, too. This allows us to /// simplify the grammar somewhat. - pub fn parse_pat_allow_top_alt( + pub fn parse_pat_no_top_guard( &mut self, expected: Option, rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, ) -> PResult<'a, P> { - self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) + self.parse_pat_no_top_guard_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) } /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true = /// recovered). - fn parse_pat_allow_top_alt_inner( + fn parse_pat_no_top_guard_inner( &mut self, expected: Option, rc: RecoverComma, @@ -229,7 +254,7 @@ impl<'a> Parser<'a> { // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level // or-patterns so that we can detect when a user tries to use it. This allows us to print a // better error message. - let (pat, trailing_vert) = self.parse_pat_allow_top_alt_inner( + let (pat, trailing_vert) = self.parse_pat_no_top_guard_inner( expected, rc, RecoverColon::No, @@ -694,7 +719,7 @@ impl<'a> Parser<'a> { } else if self.check(&token::OpenDelim(Delimiter::Bracket)) { // Parse `[pat, pat,...]` as a slice pattern. let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -942,7 +967,7 @@ impl<'a> Parser<'a> { let open_paren = self.token.span; let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1357,7 +1382,7 @@ impl<'a> Parser<'a> { path: Path, ) -> PResult<'a, PatKind> { let (fields, _) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1392,7 +1417,7 @@ impl<'a> Parser<'a> { self.parse_builtin(|self_, _lo, ident| { Ok(match ident.name { // builtin#deref(PAT) - sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt( + sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -1667,7 +1692,7 @@ impl<'a> Parser<'a> { // Parsing a pattern of the form `fieldname: pat`. let fieldname = self.parse_field_name()?; self.bump(); - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 2f19a9b6b20b4..6a7029a8f1c82 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -469,7 +469,7 @@ impl<'a> Parser<'a> { PathStyle::Pat if let Ok(_) = self .parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 27714a0fdcca0..e4cd5b5ca5441 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -296,6 +296,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { Deref, Ref, Lit, + Guard, Range, Slice, Err @@ -553,6 +554,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Slice, Rest, Never, + Guard, Paren, MacCall, Err diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 9ea5023064c1c..ac26a2293b71e 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -453,7 +453,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { let fields: Vec<_>; match &pat.kind { PatKind::AscribeUserType { subpattern, .. } - | PatKind::InlineConstant { subpattern, .. } => return self.lower_pat(subpattern), + | PatKind::InlineConstant { subpattern, .. } + | PatKind::Guard { subpattern, .. } => return self.lower_pat(subpattern), PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat), PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { ctor = Wildcard; diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index adb0ba7c82036..ea198e6d34b5e 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -758,7 +758,14 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r fn visit_pat(&mut self, p: &'ast Pat) { let prev = self.diag_metadata.current_pat; self.diag_metadata.current_pat = Some(p); - visit::walk_pat(self, p); + + match p.kind { + // We visit only the subpattern, allowing the condition to be resolved later in `resolve_pat`. + PatKind::Guard(ref subpat, _) => self.visit_pat(subpat), + // Otherwise, we just walk the pattern. + _ => visit::walk_pat(self, p), + } + self.diag_metadata.current_pat = prev; } fn visit_local(&mut self, local: &'ast Local) { @@ -2220,12 +2227,13 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let mut parameter_info = Vec::new(); let mut all_candidates = Vec::new(); + let top_rib_idx = self.ribs[ValueNS].len() - 1; let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; for (index, (pat, ty)) in inputs.enumerate() { debug!(?pat, ?ty); self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { if let Some(pat) = pat { - this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); + this.resolve_pattern(pat, PatternSource::FnParam, top_rib_idx, &mut bindings); } }); @@ -3483,6 +3491,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { Ident::new(kw::SelfLower, span), delegation.id, PatternSource::FnParam, + this.ribs[ValueNS].len() - 1, &mut bindings, ); this.visit_block(body); @@ -3491,10 +3500,11 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } fn resolve_params(&mut self, params: &'ast [Param]) { + let top_rib_idx = self.ribs[ValueNS].len() - 1; let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { for Param { pat, .. } in params { - this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); + this.resolve_pattern(pat, PatternSource::FnParam, top_rib_idx, &mut bindings); } }); for Param { ty, .. } in params { @@ -3712,20 +3722,22 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// Arising from `source`, resolve a top level pattern. fn resolve_pattern_top(&mut self, pat: &'ast Pat, pat_src: PatternSource) { let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - self.resolve_pattern(pat, pat_src, &mut bindings); + let top_rib_idx = self.ribs[ValueNS].len() - 1; + self.resolve_pattern(pat, pat_src, top_rib_idx, &mut bindings); } fn resolve_pattern( &mut self, pat: &'ast Pat, pat_src: PatternSource, + top_rib_idx: usize, bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet); 1]>, ) { // We walk the pattern before declaring the pattern's inner bindings, // so that we avoid resolving a literal expression to a binding defined // by the pattern. visit::walk_pat(self, pat); - self.resolve_pattern_inner(pat, pat_src, bindings); + self.resolve_pattern_inner(pat, pat_src, top_rib_idx, bindings); // This has to happen *after* we determine which pat_idents are variants: self.check_consistent_bindings(pat); } @@ -3751,8 +3763,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// See the implementation and `fresh_binding` for more details. fn resolve_pattern_inner( &mut self, - pat: &Pat, + pat: &'ast Pat, pat_src: PatternSource, + top_rib_idx: usize, bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet); 1]>, ) { // Visit all direct subpatterns of this pattern. @@ -3765,7 +3778,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let has_sub = sub.is_some(); let res = self .try_resolve_as_non_binding(pat_src, bmode, ident, has_sub) - .unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings)); + .unwrap_or_else(|| { + self.fresh_binding(ident, pat.id, pat_src, top_rib_idx, bindings) + }); self.r.record_partial_res(pat.id, PartialRes::new(res)); self.r.record_pat_span(pat.id, pat.span); } @@ -3796,7 +3811,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // part of the or-pattern internally rejects already bound names. // For example, `V1(a) | V2(a, a)` and `V1(a, a) | V2(a)` are bad. bindings.push((PatBoundCtx::Product, Default::default())); - self.resolve_pattern_inner(p, pat_src, bindings); + self.resolve_pattern_inner(p, pat_src, top_rib_idx, bindings); // Move up the non-overlapping bindings to the or-pattern. // Existing bindings just get "merged". let collected = bindings.pop().unwrap().1; @@ -3811,6 +3826,15 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // Prevent visiting `ps` as we've already done so above. return false; } + PatKind::Guard(ref subpat, ref cond) => { + self.with_rib(ValueNS, RibKind::Normal, |this| { + this.resolve_pattern_inner(subpat, pat_src, top_rib_idx, bindings); + this.resolve_expr(cond, None); + }); + + // Prevent visiting `pat` as we've already done so above. + return false; + } _ => {} } true @@ -3822,6 +3846,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ident: Ident, pat_id: NodeId, pat_src: PatternSource, + top_rib_idx: usize, bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet); 1]>, ) -> Res { // Add the binding to the local ribs, if it doesn't already exist in the bindings map. @@ -3854,18 +3879,23 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { bindings.last_mut().unwrap().1.insert(ident); } - if already_bound_or { + let res = if already_bound_or { // `Variant1(a) | Variant2(a)`, ok // Reuse definition from the first `a`. - self.innermost_rib_bindings(ValueNS)[&ident] + self.ribs[ValueNS][top_rib_idx].bindings[&ident] } else { let res = Res::Local(pat_id); if ident_valid { // A completely fresh binding add to the set if it's valid. - self.innermost_rib_bindings(ValueNS).insert(ident, res); + self.ribs[ValueNS][top_rib_idx].bindings.insert(ident, res); } res - } + }; + + // Record the binding in the innermost rib so guard expressions can use it. + self.innermost_rib_bindings(ValueNS).insert(ident, res); + + res } fn innermost_rib_bindings(&mut self, ns: Namespace) -> &mut IdentMap { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 890c4fdafef89..fbf7edebc22ba 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -984,6 +984,7 @@ symbols! { global_registration, globs, gt, + guard_patterns, half_open_range_patterns, half_open_range_patterns_in_slices, hash, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index d3a545fe0b61f..413a9282fab66 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -327,6 +327,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { ); return Symbol::intern("()"); } + PatKind::Guard(p, _) => return name_from_pat(&*p), PatKind::Range(..) => return kw::Underscore, PatKind::Slice(begin, ref mid, end) => { let begin = begin.iter().map(|p| name_from_pat(p).to_string()); diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index fb9f2b1526e38..9c8edfd6113f6 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -55,7 +55,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { | PatKind::Err(_) => false, PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), - PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) => unary_pattern(x), + PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), PatKind::Path(_) | PatKind::Lit(_) => true, } } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 20984bc40caf3..9185713261c0b 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -258,9 +258,11 @@ impl<'a> NormalizedPat<'a> { fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, - PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Deref(pat) | PatKind::Ref(pat, _) => { - Self::from_pat(cx, arena, pat) - }, + PatKind::Binding(.., Some(pat)) + | PatKind::Box(pat) + | PatKind::Deref(pat) + | PatKind::Ref(pat, _) + | PatKind::Guard(pat, _) => Self::from_pat(cx, arena, pat), PatKind::Never => Self::Never, PatKind::Struct(ref path, fields, _) => { let fields = diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 047d070a13153..3a0ffb6aa928e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -340,6 +340,10 @@ impl<'a> PatState<'a> { matches!(self, Self::Wild) }, + PatKind::Guard(..) => { + matches!(self, Self::Wild) + } + // Patterns for things which can only contain a single sub-pattern. PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => { self.add_pat(cx, pat) diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index c7c837de505e4..c649d5e5e1ee2 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -234,7 +234,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: us // In the case of only two patterns, replacement adds net characters. | Ref(_, Mutability::Not) // Dealt with elsewhere. - | Or(_) | Paren(_) | Deref(_) => false, + | Or(_) | Paren(_) | Deref(_) | Guard(..) => false, // Transform `box x | ... | box y` into `box (x | y)`. // // The cases below until `Slice(...)` deal with *singleton* products. diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 31f9d84f5e466..5736ab0c843e5 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -712,6 +712,12 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Ref({pat}, Mutability::{muta:?})"); self.pat(pat); }, + PatKind::Guard(pat, cond) => { + bind!(self, pat, cond); + kind!("Guard({pat}, {cond})"); + self.pat(pat); + self.expr(cond); + } PatKind::Lit(lit_expr) => { bind!(self, lit_expr); kind!("Lit({lit_expr})"); diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 181d414cbbded..fa1bd82f4f30d 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1088,6 +1088,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_pat(pat); std::mem::discriminant(&mu).hash(&mut self.s); }, + PatKind::Guard(pat, guard) => { + self.hash_pat(pat); + self.hash_expr(guard); + }, PatKind::Slice(l, m, r) => { for pat in l { self.hash_pat(pat); diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index ad85dfa2d1eb7..1cbc6793fefa3 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1780,7 +1780,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, } }, - PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) => true, + PatKind::Lit(..) | PatKind::Range(..) | PatKind::Guard(..) | PatKind::Err(_) => true, } } diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index 6fe2d4a8520e3..7bc699b07b0ce 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -48,7 +48,8 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool { | ast::PatKind::MacCall(..) | ast::PatKind::Slice(..) | ast::PatKind::Path(..) - | ast::PatKind::Range(..) => false, + | ast::PatKind::Range(..) + | ast::PatKind::Guard(..) => false, ast::PatKind::Tuple(ref subpats) => subpats.len() <= 1, ast::PatKind::TupleStruct(_, ref path, ref subpats) => { path.segments.len() <= 1 && subpats.len() <= 1 @@ -340,6 +341,7 @@ impl Rewrite for Pat { .map(|inner_pat| format!("({})", inner_pat)), PatKind::Err(_) => Err(RewriteError::Unknown), PatKind::Deref(_) => Err(RewriteError::Unknown), + PatKind::Guard(..) => Err(RewriteError::Unknown), } } } diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.rs b/tests/ui/feature-gates/feature-gate-guard-patterns.rs new file mode 100644 index 0000000000000..6ab050cc73c46 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.rs @@ -0,0 +1,39 @@ +// FIXME(guard_patterns): this lint should not be triggered +// once exhaustiveness is implemented correctly +#![allow(irrefutable_let_patterns)] + +fn match_guards_still_work() { + match 0 { + 0 if guard(0) => {}, + _ => {}, + } +} + +fn other_guards_dont() { + match 0 { + (0 if guard(0)) | 1 => {}, + //~^ ERROR: guard patterns are experimental + _ => {}, + } + + let ((x if guard(x)) | x) = 0; + //~^ ERROR: guard patterns are experimental + + if let (x if guard(x)) = 0 {} + //~^ ERROR: guard patterns are experimental + while let (x if guard(x)) = 0 {} + //~^ ERROR: guard patterns are experimental +} + +fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} +//~^ ERROR: guard patterns are experimental + +fn guard(x: T) -> bool { + unimplemented!() +} + +fn main() { + match_guards_still_work(); + other_guards_dont(); + even_as_function_parameters((0, 0)); +} diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr new file mode 100644 index 0000000000000..040e947132b04 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr @@ -0,0 +1,58 @@ +error[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:14:10 + | +LL | (0 if guard(0)) | 1 => {}, + | ^^^^^^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_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 + = help: consider using match arm guards + +error[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:19:11 + | +LL | let ((x if guard(x)) | x) = 0; + | ^^^^^^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_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 + = help: consider using match arm guards + +error[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:22:13 + | +LL | if let (x if guard(x)) = 0 {} + | ^^^^^^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_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 + = help: consider using match arm guards + +error[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:24:16 + | +LL | while let (x if guard(x)) = 0 {} + | ^^^^^^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_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 + = help: consider using match arm guards + +error[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:28:34 + | +LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} + | ^^^^^^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_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 + = help: consider using match arm guards + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/issues/issue-72373.rs b/tests/ui/parser/issues/issue-72373.rs index 4da6061c27fe8..ed88d53539bd4 100644 --- a/tests/ui/parser/issues/issue-72373.rs +++ b/tests/ui/parser/issues/issue-72373.rs @@ -3,7 +3,7 @@ fn foo(c: &[u32], n: u32) -> u32 { [h, ..] if h > n => 0, [h, ..] if h == n => 1, [h, ref ts..] => foo(c, n - h) + foo(ts, n), - //~^ ERROR expected one of `,`, `@`, `]`, or `|`, found `..` + //~^ ERROR expected one of `,`, `@`, `]`, `if`, or `|`, found `..` [] => 0, } } diff --git a/tests/ui/parser/issues/issue-72373.stderr b/tests/ui/parser/issues/issue-72373.stderr index c596c6abda553..d566d6f5fd138 100644 --- a/tests/ui/parser/issues/issue-72373.stderr +++ b/tests/ui/parser/issues/issue-72373.stderr @@ -1,8 +1,8 @@ -error: expected one of `,`, `@`, `]`, or `|`, found `..` +error: expected one of `,`, `@`, `]`, `if`, or `|`, found `..` --> $DIR/issue-72373.rs:5:19 | LL | [h, ref ts..] => foo(c, n - h) + foo(ts, n), - | ^^ expected one of `,`, `@`, `]`, or `|` + | ^^ expected one of `,`, `@`, `]`, `if`, or `|` | help: if you meant to bind the contents of the rest of the array pattern into `ts`, use `@` | diff --git a/tests/ui/parser/misspelled-keywords/ref.stderr b/tests/ui/parser/misspelled-keywords/ref.stderr index b8b52702314c8..398d9d6bb99b1 100644 --- a/tests/ui/parser/misspelled-keywords/ref.stderr +++ b/tests/ui/parser/misspelled-keywords/ref.stderr @@ -1,8 +1,8 @@ -error: expected one of `)`, `,`, `@`, or `|`, found `list` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `list` --> $DIR/ref.rs:4:19 | LL | Some(refe list) => println!("{list:?}"), - | ^^^^ expected one of `)`, `,`, `@`, or `|` + | ^^^^ expected one of `)`, `,`, `@`, `if`, or `|` | help: there is a keyword `ref` with a similar name | diff --git a/tests/ui/parser/pat-lt-bracket-7.rs b/tests/ui/parser/pat-lt-bracket-7.rs index 327aef5ad1570..abaeb4c83c05f 100644 --- a/tests/ui/parser/pat-lt-bracket-7.rs +++ b/tests/ui/parser/pat-lt-bracket-7.rs @@ -3,7 +3,7 @@ fn main() { let foo = core::iter::empty(); for Thing(x[]) in foo {} - //~^ ERROR: expected one of `)`, `,`, `@`, or `|`, found `[` + //~^ ERROR: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` } const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types diff --git a/tests/ui/parser/pat-lt-bracket-7.stderr b/tests/ui/parser/pat-lt-bracket-7.stderr index 004dcfb2a7b2d..cc457a4e64e24 100644 --- a/tests/ui/parser/pat-lt-bracket-7.stderr +++ b/tests/ui/parser/pat-lt-bracket-7.stderr @@ -1,10 +1,10 @@ -error: expected one of `)`, `,`, `@`, or `|`, found `[` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` --> $DIR/pat-lt-bracket-7.rs:5:16 | LL | for Thing(x[]) in foo {} | ^ | | - | expected one of `)`, `,`, `@`, or `|` + | expected one of `)`, `,`, `@`, `if`, or `|` | help: missing `,` error[E0308]: mismatched types diff --git a/tests/ui/parser/recover/recover-parens-around-match-arm-head.fixed b/tests/ui/parser/recover/recover-parens-around-match-arm-head.fixed deleted file mode 100644 index 7e0194d5115bb..0000000000000 --- a/tests/ui/parser/recover/recover-parens-around-match-arm-head.fixed +++ /dev/null @@ -1,12 +0,0 @@ -//@ run-rustfix -fn main() { - let val = 42; - let x = match val { - 0 if true => { - //~^ ERROR unexpected parentheses surrounding `match` arm pattern - 42u8 - } - _ => 0u8, - }; - let _y: u32 = x.into(); //~ ERROR mismatched types -} diff --git a/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs b/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs deleted file mode 100644 index d208bc7150d6f..0000000000000 --- a/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ run-rustfix -fn main() { - let val = 42; - let x = match val { - (0 if true) => { - //~^ ERROR unexpected parentheses surrounding `match` arm pattern - 42u8 - } - _ => 0u8, - }; - let _y: u32 = x; //~ ERROR mismatched types -} diff --git a/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr b/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr deleted file mode 100644 index bad4d7d2f19f5..0000000000000 --- a/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: unexpected parentheses surrounding `match` arm pattern - --> $DIR/recover-parens-around-match-arm-head.rs:5:9 - | -LL | (0 if true) => { - | ^ ^ - | -help: remove parentheses surrounding the pattern - | -LL - (0 if true) => { -LL + 0 if true => { - | - -error[E0308]: mismatched types - --> $DIR/recover-parens-around-match-arm-head.rs:11:19 - | -LL | let _y: u32 = x; - | --- ^ expected `u32`, found `u8` - | | - | expected due to this - | -help: you can convert a `u8` to a `u32` - | -LL | let _y: u32 = x.into(); - | +++++++ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/recover/recover-pat-exprs.rs b/tests/ui/parser/recover/recover-pat-exprs.rs index e5e25df0c01cc..a78bb82828d3a 100644 --- a/tests/ui/parser/recover/recover-pat-exprs.rs +++ b/tests/ui/parser/recover/recover-pat-exprs.rs @@ -27,7 +27,7 @@ fn array_indexing() { { let x[0, 1, 2]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` { let x[0; 20]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` { let x[]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` - { let (x[]); } //~ error: expected one of `)`, `,`, `@`, or `|`, found `[` + { let (x[]); } //~ error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` //~^ missing `,` } @@ -95,12 +95,12 @@ fn main() { f?() => (), //~^ error: expected a pattern, found an expression (_ + 1) => (), - //~^ error: expected one of `)`, `,`, or `|`, found `+` + //~^ error: expected one of `)`, `,`, `if`, or `|`, found `+` } let 1 + 1 = 2; //~^ error: expected a pattern, found an expression let b = matches!(x, (x * x | x.f()) | x[0]); - //~^ error: expected one of `)`, `,`, `@`, or `|`, found `*` + //~^ error: expected one of `)`, `,`, `@`, `if`, or `|`, found `*` } diff --git a/tests/ui/parser/recover/recover-pat-exprs.stderr b/tests/ui/parser/recover/recover-pat-exprs.stderr index 6cb3753de8d17..20dc99b3d01e4 100644 --- a/tests/ui/parser/recover/recover-pat-exprs.stderr +++ b/tests/ui/parser/recover/recover-pat-exprs.stderr @@ -213,13 +213,13 @@ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` LL | { let x[]; } | ^ expected one of `:`, `;`, `=`, `@`, or `|` -error: expected one of `)`, `,`, `@`, or `|`, found `[` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` --> $DIR/recover-pat-exprs.rs:30:13 | LL | { let (x[]); } | ^ | | - | expected one of `)`, `,`, `@`, or `|` + | expected one of `)`, `,`, `@`, `if`, or `|` | help: missing `,` error: expected a pattern, found an expression @@ -611,11 +611,11 @@ LL | x.sqrt() @ .. => (), | = note: bindings are `x`, `mut x`, `ref x`, and `ref mut x` -error: expected one of `)`, `,`, or `|`, found `+` +error: expected one of `)`, `,`, `if`, or `|`, found `+` --> $DIR/recover-pat-exprs.rs:97:12 | LL | (_ + 1) => (), - | ^ expected one of `)`, `,`, or `|` + | ^ expected one of `)`, `,`, `if`, or `|` error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:81:9 @@ -772,11 +772,11 @@ LL | let 1 + 1 = 2; | = note: arbitrary expressions are not allowed in patterns: -error: expected one of `)`, `,`, `@`, or `|`, found `*` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `*` --> $DIR/recover-pat-exprs.rs:104:28 | LL | let b = matches!(x, (x * x | x.f()) | x[0]); - | ^ expected one of `)`, `,`, `@`, or `|` + | ^ expected one of `)`, `,`, `@`, `if`, or `|` --> $SRC_DIR/core/src/macros/mod.rs:LL:COL | = note: while parsing argument for this `pat` macro fragment diff --git a/tests/ui/parser/recover/recover-pat-wildcards.rs b/tests/ui/parser/recover/recover-pat-wildcards.rs index f506e2223d608..d4d28ce63587a 100644 --- a/tests/ui/parser/recover/recover-pat-wildcards.rs +++ b/tests/ui/parser/recover/recover-pat-wildcards.rs @@ -8,7 +8,7 @@ fn a() { fn b() { match 2 { - (_ % 4) => () //~ error: expected one of `)`, `,`, or `|`, found `%` + (_ % 4) => () //~ error: expected one of `)`, `,`, `if`, or `|`, found `%` } } @@ -40,7 +40,7 @@ fn f() { fn g() { match 7 { - (_ * 0)..5 => () //~ error: expected one of `)`, `,`, or `|`, found `*` + (_ * 0)..5 => () //~ error: expected one of `)`, `,`, `if`, or `|`, found `*` } } diff --git a/tests/ui/parser/recover/recover-pat-wildcards.stderr b/tests/ui/parser/recover/recover-pat-wildcards.stderr index 8d4212ed389dd..15413f478c305 100644 --- a/tests/ui/parser/recover/recover-pat-wildcards.stderr +++ b/tests/ui/parser/recover/recover-pat-wildcards.stderr @@ -4,11 +4,11 @@ error: expected one of `=>`, `if`, or `|`, found `+` LL | _ + 1 => () | ^ expected one of `=>`, `if`, or `|` -error: expected one of `)`, `,`, or `|`, found `%` +error: expected one of `)`, `,`, `if`, or `|`, found `%` --> $DIR/recover-pat-wildcards.rs:11:12 | LL | (_ % 4) => () - | ^ expected one of `)`, `,`, or `|` + | ^ expected one of `)`, `,`, `if`, or `|` error: expected one of `=>`, `if`, or `|`, found `.` --> $DIR/recover-pat-wildcards.rs:17:10 @@ -47,11 +47,11 @@ error: expected one of `=>`, `if`, or `|`, found reserved identifier `_` LL | 0..._ => () | ^ expected one of `=>`, `if`, or `|` -error: expected one of `)`, `,`, or `|`, found `*` +error: expected one of `)`, `,`, `if`, or `|`, found `*` --> $DIR/recover-pat-wildcards.rs:43:12 | LL | (_ * 0)..5 => () - | ^ expected one of `)`, `,`, or `|` + | ^ expected one of `)`, `,`, `if`, or `|` error: expected one of `=>`, `if`, or `|`, found `(` --> $DIR/recover-pat-wildcards.rs:49:11 diff --git a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr index da8f4ca5f0cd7..6ce8f6d31a031 100644 --- a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr +++ b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr @@ -6,11 +6,11 @@ LL | let a: u8 @ b = 0; | | | while parsing the type for `a` -error: expected one of `)`, `,`, `@`, or `|`, found `:` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `:` --> $DIR/nested-type-ascription-syntactically-invalid.rs:24:15 | LL | let a @ (b: u8); - | ^ expected one of `)`, `,`, `@`, or `|` + | ^ expected one of `)`, `,`, `@`, `if`, or `|` | = note: type ascription syntax has been removed, see issue #101728