Skip to content

Commit 9e7b7d5

Browse files
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
2 parents 4ce1b0f + 10b69ab commit 9e7b7d5

18 files changed

+192
-138
lines changed

compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ impl Qualif for CustomEq {
226226
// because that component may be part of an enum variant (e.g.,
227227
// `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be
228228
// structural-match (`Option::None`).
229-
traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, true).is_some()
229+
traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some()
230230
}
231231

232232
fn in_adt_inherently<'tcx>(

compiler/rustc_middle/src/ty/consts.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ impl<'tcx> Const<'tcx> {
203203
pub fn to_valtree(self) -> ty::ValTree<'tcx> {
204204
match self.kind() {
205205
ty::ConstKind::Value(valtree) => valtree,
206-
_ => bug!("expected ConstKind::Value"),
206+
_ => bug!("expected ConstKind::Value, got {:?}", self.kind()),
207207
}
208208
}
209209

compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs

+29-31
Original file line numberDiff line numberDiff line change
@@ -120,37 +120,35 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
120120
}
121121

122122
fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> {
123-
traits::search_for_structural_match_violation(self.span, self.tcx(), ty, true).map(
124-
|non_sm_ty| {
125-
with_no_trimmed_paths!(match non_sm_ty.kind {
126-
traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt),
127-
traits::NonStructuralMatchTyKind::Dynamic => {
128-
"trait objects cannot be used in patterns".to_string()
129-
}
130-
traits::NonStructuralMatchTyKind::Opaque => {
131-
"opaque types cannot be used in patterns".to_string()
132-
}
133-
traits::NonStructuralMatchTyKind::Closure => {
134-
"closures cannot be used in patterns".to_string()
135-
}
136-
traits::NonStructuralMatchTyKind::Generator => {
137-
"generators cannot be used in patterns".to_string()
138-
}
139-
traits::NonStructuralMatchTyKind::Float => {
140-
"floating-point numbers cannot be used in patterns".to_string()
141-
}
142-
traits::NonStructuralMatchTyKind::Param => {
143-
bug!("use of a constant whose type is a parameter inside a pattern")
144-
}
145-
traits::NonStructuralMatchTyKind::Projection => {
146-
bug!("use of a constant whose type is a projection inside a pattern")
147-
}
148-
traits::NonStructuralMatchTyKind::Foreign => {
149-
bug!("use of a value of a foreign type inside a pattern")
150-
}
151-
})
152-
},
153-
)
123+
traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| {
124+
with_no_trimmed_paths!(match non_sm_ty.kind() {
125+
ty::Adt(adt, _) => self.adt_derive_msg(*adt),
126+
ty::Dynamic(..) => {
127+
"trait objects cannot be used in patterns".to_string()
128+
}
129+
ty::Opaque(..) => {
130+
"opaque types cannot be used in patterns".to_string()
131+
}
132+
ty::Closure(..) => {
133+
"closures cannot be used in patterns".to_string()
134+
}
135+
ty::Generator(..) | ty::GeneratorWitness(..) => {
136+
"generators cannot be used in patterns".to_string()
137+
}
138+
ty::Float(..) => {
139+
"floating-point numbers cannot be used in patterns".to_string()
140+
}
141+
ty::FnPtr(..) => {
142+
"function pointers cannot be used in patterns".to_string()
143+
}
144+
ty::RawPtr(..) => {
145+
"raw pointers cannot be used in patterns".to_string()
146+
}
147+
_ => {
148+
bug!("use of a value of `{non_sm_ty}` inside a pattern")
149+
}
150+
})
151+
})
154152
}
155153

156154
fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {

compiler/rustc_trait_selection/src/traits/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError
6060
pub use self::specialize::specialization_graph::FutureCompatOverlapError;
6161
pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
6262
pub use self::specialize::{specialization_graph, translate_substs, OverlapError};
63-
pub use self::structural_match::search_for_structural_match_violation;
64-
pub use self::structural_match::{NonStructuralMatchTy, NonStructuralMatchTyKind};
63+
pub use self::structural_match::{
64+
search_for_adt_const_param_violation, search_for_structural_match_violation,
65+
};
6566
pub use self::util::{
6667
elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span,
6768
elaborate_trait_ref, elaborate_trait_refs,

compiler/rustc_trait_selection/src/traits/structural_match.rs

+65-66
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,10 @@ use rustc_data_structures::fx::FxHashSet;
66
use rustc_hir as hir;
77
use rustc_hir::lang_items::LangItem;
88
use rustc_middle::ty::query::Providers;
9-
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
9+
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
1010
use rustc_span::Span;
1111
use std::ops::ControlFlow;
1212

13-
#[derive(Debug)]
14-
pub struct NonStructuralMatchTy<'tcx> {
15-
pub ty: Ty<'tcx>,
16-
pub kind: NonStructuralMatchTyKind<'tcx>,
17-
}
18-
19-
#[derive(Debug)]
20-
pub enum NonStructuralMatchTyKind<'tcx> {
21-
Adt(AdtDef<'tcx>),
22-
Param,
23-
Dynamic,
24-
Foreign,
25-
Opaque,
26-
Closure,
27-
Generator,
28-
Projection,
29-
Float,
30-
}
31-
3213
/// This method traverses the structure of `ty`, trying to find an
3314
/// instance of an ADT (i.e. struct or enum) that doesn't implement
3415
/// the structural-match traits, or a generic type parameter
@@ -54,15 +35,28 @@ pub enum NonStructuralMatchTyKind<'tcx> {
5435
/// For more background on why Rust has this requirement, and issues
5536
/// that arose when the requirement was not enforced completely, see
5637
/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
57-
///
58-
/// The floats_allowed flag is used to deny constants in floating point
5938
pub fn search_for_structural_match_violation<'tcx>(
6039
span: Span,
6140
tcx: TyCtxt<'tcx>,
6241
ty: Ty<'tcx>,
63-
floats_allowed: bool,
64-
) -> Option<NonStructuralMatchTy<'tcx>> {
65-
ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), floats_allowed })
42+
) -> Option<Ty<'tcx>> {
43+
ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false })
44+
.break_value()
45+
}
46+
47+
/// This method traverses the structure of `ty`, trying to find any
48+
/// types that are not allowed to be used in a const generic.
49+
///
50+
/// This is either because the type does not implement `StructuralEq`
51+
/// and `StructuralPartialEq`, or because the type is intentionally
52+
/// not supported in const generics (such as floats and raw pointers,
53+
/// which are allowed in match blocks).
54+
pub fn search_for_adt_const_param_violation<'tcx>(
55+
span: Span,
56+
tcx: TyCtxt<'tcx>,
57+
ty: Ty<'tcx>,
58+
) -> Option<Ty<'tcx>> {
59+
ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true })
6660
.break_value()
6761
}
6862

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

128-
floats_allowed: bool,
122+
// Additionally deny things that have been allowed in patterns,
123+
// but are not allowed in adt const params, such as floats and
124+
// fn ptrs.
125+
adt_const_param: bool,
129126
}
130127

131128
impl<'tcx> Search<'tcx> {
@@ -135,59 +132,35 @@ impl<'tcx> Search<'tcx> {
135132
}
136133

137134
impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
138-
type BreakTy = NonStructuralMatchTy<'tcx>;
135+
type BreakTy = Ty<'tcx>;
139136

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

143140
let (adt_def, substs) = match *ty.kind() {
144141
ty::Adt(adt_def, substs) => (adt_def, substs),
145142
ty::Param(_) => {
146-
let kind = NonStructuralMatchTyKind::Param;
147-
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
143+
return ControlFlow::Break(ty);
148144
}
149145
ty::Dynamic(..) => {
150-
let kind = NonStructuralMatchTyKind::Dynamic;
151-
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
146+
return ControlFlow::Break(ty);
152147
}
153148
ty::Foreign(_) => {
154-
let kind = NonStructuralMatchTyKind::Foreign;
155-
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
149+
return ControlFlow::Break(ty);
156150
}
157151
ty::Opaque(..) => {
158-
let kind = NonStructuralMatchTyKind::Opaque;
159-
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
152+
return ControlFlow::Break(ty);
160153
}
161154
ty::Projection(..) => {
162-
let kind = NonStructuralMatchTyKind::Projection;
163-
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
155+
return ControlFlow::Break(ty);
164156
}
165157
ty::Closure(..) => {
166-
let kind = NonStructuralMatchTyKind::Closure;
167-
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
158+
return ControlFlow::Break(ty);
168159
}
169160
ty::Generator(..) | ty::GeneratorWitness(..) => {
170-
let kind = NonStructuralMatchTyKind::Generator;
171-
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
172-
}
173-
ty::RawPtr(..) => {
174-
// structural-match ignores substructure of
175-
// `*const _`/`*mut _`, so skip `super_visit_with`.
176-
//
177-
// For example, if you have:
178-
// ```
179-
// struct NonStructural;
180-
// #[derive(PartialEq, Eq)]
181-
// struct T(*const NonStructural);
182-
// const C: T = T(std::ptr::null());
183-
// ```
184-
//
185-
// Even though `NonStructural` does not implement `PartialEq`,
186-
// structural equality on `T` does not recur into the raw
187-
// pointer. Therefore, one can still use `C` in a pattern.
188-
return ControlFlow::CONTINUE;
161+
return ControlFlow::Break(ty);
189162
}
190-
ty::FnDef(..) | ty::FnPtr(..) => {
163+
ty::FnDef(..) => {
191164
// Types of formals and return in `fn(_) -> _` are also irrelevant;
192165
// so we do not recur into them via `super_visit_with`
193166
return ControlFlow::CONTINUE;
@@ -206,14 +179,41 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
206179
return ControlFlow::CONTINUE;
207180
}
208181

182+
ty::FnPtr(..) => {
183+
if !self.adt_const_param {
184+
return ControlFlow::CONTINUE;
185+
} else {
186+
return ControlFlow::Break(ty);
187+
}
188+
}
189+
190+
ty::RawPtr(..) => {
191+
if !self.adt_const_param {
192+
// structural-match ignores substructure of
193+
// `*const _`/`*mut _`, so skip `super_visit_with`.
194+
//
195+
// For example, if you have:
196+
// ```
197+
// struct NonStructural;
198+
// #[derive(PartialEq, Eq)]
199+
// struct T(*const NonStructural);
200+
// const C: T = T(std::ptr::null());
201+
// ```
202+
//
203+
// Even though `NonStructural` does not implement `PartialEq`,
204+
// structural equality on `T` does not recur into the raw
205+
// pointer. Therefore, one can still use `C` in a pattern.
206+
return ControlFlow::CONTINUE;
207+
} else {
208+
return ControlFlow::Break(ty);
209+
}
210+
}
211+
209212
ty::Float(_) => {
210-
if self.floats_allowed {
213+
if !self.adt_const_param {
211214
return ControlFlow::CONTINUE;
212215
} else {
213-
return ControlFlow::Break(NonStructuralMatchTy {
214-
ty,
215-
kind: NonStructuralMatchTyKind::Float,
216-
});
216+
return ControlFlow::Break(ty);
217217
}
218218
}
219219

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

240240
if !self.type_marked_structural(ty) {
241241
debug!("Search found ty: {:?}", ty);
242-
let kind = NonStructuralMatchTyKind::Adt(adt_def);
243-
return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
242+
return ControlFlow::Break(ty);
244243
}
245244

246245
// structural-match does not care about the

compiler/rustc_typeck/src/check/wfcheck.rs

+22-20
Original file line numberDiff line numberDiff line change
@@ -848,29 +848,13 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
848848
let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id));
849849

850850
if tcx.features().adt_const_params {
851-
let err = match ty.peel_refs().kind() {
852-
ty::FnPtr(_) => Some("function pointers"),
853-
ty::RawPtr(_) => Some("raw pointers"),
854-
_ => None,
855-
};
856-
857-
if let Some(unsupported_type) = err {
858-
tcx.sess.span_err(
859-
hir_ty.span,
860-
&format!(
861-
"using {} as const generic parameters is forbidden",
862-
unsupported_type
863-
),
864-
);
865-
}
866-
867851
if let Some(non_structural_match_ty) =
868-
traits::search_for_structural_match_violation(param.span, tcx, ty, false)
852+
traits::search_for_adt_const_param_violation(param.span, tcx, ty)
869853
{
870854
// We use the same error code in both branches, because this is really the same
871855
// issue: we just special-case the message for type parameters to make it
872856
// clearer.
873-
match ty.peel_refs().kind() {
857+
match non_structural_match_ty.kind() {
874858
ty::Param(_) => {
875859
// Const parameters may not have type parameters as their types,
876860
// because we cannot be sure that the type parameter derives `PartialEq`
@@ -902,17 +886,35 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
902886
.note("floats do not derive `Eq` or `Ord`, which are required for const parameters")
903887
.emit();
904888
}
889+
ty::FnPtr(_) => {
890+
struct_span_err!(
891+
tcx.sess,
892+
hir_ty.span,
893+
E0741,
894+
"using function pointers as const generic parameters is forbidden",
895+
)
896+
.emit();
897+
}
898+
ty::RawPtr(_) => {
899+
struct_span_err!(
900+
tcx.sess,
901+
hir_ty.span,
902+
E0741,
903+
"using raw pointers as const generic parameters is forbidden",
904+
)
905+
.emit();
906+
}
905907
_ => {
906908
let mut diag = struct_span_err!(
907909
tcx.sess,
908910
hir_ty.span,
909911
E0741,
910912
"`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
911913
the type of a const parameter",
912-
non_structural_match_ty.ty,
914+
non_structural_match_ty,
913915
);
914916

915-
if ty == non_structural_match_ty.ty {
917+
if ty == non_structural_match_ty {
916918
diag.span_label(
917919
hir_ty.span,
918920
format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
error: using function pointers as const generic parameters is forbidden
1+
error[E0741]: using function pointers as const generic parameters is forbidden
22
--> $DIR/fn-const-param-call.rs:11:25
33
|
44
LL | struct Wrapper<const F: fn() -> u32>;
55
| ^^^^^^^^^^^
66

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

1313
error: aborting due to 2 previous errors
1414

15+
For more information about this error, try `rustc --explain E0741`.
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
error: using function pointers as const generic parameters is forbidden
1+
error[E0741]: using function pointers as const generic parameters is forbidden
22
--> $DIR/fn-const-param-infer.rs:6:25
33
|
44
LL | struct Checked<const F: fn(usize) -> bool>;
55
| ^^^^^^^^^^^^^^^^^
66

77
error: aborting due to previous error
88

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

0 commit comments

Comments
 (0)