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

Deeply deny fn and raw ptrs in const generics #99651

Merged
Merged
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
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