Skip to content

Commit

Permalink
Rollup merge of #72153 - lcnr:exhaustively-match, r=pnkfelix
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Dylan-DPC authored May 25, 2020
2 parents ee6c0da + a5a4ec9 commit d5250c1
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 6 deletions.
14 changes: 13 additions & 1 deletion src/librustc_mir_build/hair/pattern/const_to_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,20 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
traits::NonStructuralMatchTy::Dynamic => {
"trait objects cannot be used in patterns".to_string()
}
traits::NonStructuralMatchTy::Opaque => {
"opaque types cannot be used in patterns".to_string()
}
traits::NonStructuralMatchTy::Generator => {
"generators cannot be used in patterns".to_string()
}
traits::NonStructuralMatchTy::Param => {
bug!("use of constant whose type is a parameter inside a pattern")
bug!("use of a constant whose type is a parameter inside a pattern")
}
traits::NonStructuralMatchTy::Projection => {
bug!("use of a constant whose type is a projection inside a pattern")
}
traits::NonStructuralMatchTy::Foreign => {
bug!("use of a value of a foreign type inside a pattern")
}
};

Expand Down
53 changes: 48 additions & 5 deletions src/librustc_trait_selection/traits/structural_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ pub enum NonStructuralMatchTy<'tcx> {
Adt(&'tcx AdtDef),
Param,
Dynamic,
Foreign,
Opaque,
Generator,
Projection,
}

/// This method traverses the structure of `ty`, trying to find an
Expand Down Expand Up @@ -143,6 +147,22 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
self.found = Some(NonStructuralMatchTy::Dynamic);
return true; // Stop visiting.
}
ty::Foreign(_) => {
self.found = Some(NonStructuralMatchTy::Foreign);
return true; // Stop visiting.
}
ty::Opaque(..) => {
self.found = Some(NonStructuralMatchTy::Opaque);
return true; // Stop visiting.
}
ty::Projection(..) => {
self.found = Some(NonStructuralMatchTy::Projection);
return true; // Stop visiting.
}
ty::Generator(..) | ty::GeneratorWitness(..) => {
self.found = Some(NonStructuralMatchTy::Generator);
return true; // Stop visiting.
}
ty::RawPtr(..) => {
// structural-match ignores substructure of
// `*const _`/`*mut _`, so skip `super_visit_with`.
Expand All @@ -159,32 +179,55 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
// structural equality on `T` does not recur into the raw
// pointer. Therefore, one can still use `C` in a pattern.

// (But still tell caller to continue search.)
// (But still tell the caller to continue search.)
return false;
}
ty::FnDef(..) | ty::FnPtr(..) => {
// types of formals and return in `fn(_) -> _` are also irrelevant;
// Types of formals and return in `fn(_) -> _` are also irrelevant;
// so we do not recur into them via `super_visit_with`
//
// (But still tell caller to continue search.)
// (But still tell the caller to continue search.)
return false;
}
ty::Array(_, n)
if { n.try_eval_usize(self.tcx(), ty::ParamEnv::reveal_all()) == Some(0) } =>
{
// rust-lang/rust#62336: ignore type of contents
// for empty array.
//
// (But still tell the caller to continue search.)
return false;
}
_ => {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => {
// These primitive types are always structural match.
//
// `Never` is kind of special here, but as it is not inhabitable, this should be fine.
//
// (But still tell the caller to continue search.)
return false;
}

ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => {
// First check all contained types and then tell the caller to continue searching.
ty.super_visit_with(self);
return false;
}
ty::Closure(..) | ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => {
bug!("unexpected type during structural-match checking: {:?}", ty);
}
ty::Error => {
self.tcx().sess.delay_span_bug(self.span, "ty::Error in structural-match check");
// We still want to check other types after encountering an error,
// as this may still emit relevant errors.
//
// So we continue searching here.
return false;
}
};

if !self.seen.insert(adt_def.did) {
debug!("Search already seen adt_def: {:?}", adt_def);
// let caller continue its search
// Let caller continue its search.
return false;
}

Expand Down
20 changes: 20 additions & 0 deletions src/test/ui/type-alias-impl-trait/structural-match-no-leak.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#![feature(const_fn, type_alias_impl_trait)]

type Bar = impl Send;

// While i32 is structural-match, we do not want to leak this information.
// (See /~https://github.com/rust-lang/rust/issues/72156)
const fn leak_free() -> Bar {
7i32
}
const LEAK_FREE: Bar = leak_free();

fn leak_free_test() {
match todo!() {
LEAK_FREE => (),
//~^ opaque types cannot be used in patterns
_ => (),
}
}

fn main() { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: opaque types cannot be used in patterns
--> $DIR/structural-match-no-leak.rs:14:9
|
LL | LEAK_FREE => (),
| ^^^^^^^^^

error: aborting due to previous error

21 changes: 21 additions & 0 deletions src/test/ui/type-alias-impl-trait/structural-match.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#![feature(const_fn, type_alias_impl_trait)]

type Foo = impl Send;

// This is not structural-match
struct A;

const fn value() -> Foo {
A
}
const VALUE: Foo = value();

fn test() {
match todo!() {
VALUE => (),
//~^ opaque types cannot be used in patterns
_ => (),
}
}

fn main() { }
8 changes: 8 additions & 0 deletions src/test/ui/type-alias-impl-trait/structural-match.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: opaque types cannot be used in patterns
--> $DIR/structural-match.rs:15:9
|
LL | VALUE => (),
| ^^^^^

error: aborting due to previous error

0 comments on commit d5250c1

Please sign in to comment.