Skip to content

Commit d367c34

Browse files
committed
Auto merge of rust-lang#92080 - Aaron1011:pattern-ice, r=cjgillot
Move `PatKind::Lit` checking from ast_validation to ast lowering Fixes rust-lang#92074 This allows us to insert an `ExprKind::Err` when an invalid expression is used in a literal pattern, preventing later stages of compilation from seeing an unexpected literal pattern.
2 parents b5da808 + 137c374 commit d367c34

File tree

5 files changed

+109
-54
lines changed

5 files changed

+109
-54
lines changed

compiler/rustc_ast_lowering/src/pat.rs

+34-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
2424
let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s));
2525
break self.lower_pat_ident(pattern, binding_mode, ident, lower_sub);
2626
}
27-
PatKind::Lit(ref e) => break hir::PatKind::Lit(self.lower_expr(e)),
27+
PatKind::Lit(ref e) => {
28+
break hir::PatKind::Lit(self.lower_expr_within_pat(e, false));
29+
}
2830
PatKind::TupleStruct(ref qself, ref path, ref pats) => {
2931
let qpath = self.lower_qpath(
3032
pattern.id,
@@ -81,8 +83,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
8183
}
8284
PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => {
8385
break hir::PatKind::Range(
84-
e1.as_deref().map(|e| self.lower_expr(e)),
85-
e2.as_deref().map(|e| self.lower_expr(e)),
86+
e1.as_deref().map(|e| self.lower_expr_within_pat(e, true)),
87+
e2.as_deref().map(|e| self.lower_expr_within_pat(e, true)),
8688
self.lower_range_end(end, e2.is_some()),
8789
);
8890
}
@@ -314,4 +316,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
314316
RangeEnd::Excluded | RangeEnd::Included(_) => hir::RangeEnd::Included,
315317
}
316318
}
319+
320+
/// Matches `'-' lit | lit (cf. parser::Parser::parse_literal_maybe_minus)`,
321+
/// or paths for ranges.
322+
//
323+
// FIXME: do we want to allow `expr -> pattern` conversion to create path expressions?
324+
// That means making this work:
325+
//
326+
// ```rust,ignore (FIXME)
327+
// struct S;
328+
// macro_rules! m {
329+
// ($a:expr) => {
330+
// let $a = S;
331+
// }
332+
// }
333+
// m!(S);
334+
// ```
335+
fn lower_expr_within_pat(&mut self, expr: &Expr, allow_paths: bool) -> &'hir hir::Expr<'hir> {
336+
match expr.kind {
337+
ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Err => {}
338+
ExprKind::Path(..) if allow_paths => {}
339+
ExprKind::Unary(UnOp::Neg, ref inner) if matches!(inner.kind, ExprKind::Lit(_)) => {}
340+
_ => {
341+
self.diagnostic()
342+
.span_err(expr.span, "arbitrary expressions aren't allowed in patterns");
343+
return self.arena.alloc(self.expr_err(expr.span));
344+
}
345+
}
346+
self.lower_expr(expr)
347+
}
317348
}

compiler/rustc_ast_passes/src/ast_validation.rs

-47
Original file line numberDiff line numberDiff line change
@@ -302,34 +302,6 @@ impl<'a> AstValidator<'a> {
302302
}
303303
}
304304

305-
/// Matches `'-' lit | lit (cf. parser::Parser::parse_literal_maybe_minus)`,
306-
/// or paths for ranges.
307-
//
308-
// FIXME: do we want to allow `expr -> pattern` conversion to create path expressions?
309-
// That means making this work:
310-
//
311-
// ```rust,ignore (FIXME)
312-
// struct S;
313-
// macro_rules! m {
314-
// ($a:expr) => {
315-
// let $a = S;
316-
// }
317-
// }
318-
// m!(S);
319-
// ```
320-
fn check_expr_within_pat(&self, expr: &Expr, allow_paths: bool) {
321-
match expr.kind {
322-
ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Err => {}
323-
ExprKind::Path(..) if allow_paths => {}
324-
ExprKind::Unary(UnOp::Neg, ref inner) if matches!(inner.kind, ExprKind::Lit(_)) => {}
325-
_ => self.err_handler().span_err(
326-
expr.span,
327-
"arbitrary expressions aren't allowed \
328-
in patterns",
329-
),
330-
}
331-
}
332-
333305
fn check_late_bound_lifetime_defs(&self, params: &[GenericParam]) {
334306
// Check only lifetime parameters are present and that the lifetime
335307
// parameters that are present have no bounds.
@@ -1426,25 +1398,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
14261398
visit::walk_param_bound(self, bound)
14271399
}
14281400

1429-
fn visit_pat(&mut self, pat: &'a Pat) {
1430-
match &pat.kind {
1431-
PatKind::Lit(expr) => {
1432-
self.check_expr_within_pat(expr, false);
1433-
}
1434-
PatKind::Range(start, end, _) => {
1435-
if let Some(expr) = start {
1436-
self.check_expr_within_pat(expr, true);
1437-
}
1438-
if let Some(expr) = end {
1439-
self.check_expr_within_pat(expr, true);
1440-
}
1441-
}
1442-
_ => {}
1443-
}
1444-
1445-
visit::walk_pat(self, pat)
1446-
}
1447-
14481401
fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef, m: &'a TraitBoundModifier) {
14491402
self.check_late_bound_lifetime_defs(&t.bound_generic_params);
14501403
visit::walk_poly_trait_ref(self, t, m);

src/test/ui/match/expr_before_ident_pat.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
error: arbitrary expressions aren't allowed in patterns
1+
error[E0425]: cannot find value `a` in this scope
22
--> $DIR/expr_before_ident_pat.rs:12:12
33
|
44
LL | funny!(a, a);
5-
| ^
5+
| ^ not found in this scope
66

7-
error[E0425]: cannot find value `a` in this scope
7+
error: arbitrary expressions aren't allowed in patterns
88
--> $DIR/expr_before_ident_pat.rs:12:12
99
|
1010
LL | funny!(a, a);
11-
| ^ not found in this scope
11+
| ^
1212

1313
error: aborting due to 2 previous errors
1414

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
pub enum En {
2+
A(Vec<u8>)
3+
}
4+
5+
fn get_usize() -> usize {
6+
0
7+
}
8+
9+
macro_rules! force_expr {
10+
($e:expr) => { $e }
11+
}
12+
13+
macro_rules! force_pat {
14+
($a:expr, $b:expr) => { $a..=$b }
15+
}
16+
17+
macro_rules! make_vec {
18+
() => { force_expr!(Vec::new()) } //~ ERROR arbitrary expressions aren't allowed
19+
}
20+
21+
macro_rules! make_pat {
22+
() => { force_pat!(get_usize(), get_usize()) }
23+
//~^ ERROR arbitrary expressions aren't allowed
24+
//~| ERROR arbitrary expressions aren't allowed
25+
}
26+
27+
#[allow(unreachable_code)]
28+
fn f() -> Result<(), impl core::fmt::Debug> {
29+
let x: En = loop {};
30+
31+
assert!(matches!(x, En::A(make_vec!())));
32+
assert!(matches!(5, make_pat!()));
33+
Ok::<(), &'static str>(())
34+
}
35+
36+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error: arbitrary expressions aren't allowed in patterns
2+
--> $DIR/issue-92074-macro-ice.rs:18:25
3+
|
4+
LL | () => { force_expr!(Vec::new()) }
5+
| ^^^^^^^^^^
6+
...
7+
LL | assert!(matches!(x, En::A(make_vec!())));
8+
| ----------- in this macro invocation
9+
|
10+
= note: this error originates in the macro `make_vec` (in Nightly builds, run with -Z macro-backtrace for more info)
11+
12+
error: arbitrary expressions aren't allowed in patterns
13+
--> $DIR/issue-92074-macro-ice.rs:22:24
14+
|
15+
LL | () => { force_pat!(get_usize(), get_usize()) }
16+
| ^^^^^^^^^^^
17+
...
18+
LL | assert!(matches!(5, make_pat!()));
19+
| ----------- in this macro invocation
20+
|
21+
= note: this error originates in the macro `make_pat` (in Nightly builds, run with -Z macro-backtrace for more info)
22+
23+
error: arbitrary expressions aren't allowed in patterns
24+
--> $DIR/issue-92074-macro-ice.rs:22:37
25+
|
26+
LL | () => { force_pat!(get_usize(), get_usize()) }
27+
| ^^^^^^^^^^^
28+
...
29+
LL | assert!(matches!(5, make_pat!()));
30+
| ----------- in this macro invocation
31+
|
32+
= note: this error originates in the macro `make_pat` (in Nightly builds, run with -Z macro-backtrace for more info)
33+
34+
error: aborting due to 3 previous errors
35+

0 commit comments

Comments
 (0)