Skip to content

Commit 458bae5

Browse files
committed
Enforce a whitelist on the constant primitive types allowed in patterns.
1 parent 4015890 commit 458bae5

File tree

7 files changed

+106
-97
lines changed

7 files changed

+106
-97
lines changed

src/librustc_mir_build/hair/pattern/const_to_pat.rs

+50-14
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,24 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
101101
cv.ty, structural
102102
);
103103
if let Some(non_sm_ty) = structural {
104-
let adt_def = match non_sm_ty {
105-
traits::NonStructuralMatchTy::Adt(adt_def) => adt_def,
104+
let msg = match non_sm_ty {
105+
traits::NonStructuralMatchTy::Adt(adt_def) => {
106+
let path = self.tcx().def_path_str(adt_def.did);
107+
format!(
108+
"to use a constant of type `{}` in a pattern, \
109+
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
110+
path, path,
111+
)
112+
}
106113
traits::NonStructuralMatchTy::Param => {
107114
bug!("use of constant whose type is a parameter inside a pattern")
108115
}
109-
};
110-
let path = self.tcx().def_path_str(adt_def.did);
111-
112-
let make_msg = || -> String {
113-
format!(
114-
"to use a constant of type `{}` in a pattern, \
115-
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
116-
path, path,
117-
)
116+
traits::NonStructuralMatchTy::FnPtr => {
117+
format!("`fn` pointers cannot be used in a patterns")
118+
}
119+
traits::NonStructuralMatchTy::RawPtr => {
120+
format!("raw pointers cannot be used in a patterns")
121+
}
118122
};
119123

120124
// double-check there even *is* a semantic `PartialEq` to dispatch to.
@@ -145,13 +149,13 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
145149

146150
if !ty_is_partial_eq {
147151
// span_fatal avoids ICE from resolution of non-existent method (rare case).
148-
self.tcx().sess.span_fatal(self.span, &make_msg());
152+
self.tcx().sess.span_fatal(self.span, &msg);
149153
} else {
150154
self.tcx().struct_span_lint_hir(
151155
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
152156
self.id,
153157
self.span,
154-
|lint| lint.build(&make_msg()).emit(),
158+
|lint| lint.build(&msg).emit(),
155159
);
156160
}
157161
}
@@ -257,7 +261,39 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
257261
slice: None,
258262
suffix: Vec::new(),
259263
},
260-
_ => PatKind::Constant { value: cv },
264+
_ => {
265+
let mut leaf_ty = cv.ty;
266+
267+
// HACK(eddyb) workaround missing reference destructuring.
268+
loop {
269+
leaf_ty = match leaf_ty.kind {
270+
ty::Ref(_, pointee_ty, _) => pointee_ty,
271+
272+
// HACK(eddyb) even worse, these show up *behind*
273+
// references, so despite being supported above we have
274+
// to reimplement them here (and we can't really do the
275+
// same for nested tuples or ADTs...).
276+
ty::Array(elem_ty, _) | ty::Slice(elem_ty) => elem_ty,
277+
278+
_ => break,
279+
};
280+
}
281+
282+
match leaf_ty.kind {
283+
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::FnDef(..) => {
284+
PatKind::Constant { value: cv }
285+
}
286+
287+
_ => {
288+
debug!("non-structural leaf constant type {:?}", leaf_ty);
289+
let msg =
290+
format!("constants of type `{}` cannot be used in a pattern", leaf_ty);
291+
self.saw_const_match_error.set(true);
292+
tcx.sess.span_err(span, &msg);
293+
PatKind::Wild
294+
}
295+
}
296+
}
261297
};
262298

263299
Pat { span, ty: cv.ty, kind: Box::new(kind) }

src/librustc_trait_selection/traits/structural_match.rs

+11-19
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use rustc_span::Span;
1111
pub enum NonStructuralMatchTy<'tcx> {
1212
Adt(&'tcx AdtDef),
1313
Param,
14+
FnPtr,
15+
RawPtr,
1416
}
1517

1618
/// This method traverses the structure of `ty`, trying to find an
@@ -138,26 +140,16 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
138140
return true; // Stop visiting.
139141
}
140142
ty::RawPtr(..) => {
141-
// structural-match ignores substructure of
142-
// `*const _`/`*mut _`, so skip `super_visit_with`.
143-
//
144-
// For example, if you have:
145-
// ```
146-
// struct NonStructural;
147-
// #[derive(PartialEq, Eq)]
148-
// struct T(*const NonStructural);
149-
// const C: T = T(std::ptr::null());
150-
// ```
151-
//
152-
// Even though `NonStructural` does not implement `PartialEq`,
153-
// structural equality on `T` does not recur into the raw
154-
// pointer. Therefore, one can still use `C` in a pattern.
155-
156-
// (But still tell caller to continue search.)
157-
return false;
143+
self.found = Some(NonStructuralMatchTy::RawPtr);
144+
return true; // Stop visiting.
145+
}
146+
ty::FnPtr(..) => {
147+
self.found = Some(NonStructuralMatchTy::FnPtr);
148+
return true; // Stop visiting.
158149
}
159-
ty::FnDef(..) | ty::FnPtr(..) => {
160-
// types of formals and return in `fn(_) -> _` are also irrelevant;
150+
ty::FnDef(..) => {
151+
// substs of ZST function (*not* `fn(...) -> _` pointers!) types
152+
// are also irrelevant;
161153
// so we do not recur into them via `super_visit_with`
162154
//
163155
// (But still tell caller to continue search.)

src/test/ui/const-generics/fn-const-param-infer.stderr

+6-38
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,12 @@ LL | #![feature(const_generics, const_compare_raw_pointers)]
66
|
77
= note: `#[warn(incomplete_features)]` on by default
88

9-
error[E0308]: mismatched types
10-
--> $DIR/fn-const-param-infer.rs:16:31
9+
error[E0741]: the types of const generic parameters must derive `PartialEq` and `Eq`
10+
--> $DIR/fn-const-param-infer.rs:4:25
1111
|
12-
LL | let _: Checked<not_one> = Checked::<not_two>;
13-
| ---------------- ^^^^^^^^^^^^^^^^^^ expected `{not_one as fn(usize) -> bool}`, found `{not_two as fn(usize) -> bool}`
14-
| |
15-
| expected due to this
16-
|
17-
= note: expected struct `Checked<{not_one as fn(usize) -> bool}>`
18-
found struct `Checked<{not_two as fn(usize) -> bool}>`
19-
20-
error[E0308]: mismatched types
21-
--> $DIR/fn-const-param-infer.rs:20:24
22-
|
23-
LL | let _ = Checked::<{generic_arg::<u32>}>;
24-
| ^^^^^^^^^^^^^^^^^^ expected `usize`, found `u32`
25-
|
26-
= note: expected fn pointer `fn(usize) -> _`
27-
found fn item `fn(u32) -> _ {generic_arg::<u32>}`
28-
29-
error[E0282]: type annotations needed
30-
--> $DIR/fn-const-param-infer.rs:22:23
31-
|
32-
LL | let _ = Checked::<generic>;
33-
| ^^^^^^^ cannot infer type for type parameter `T` declared on the function `generic`
34-
35-
error[E0308]: mismatched types
36-
--> $DIR/fn-const-param-infer.rs:25:40
37-
|
38-
LL | let _: Checked<{generic::<u32>}> = Checked::<{generic::<u16>}>;
39-
| ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{generic::<u32> as fn(usize) -> bool}`, found `{generic::<u16> as fn(usize) -> bool}`
40-
| |
41-
| expected due to this
42-
|
43-
= note: expected struct `Checked<{generic::<u32> as fn(usize) -> bool}>`
44-
found struct `Checked<{generic::<u16> as fn(usize) -> bool}>`
12+
LL | struct Checked<const F: fn(usize) -> bool>;
13+
| ^^^^^^^^^^^^^^^^^ `fn(usize) -> bool` doesn't derive both `PartialEq` and `Eq`
4514

46-
error: aborting due to 4 previous errors
15+
error: aborting due to previous error
4716

48-
Some errors have detailed explanations: E0282, E0308.
49-
For more information about an error, try `rustc --explain E0282`.
17+
For more information about this error, try `rustc --explain E0741`.

src/test/ui/const-generics/raw-ptr-const-param.stderr

+5-10
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,12 @@ LL | #![feature(const_generics, const_compare_raw_pointers)]
66
|
77
= note: `#[warn(incomplete_features)]` on by default
88

9-
error[E0308]: mismatched types
10-
--> $DIR/raw-ptr-const-param.rs:7:40
9+
error[E0741]: the types of const generic parameters must derive `PartialEq` and `Eq`
10+
--> $DIR/raw-ptr-const-param.rs:4:23
1111
|
12-
LL | let _: Const<{ 15 as *const _ }> = Const::<{ 10 as *const _ }>;
13-
| ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{0xf as *const u32}`, found `{0xa as *const u32}`
14-
| |
15-
| expected due to this
16-
|
17-
= note: expected struct `Const<{0xf as *const u32}>`
18-
found struct `Const<{0xa as *const u32}>`
12+
LL | struct Const<const P: *const u32>;
13+
| ^^^^^^^^^^ `*const u32` doesn't derive both `PartialEq` and `Eq`
1914

2015
error: aborting due to previous error
2116

22-
For more information about this error, try `rustc --explain E0308`.
17+
For more information about this error, try `rustc --explain E0741`.

src/test/ui/consts/match_ice.stderr

+11-12
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,23 @@ error: to use a constant of type `S` in a pattern, `S` must be annotated with `#
44
LL | C => {}
55
| ^
66

7-
error[E0004]: non-exhaustive patterns: `&T` not covered
8-
--> $DIR/match_ice.rs:16:11
7+
error: constants of type `T` cannot be used in a pattern
8+
--> $DIR/match_ice.rs:17:9
99
|
10-
LL | struct T;
11-
| --------- `T` defined here
12-
...
13-
LL | match K {
14-
| ^ pattern `&T` not covered
15-
|
16-
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
17-
= note: the matched value is of type `&T`
10+
LL | K => {}
11+
| ^
1812

1913
error: to use a constant of type `S` in a pattern, `S` must be annotated with `#[derive(PartialEq, Eq)]`
2014
--> $DIR/match_ice.rs:11:9
2115
|
2216
LL | C => {}
2317
| ^
2418

25-
error: aborting due to 3 previous errors
19+
error: constants of type `T` cannot be used in a pattern
20+
--> $DIR/match_ice.rs:17:9
21+
|
22+
LL | K => {}
23+
| ^
24+
25+
error: aborting due to 4 previous errors
2626

27-
For more information about this error, try `rustc --explain E0004`.

src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr

+15-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ LL | struct ConstFn<const F: fn()>;
2525
= note: see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information
2626
= help: add `#![feature(const_compare_raw_pointers)]` to the crate attributes to enable
2727

28+
error[E0741]: the types of const generic parameters must derive `PartialEq` and `Eq`
29+
--> $DIR/feature-gate-const_generics-ptr.rs:1:25
30+
|
31+
LL | struct ConstFn<const F: fn()>;
32+
| ^^^^ `fn()` doesn't derive both `PartialEq` and `Eq`
33+
2834
error[E0658]: using raw pointers as const generic parameters is unstable
2935
--> $DIR/feature-gate-const_generics-ptr.rs:5:26
3036
|
@@ -34,6 +40,13 @@ LL | struct ConstPtr<const P: *const u32>;
3440
= note: see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information
3541
= help: add `#![feature(const_compare_raw_pointers)]` to the crate attributes to enable
3642

37-
error: aborting due to 4 previous errors
43+
error[E0741]: the types of const generic parameters must derive `PartialEq` and `Eq`
44+
--> $DIR/feature-gate-const_generics-ptr.rs:5:26
45+
|
46+
LL | struct ConstPtr<const P: *const u32>;
47+
| ^^^^^^^^^^ `*const u32` doesn't derive both `PartialEq` and `Eq`
48+
49+
error: aborting due to 6 previous errors
3850

39-
For more information about this error, try `rustc --explain E0658`.
51+
Some errors have detailed explanations: E0658, E0741.
52+
For more information about an error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
error: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq, Eq)]`
1+
error: constants of type `B` cannot be used in a pattern
22
--> $DIR/issue-61188-match-slice-forbidden-without-eq.rs:15:9
33
|
44
LL | A => (),
55
| ^
66

7-
error: aborting due to previous error
7+
error: constants of type `B` cannot be used in a pattern
8+
--> $DIR/issue-61188-match-slice-forbidden-without-eq.rs:15:9
9+
|
10+
LL | A => (),
11+
| ^
12+
13+
error: aborting due to 2 previous errors
814

0 commit comments

Comments
 (0)