Skip to content

Commit 1611ed8

Browse files
authored
Rollup merge of rust-lang#72153 - lcnr:exhaustively-match, r=pnkfelix
exhaustively check `ty::Kind` during structural match checking This was prone to errors as we may forget new kinds in the future. I am also not yet sure about some kinds. `ty::GeneratorWitness(..) | ty::Infer(_) | ty::Placeholder(_) | ty::UnnormalizedProjection(..) | ty::Bound(..)` might be unreachable here. We may want to forbid `ty::Projection`, similar to `ty::Param`. `ty::Opaque` seems fine afaict, should not be possible in a match atm. I believe `ty::Foreign` should not be structurally match, as I don't even know what that would actually mean. r? @pnkfelix cc @eddyb
2 parents 7a9e286 + 60d9df2 commit 1611ed8

File tree

6 files changed

+122
-6
lines changed

6 files changed

+122
-6
lines changed

src/librustc_mir_build/hair/pattern/const_to_pat.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,20 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
124124
traits::NonStructuralMatchTy::Dynamic => {
125125
"trait objects cannot be used in patterns".to_string()
126126
}
127+
traits::NonStructuralMatchTy::Opaque => {
128+
"opaque types cannot be used in patterns".to_string()
129+
}
130+
traits::NonStructuralMatchTy::Generator => {
131+
"generators cannot be used in patterns".to_string()
132+
}
127133
traits::NonStructuralMatchTy::Param => {
128-
bug!("use of constant whose type is a parameter inside a pattern")
134+
bug!("use of a constant whose type is a parameter inside a pattern")
135+
}
136+
traits::NonStructuralMatchTy::Projection => {
137+
bug!("use of a constant whose type is a projection inside a pattern")
138+
}
139+
traits::NonStructuralMatchTy::Foreign => {
140+
bug!("use of a value of a foreign type inside a pattern")
129141
}
130142
};
131143

src/librustc_trait_selection/traits/structural_match.rs

+52-5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ pub enum NonStructuralMatchTy<'tcx> {
1313
Adt(&'tcx AdtDef),
1414
Param,
1515
Dynamic,
16+
Foreign,
17+
Opaque,
18+
Generator,
19+
Projection,
1620
}
1721

1822
/// This method traverses the structure of `ty`, trying to find an
@@ -143,6 +147,22 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
143147
self.found = Some(NonStructuralMatchTy::Dynamic);
144148
return true; // Stop visiting.
145149
}
150+
ty::Foreign(_) => {
151+
self.found = Some(NonStructuralMatchTy::Foreign);
152+
return true; // Stop visiting.
153+
}
154+
ty::Opaque(..) => {
155+
self.found = Some(NonStructuralMatchTy::Opaque);
156+
return true; // Stop visiting.
157+
}
158+
ty::Projection(..) => {
159+
self.found = Some(NonStructuralMatchTy::Projection);
160+
return true; // Stop visiting.
161+
}
162+
ty::Generator(..) | ty::GeneratorWitness(..) => {
163+
self.found = Some(NonStructuralMatchTy::Generator);
164+
return true; // Stop visiting.
165+
}
146166
ty::RawPtr(..) => {
147167
// structural-match ignores substructure of
148168
// `*const _`/`*mut _`, so skip `super_visit_with`.
@@ -159,32 +179,59 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
159179
// structural equality on `T` does not recur into the raw
160180
// pointer. Therefore, one can still use `C` in a pattern.
161181

162-
// (But still tell caller to continue search.)
182+
// (But still tell the caller to continue search.)
163183
return false;
164184
}
165185
ty::FnDef(..) | ty::FnPtr(..) => {
166-
// types of formals and return in `fn(_) -> _` are also irrelevant;
186+
// Types of formals and return in `fn(_) -> _` are also irrelevant;
167187
// so we do not recur into them via `super_visit_with`
168188
//
169-
// (But still tell caller to continue search.)
189+
// (But still tell the caller to continue search.)
170190
return false;
171191
}
172192
ty::Array(_, n)
173193
if { n.try_eval_usize(self.tcx(), ty::ParamEnv::reveal_all()) == Some(0) } =>
174194
{
175195
// rust-lang/rust#62336: ignore type of contents
176196
// for empty array.
197+
//
198+
// (But still tell the caller to continue search.)
177199
return false;
178200
}
179-
_ => {
201+
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => {
202+
// These primitive types are always structural match.
203+
//
204+
// `Never` is kind of special here, but as it is not inhabitable, this should be fine.
205+
//
206+
// (But still tell the caller to continue search.)
207+
return false;
208+
}
209+
210+
ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => {
211+
// First check all contained types and then tell the caller to continue searching.
180212
ty.super_visit_with(self);
181213
return false;
182214
}
215+
ty::Closure(..)
216+
| ty::Infer(_)
217+
| ty::Placeholder(_)
218+
| ty::UnnormalizedProjection(..)
219+
| ty::Bound(..) => {
220+
bug!("unexpected type during structural-match checking: {:?}", ty);
221+
}
222+
ty::Error => {
223+
self.tcx().sess.delay_span_bug(self.span, "ty::Error in structural-match check");
224+
// We still want to check other types after encountering an error,
225+
// as this may still emit relevant errors.
226+
//
227+
// So we continue searching here.
228+
return false;
229+
}
183230
};
184231

185232
if !self.seen.insert(adt_def.did) {
186233
debug!("Search already seen adt_def: {:?}", adt_def);
187-
// let caller continue its search
234+
// Let caller continue its search.
188235
return false;
189236
}
190237

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![feature(const_fn, type_alias_impl_trait)]
2+
3+
type Bar = impl Send;
4+
5+
// While i32 is structural-match, we do not want to leak this information.
6+
// (See https://github.com/rust-lang/rust/issues/72156)
7+
const fn leak_free() -> Bar {
8+
7i32
9+
}
10+
const LEAK_FREE: Bar = leak_free();
11+
12+
fn leak_free_test() {
13+
match todo!() {
14+
LEAK_FREE => (),
15+
//~^ opaque types cannot be used in patterns
16+
_ => (),
17+
}
18+
}
19+
20+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: opaque types cannot be used in patterns
2+
--> $DIR/structural-match-no-leak.rs:14:9
3+
|
4+
LL | LEAK_FREE => (),
5+
| ^^^^^^^^^
6+
7+
error: aborting due to previous error
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![feature(const_fn, type_alias_impl_trait)]
2+
3+
type Foo = impl Send;
4+
5+
// This is not structural-match
6+
struct A;
7+
8+
const fn value() -> Foo {
9+
A
10+
}
11+
const VALUE: Foo = value();
12+
13+
fn test() {
14+
match todo!() {
15+
VALUE => (),
16+
//~^ opaque types cannot be used in patterns
17+
_ => (),
18+
}
19+
}
20+
21+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: opaque types cannot be used in patterns
2+
--> $DIR/structural-match.rs:15:9
3+
|
4+
LL | VALUE => (),
5+
| ^^^^^
6+
7+
error: aborting due to previous error
8+

0 commit comments

Comments
 (0)