Rollup merge of #99651 - compiler-errors:fn-and-raw-ptr-in-const-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 #99641
This commit is contained in:
commit
9e7b7d5e1c
18 changed files with 193 additions and 139 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -135,7 +132,7 @@ 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);
|
||||
|
@ -143,51 +140,27 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
|
|||
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 });
|
||||
return ControlFlow::Break(ty);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
|
@ -206,14 +179,41 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
|
|||
return ControlFlow::CONTINUE;
|
||||
}
|
||||
|
||||
ty::Float(_) => {
|
||||
if self.floats_allowed {
|
||||
ty::FnPtr(..) => {
|
||||
if !self.adt_const_param {
|
||||
return ControlFlow::CONTINUE;
|
||||
} else {
|
||||
return ControlFlow::Break(NonStructuralMatchTy {
|
||||
ty,
|
||||
kind: NonStructuralMatchTyKind::Float,
|
||||
});
|
||||
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.adt_const_param {
|
||||
return ControlFlow::CONTINUE;
|
||||
} else {
|
||||
return ControlFlow::Break(ty);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue