Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce a whitelist on the constant primitive types allowed in patterns. #70872

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 50 additions & 14 deletions src/librustc_mir_build/hair/pattern/const_to_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,20 +101,24 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
cv.ty, structural
);
if let Some(non_sm_ty) = structural {
let adt_def = match non_sm_ty {
traits::NonStructuralMatchTy::Adt(adt_def) => adt_def,
let msg = match non_sm_ty {
traits::NonStructuralMatchTy::Adt(adt_def) => {
let path = self.tcx().def_path_str(adt_def.did);
format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
path, path,
)
}
traits::NonStructuralMatchTy::Param => {
bug!("use of constant whose type is a parameter inside a pattern")
}
};
let path = self.tcx().def_path_str(adt_def.did);

let make_msg = || -> String {
format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
path, path,
)
traits::NonStructuralMatchTy::FnPtr => {
format!("`fn` pointers cannot be used in a patterns")
}
traits::NonStructuralMatchTy::RawPtr => {
format!("raw pointers cannot be used in a patterns")
}
};

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

if !ty_is_partial_eq {
// span_fatal avoids ICE from resolution of non-existent method (rare case).
self.tcx().sess.span_fatal(self.span, &make_msg());
self.tcx().sess.span_fatal(self.span, &msg);
} else {
self.tcx().struct_span_lint_hir(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
self.span,
|lint| lint.build(&make_msg()).emit(),
|lint| lint.build(&msg).emit(),
);
}
}
Expand Down Expand Up @@ -257,7 +261,39 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
slice: None,
suffix: Vec::new(),
},
_ => PatKind::Constant { value: cv },
_ => {
let mut leaf_ty = cv.ty;

// HACK(eddyb) workaround missing reference destructuring.
loop {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little surprised to see there not a pre-existing method that does this traversal to find the content underlying a ref/array/slice? (Is the presupposition that we do not want such a method because any such traversal is almost certainly a hack, and should be annotated as such, as eddyb as done here?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is that thing where I was very confused that this code doesn't handle references at all.
IIRC, @oli-obk's #70743 would make this unnecessary and we'd be correct by default.

leaf_ty = match leaf_ty.kind {
ty::Ref(_, pointee_ty, _) => pointee_ty,

// HACK(eddyb) even worse, these show up *behind*
// references, so despite being supported above we have
// to reimplement them here (and we can't really do the
// same for nested tuples or ADTs...).
ty::Array(elem_ty, _) | ty::Slice(elem_ty) => elem_ty,

_ => break,
};
}

match leaf_ty.kind {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::FnDef(..) => {
PatKind::Constant { value: cv }
}

_ => {
debug!("non-structural leaf constant type {:?}", leaf_ty);
let msg =
format!("constants of type `{}` cannot be used in a pattern", leaf_ty);
self.saw_const_match_error.set(true);
tcx.sess.span_err(span, &msg);
PatKind::Wild
}
}
}
};

Pat { span, ty: cv.ty, kind: Box::new(kind) }
Expand Down
30 changes: 11 additions & 19 deletions src/librustc_trait_selection/traits/structural_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use rustc_span::Span;
pub enum NonStructuralMatchTy<'tcx> {
Adt(&'tcx AdtDef),
Param,
FnPtr,
RawPtr,
}

/// This method traverses the structure of `ty`, trying to find an
Expand Down Expand Up @@ -138,26 +140,16 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
return true; // Stop visiting.
}
ty::RawPtr(..) => {
// structural-match ignores substructure of
// `*const _`/`*mut _`, so skip `super_visit_with`.
//
// For example, if you have:
// ```
// struct NonStructural;
// #[derive(PartialEq, Eq)]
// struct T(*const NonStructural);
// const C: T = T(std::ptr::null());
// ```
//
// Even though `NonStructural` does not implement `PartialEq`,
// 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.)
return false;
self.found = Some(NonStructuralMatchTy::RawPtr);
return true; // Stop visiting.
}
ty::FnPtr(..) => {
self.found = Some(NonStructuralMatchTy::FnPtr);
return true; // Stop visiting.
}
ty::FnDef(..) | ty::FnPtr(..) => {
// types of formals and return in `fn(_) -> _` are also irrelevant;
ty::FnDef(..) => {
// substs of ZST function (*not* `fn(...) -> _` pointers!) types
// are also irrelevant;
// so we do not recur into them via `super_visit_with`
//
// (But still tell caller to continue search.)
Expand Down
44 changes: 6 additions & 38 deletions src/test/ui/const-generics/fn-const-param-infer.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,12 @@ LL | #![feature(const_generics, const_compare_raw_pointers)]
|
= note: `#[warn(incomplete_features)]` on by default

error[E0308]: mismatched types
--> $DIR/fn-const-param-infer.rs:16:31
error[E0741]: the types of const generic parameters must derive `PartialEq` and `Eq`
--> $DIR/fn-const-param-infer.rs:4:25
|
LL | let _: Checked<not_one> = Checked::<not_two>;
| ---------------- ^^^^^^^^^^^^^^^^^^ expected `{not_one as fn(usize) -> bool}`, found `{not_two as fn(usize) -> bool}`
| |
| expected due to this
|
= note: expected struct `Checked<{not_one as fn(usize) -> bool}>`
found struct `Checked<{not_two as fn(usize) -> bool}>`

error[E0308]: mismatched types
--> $DIR/fn-const-param-infer.rs:20:24
|
LL | let _ = Checked::<{generic_arg::<u32>}>;
| ^^^^^^^^^^^^^^^^^^ expected `usize`, found `u32`
|
= note: expected fn pointer `fn(usize) -> _`
found fn item `fn(u32) -> _ {generic_arg::<u32>}`

error[E0282]: type annotations needed
--> $DIR/fn-const-param-infer.rs:22:23
|
LL | let _ = Checked::<generic>;
| ^^^^^^^ cannot infer type for type parameter `T` declared on the function `generic`

error[E0308]: mismatched types
--> $DIR/fn-const-param-infer.rs:25:40
|
LL | let _: Checked<{generic::<u32>}> = Checked::<{generic::<u16>}>;
| ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{generic::<u32> as fn(usize) -> bool}`, found `{generic::<u16> as fn(usize) -> bool}`
| |
| expected due to this
|
= note: expected struct `Checked<{generic::<u32> as fn(usize) -> bool}>`
found struct `Checked<{generic::<u16> as fn(usize) -> bool}>`
LL | struct Checked<const F: fn(usize) -> bool>;
| ^^^^^^^^^^^^^^^^^ `fn(usize) -> bool` doesn't derive both `PartialEq` and `Eq`

error: aborting due to 4 previous errors
error: aborting due to previous error

Some errors have detailed explanations: E0282, E0308.
For more information about an error, try `rustc --explain E0282`.
For more information about this error, try `rustc --explain E0741`.
15 changes: 5 additions & 10 deletions src/test/ui/const-generics/raw-ptr-const-param.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,12 @@ LL | #![feature(const_generics, const_compare_raw_pointers)]
|
= note: `#[warn(incomplete_features)]` on by default

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

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
For more information about this error, try `rustc --explain E0741`.
23 changes: 11 additions & 12 deletions src/test/ui/consts/match_ice.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,23 @@ error: to use a constant of type `S` in a pattern, `S` must be annotated with `#
LL | C => {}
| ^

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

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

error: aborting due to 3 previous errors
error: constants of type `T` cannot be used in a pattern
--> $DIR/match_ice.rs:17:9
|
LL | K => {}
| ^

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0004`.
17 changes: 15 additions & 2 deletions src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ LL | struct ConstFn<const F: fn()>;
= note: see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information
= help: add `#![feature(const_compare_raw_pointers)]` to the crate attributes to enable

error[E0741]: the types of const generic parameters must derive `PartialEq` and `Eq`
--> $DIR/feature-gate-const_generics-ptr.rs:1:25
|
LL | struct ConstFn<const F: fn()>;
| ^^^^ `fn()` doesn't derive both `PartialEq` and `Eq`

error[E0658]: using raw pointers as const generic parameters is unstable
--> $DIR/feature-gate-const_generics-ptr.rs:5:26
|
Expand All @@ -34,6 +40,13 @@ LL | struct ConstPtr<const P: *const u32>;
= note: see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information
= help: add `#![feature(const_compare_raw_pointers)]` to the crate attributes to enable

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

error: aborting due to 6 previous errors

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

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

error: aborting due to 2 previous errors