Skip to content

Commit

Permalink
Rollup merge of rust-lang#99651 - compiler-errors:fn-and-raw-ptr-in-c…
Browse files Browse the repository at this point in the history
…onst-generics, r=oli-obk

Deeply deny fn and raw ptrs in const generics

I think this is right -- just because we wrap a fn ptr in a wrapper type does not mean we should allow it in a const parameter.

We now reject both of these in the same way:

```
#![feature(adt_const_params)]

#[derive(Eq, PartialEq)]
struct Wrapper();

fn foo<const W: Wrapper>() {}

fn foo2<const F: fn()>() {}
```

This does regress one test (`src/test/ui/consts/refs_check_const_eq-issue-88384.stderr`), but I'm not sure it should've passed in the first place.

cc: `@b-naber` who introduced that test^
fixes rust-lang#99641
  • Loading branch information
matthiaskrgr committed Jul 26, 2022
2 parents b66240a + 10b69ab commit 18f0e7d
Show file tree
Hide file tree
Showing 18 changed files with 192 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ impl Qualif for CustomEq {
// because that component may be part of an enum variant (e.g.,
// `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be
// structural-match (`Option::None`).
traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, true).is_some()
traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some()
}

fn in_adt_inherently<'tcx>(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ impl<'tcx> Const<'tcx> {
pub fn to_valtree(self) -> ty::ValTree<'tcx> {
match self.kind() {
ty::ConstKind::Value(valtree) => valtree,
_ => bug!("expected ConstKind::Value"),
_ => bug!("expected ConstKind::Value, got {:?}", self.kind()),
}
}

Expand Down
60 changes: 29 additions & 31 deletions compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,37 +120,35 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
}

fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> {
traits::search_for_structural_match_violation(self.span, self.tcx(), ty, true).map(
|non_sm_ty| {
with_no_trimmed_paths!(match non_sm_ty.kind {
traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt),
traits::NonStructuralMatchTyKind::Dynamic => {
"trait objects cannot be used in patterns".to_string()
}
traits::NonStructuralMatchTyKind::Opaque => {
"opaque types cannot be used in patterns".to_string()
}
traits::NonStructuralMatchTyKind::Closure => {
"closures cannot be used in patterns".to_string()
}
traits::NonStructuralMatchTyKind::Generator => {
"generators cannot be used in patterns".to_string()
}
traits::NonStructuralMatchTyKind::Float => {
"floating-point numbers cannot be used in patterns".to_string()
}
traits::NonStructuralMatchTyKind::Param => {
bug!("use of a constant whose type is a parameter inside a pattern")
}
traits::NonStructuralMatchTyKind::Projection => {
bug!("use of a constant whose type is a projection inside a pattern")
}
traits::NonStructuralMatchTyKind::Foreign => {
bug!("use of a value of a foreign type inside a pattern")
}
})
},
)
traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| {
with_no_trimmed_paths!(match non_sm_ty.kind() {
ty::Adt(adt, _) => self.adt_derive_msg(*adt),
ty::Dynamic(..) => {
"trait objects cannot be used in patterns".to_string()
}
ty::Opaque(..) => {
"opaque types cannot be used in patterns".to_string()
}
ty::Closure(..) => {
"closures cannot be used in patterns".to_string()
}
ty::Generator(..) | ty::GeneratorWitness(..) => {
"generators cannot be used in patterns".to_string()
}
ty::Float(..) => {
"floating-point numbers cannot be used in patterns".to_string()
}
ty::FnPtr(..) => {
"function pointers cannot be used in patterns".to_string()
}
ty::RawPtr(..) => {
"raw pointers cannot be used in patterns".to_string()
}
_ => {
bug!("use of a value of `{non_sm_ty}` inside a pattern")
}
})
})
}

fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError
pub use self::specialize::specialization_graph::FutureCompatOverlapError;
pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
pub use self::specialize::{specialization_graph, translate_substs, OverlapError};
pub use self::structural_match::search_for_structural_match_violation;
pub use self::structural_match::{NonStructuralMatchTy, NonStructuralMatchTyKind};
pub use self::structural_match::{
search_for_adt_const_param_violation, search_for_structural_match_violation,
};
pub use self::util::{
elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span,
elaborate_trait_ref, elaborate_trait_refs,
Expand Down
131 changes: 65 additions & 66 deletions compiler/rustc_trait_selection/src/traits/structural_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,10 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_span::Span;
use std::ops::ControlFlow;

#[derive(Debug)]
pub struct NonStructuralMatchTy<'tcx> {
pub ty: Ty<'tcx>,
pub kind: NonStructuralMatchTyKind<'tcx>,
}

#[derive(Debug)]
pub enum NonStructuralMatchTyKind<'tcx> {
Adt(AdtDef<'tcx>),
Param,
Dynamic,
Foreign,
Opaque,
Closure,
Generator,
Projection,
Float,
}

/// This method traverses the structure of `ty`, trying to find an
/// instance of an ADT (i.e. struct or enum) that doesn't implement
/// the structural-match traits, or a generic type parameter
Expand All @@ -54,15 +35,28 @@ pub enum NonStructuralMatchTyKind<'tcx> {
/// For more background on why Rust has this requirement, and issues
/// that arose when the requirement was not enforced completely, see
/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
///
/// The floats_allowed flag is used to deny constants in floating point
pub fn search_for_structural_match_violation<'tcx>(
span: Span,
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
floats_allowed: bool,
) -> Option<NonStructuralMatchTy<'tcx>> {
ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), floats_allowed })
) -> Option<Ty<'tcx>> {
ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false })
.break_value()
}

/// This method traverses the structure of `ty`, trying to find any
/// types that are not allowed to be used in a const generic.
///
/// This is either because the type does not implement `StructuralEq`
/// and `StructuralPartialEq`, or because the type is intentionally
/// not supported in const generics (such as floats and raw pointers,
/// which are allowed in match blocks).
pub fn search_for_adt_const_param_violation<'tcx>(
span: Span,
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Option<Ty<'tcx>> {
ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true })
.break_value()
}

Expand Down Expand Up @@ -125,7 +119,10 @@ struct Search<'tcx> {
/// we will not recur on them again.
seen: FxHashSet<hir::def_id::DefId>,

floats_allowed: bool,
// Additionally deny things that have been allowed in patterns,
// but are not allowed in adt const params, such as floats and
// fn ptrs.
adt_const_param: bool,
}

impl<'tcx> Search<'tcx> {
Expand All @@ -135,59 +132,35 @@ impl<'tcx> Search<'tcx> {
}

impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
type BreakTy = NonStructuralMatchTy<'tcx>;
type BreakTy = Ty<'tcx>;

fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!("Search visiting ty: {:?}", ty);

let (adt_def, substs) = match *ty.kind() {
ty::Adt(adt_def, substs) => (adt_def, substs),
ty::Param(_) => {
let kind = NonStructuralMatchTyKind::Param;
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
return ControlFlow::Break(ty);
}
ty::Dynamic(..) => {
let kind = NonStructuralMatchTyKind::Dynamic;
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
return ControlFlow::Break(ty);
}
ty::Foreign(_) => {
let kind = NonStructuralMatchTyKind::Foreign;
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
return ControlFlow::Break(ty);
}
ty::Opaque(..) => {
let kind = NonStructuralMatchTyKind::Opaque;
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
return ControlFlow::Break(ty);
}
ty::Projection(..) => {
let kind = NonStructuralMatchTyKind::Projection;
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
return ControlFlow::Break(ty);
}
ty::Closure(..) => {
let kind = NonStructuralMatchTyKind::Closure;
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
return ControlFlow::Break(ty);
}
ty::Generator(..) | ty::GeneratorWitness(..) => {
let kind = NonStructuralMatchTyKind::Generator;
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
}
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.
return ControlFlow::CONTINUE;
return ControlFlow::Break(ty);
}
ty::FnDef(..) | ty::FnPtr(..) => {
ty::FnDef(..) => {
// Types of formals and return in `fn(_) -> _` are also irrelevant;
// so we do not recur into them via `super_visit_with`
return ControlFlow::CONTINUE;
Expand All @@ -206,14 +179,41 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
return ControlFlow::CONTINUE;
}

ty::FnPtr(..) => {
if !self.adt_const_param {
return ControlFlow::CONTINUE;
} else {
return ControlFlow::Break(ty);
}
}

ty::RawPtr(..) => {
if !self.adt_const_param {
// 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.
return ControlFlow::CONTINUE;
} else {
return ControlFlow::Break(ty);
}
}

ty::Float(_) => {
if self.floats_allowed {
if !self.adt_const_param {
return ControlFlow::CONTINUE;
} else {
return ControlFlow::Break(NonStructuralMatchTy {
ty,
kind: NonStructuralMatchTyKind::Float,
});
return ControlFlow::Break(ty);
}
}

Expand All @@ -239,8 +239,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {

if !self.type_marked_structural(ty) {
debug!("Search found ty: {:?}", ty);
let kind = NonStructuralMatchTyKind::Adt(adt_def);
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
return ControlFlow::Break(ty);
}

// structural-match does not care about the
Expand Down
42 changes: 22 additions & 20 deletions compiler/rustc_typeck/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -848,29 +848,13 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id));

if tcx.features().adt_const_params {
let err = match ty.peel_refs().kind() {
ty::FnPtr(_) => Some("function pointers"),
ty::RawPtr(_) => Some("raw pointers"),
_ => None,
};

if let Some(unsupported_type) = err {
tcx.sess.span_err(
hir_ty.span,
&format!(
"using {} as const generic parameters is forbidden",
unsupported_type
),
);
}

if let Some(non_structural_match_ty) =
traits::search_for_structural_match_violation(param.span, tcx, ty, false)
traits::search_for_adt_const_param_violation(param.span, tcx, ty)
{
// We use the same error code in both branches, because this is really the same
// issue: we just special-case the message for type parameters to make it
// clearer.
match ty.peel_refs().kind() {
match non_structural_match_ty.kind() {
ty::Param(_) => {
// Const parameters may not have type parameters as their types,
// because we cannot be sure that the type parameter derives `PartialEq`
Expand Down Expand Up @@ -902,17 +886,35 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
.note("floats do not derive `Eq` or `Ord`, which are required for const parameters")
.emit();
}
ty::FnPtr(_) => {
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"using function pointers as const generic parameters is forbidden",
)
.emit();
}
ty::RawPtr(_) => {
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"using raw pointers as const generic parameters is forbidden",
)
.emit();
}
_ => {
let mut diag = struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
the type of a const parameter",
non_structural_match_ty.ty,
non_structural_match_ty,
);

if ty == non_structural_match_ty.ty {
if ty == non_structural_match_ty {
diag.span_label(
hir_ty.span,
format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
Expand Down
5 changes: 3 additions & 2 deletions src/test/ui/const-generics/fn-const-param-call.full.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
error: using function pointers as const generic parameters is forbidden
error[E0741]: using function pointers as const generic parameters is forbidden
--> $DIR/fn-const-param-call.rs:11:25
|
LL | struct Wrapper<const F: fn() -> u32>;
| ^^^^^^^^^^^

error: using function pointers as const generic parameters is forbidden
error[E0741]: using function pointers as const generic parameters is forbidden
--> $DIR/fn-const-param-call.rs:13:15
|
LL | impl<const F: fn() -> u32> Wrapper<F> {
| ^^^^^^^^^^^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0741`.
3 changes: 2 additions & 1 deletion src/test/ui/const-generics/fn-const-param-infer.full.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
error: using function pointers as const generic parameters is forbidden
error[E0741]: using function pointers as const generic parameters is forbidden
--> $DIR/fn-const-param-infer.rs:6:25
|
LL | struct Checked<const F: fn(usize) -> bool>;
| ^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0741`.
Loading

0 comments on commit 18f0e7d

Please sign in to comment.