Skip to content

Commit 24404e6

Browse files
Rollup merge of #111670 - compiler-errors:const-param-ty, r=BoxyUwU
Require that const param tys implement `ConstParamTy` 1. Require that const param tys implement `ConstParamTy` instead of using `search_for_adt_const_param_violation` 2. Add `StructuralPartialEq` as a supertrait for `ConstParamTy`, since we need to make sure that we derive *both* `PartialEq` and `Eq` 3. Implement `ConstParamTy` for tuples up to 12 (or whatever the default for tuples is) 4. Add some custom diagnostics to `ConstParamTy` errors, to avoid regressions from (1.). It's still not as great as it could be -- will point out inline in comments. r? `@BoxyUwU`
2 parents 774a3d1 + 97bacba commit 24404e6

File tree

63 files changed

+569
-316
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+569
-316
lines changed

compiler/rustc_error_codes/src/error_codes/E0741.md

+8-4
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,19 @@ struct A;
1010
struct B<const X: A>; // error!
1111
```
1212

13-
Only structural-match types (that is, types that derive `PartialEq` and `Eq`)
14-
may be used as the types of const generic parameters.
13+
Only structural-match types, which are types that derive `PartialEq` and `Eq`
14+
and implement `ConstParamTy`, may be used as the types of const generic
15+
parameters.
1516

16-
To fix the previous code example, we derive `PartialEq` and `Eq`:
17+
To fix the previous code example, we derive `PartialEq`, `Eq`, and
18+
`ConstParamTy`:
1719

1820
```
1921
#![feature(adt_const_params)]
2022
21-
#[derive(PartialEq, Eq)] // We derive both traits here.
23+
use std::marker::ConstParamTy;
24+
25+
#[derive(PartialEq, Eq, ConstParamTy)] // We derive both traits here.
2226
struct A;
2327
2428
struct B<const X: A>; // ok!

compiler/rustc_errors/src/diagnostic_builder.rs

+8-24
Original file line numberDiff line numberDiff line change
@@ -115,36 +115,22 @@ pub trait EmissionGuarantee: Sized {
115115
) -> DiagnosticBuilder<'_, Self>;
116116
}
117117

118-
/// Private module for sealing the `IsError` helper trait.
119-
mod sealed_level_is_error {
120-
use crate::Level;
121-
122-
/// Sealed helper trait for statically checking that a `Level` is an error.
123-
pub(crate) trait IsError<const L: Level> {}
124-
125-
impl IsError<{ Level::Bug }> for () {}
126-
impl IsError<{ Level::DelayedBug }> for () {}
127-
impl IsError<{ Level::Fatal }> for () {}
128-
// NOTE(eddyb) `Level::Error { lint: true }` is also an error, but lints
129-
// don't need error guarantees, as their levels are always dynamic.
130-
impl IsError<{ Level::Error { lint: false } }> for () {}
131-
}
132-
133118
impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
134119
/// Convenience function for internal use, clients should use one of the
135120
/// `struct_*` methods on [`Handler`].
136121
#[track_caller]
137-
pub(crate) fn new_guaranteeing_error<M: Into<DiagnosticMessage>, const L: Level>(
122+
pub(crate) fn new_guaranteeing_error<M: Into<DiagnosticMessage>>(
138123
handler: &'a Handler,
139124
message: M,
140-
) -> Self
141-
where
142-
(): sealed_level_is_error::IsError<L>,
143-
{
125+
) -> Self {
144126
Self {
145127
inner: DiagnosticBuilderInner {
146128
state: DiagnosticBuilderState::Emittable(handler),
147-
diagnostic: Box::new(Diagnostic::new_with_code(L, None, message)),
129+
diagnostic: Box::new(Diagnostic::new_with_code(
130+
Level::Error { lint: false },
131+
None,
132+
message,
133+
)),
148134
},
149135
_marker: PhantomData,
150136
}
@@ -203,9 +189,7 @@ impl EmissionGuarantee for ErrorGuaranteed {
203189
handler: &Handler,
204190
msg: impl Into<DiagnosticMessage>,
205191
) -> DiagnosticBuilder<'_, Self> {
206-
DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(
207-
handler, msg,
208-
)
192+
DiagnosticBuilder::new_guaranteeing_error(handler, msg)
209193
}
210194
}
211195

compiler/rustc_errors/src/lib.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#![feature(array_windows)]
77
#![feature(drain_filter)]
88
#![feature(if_let_guard)]
9-
#![feature(adt_const_params)]
109
#![feature(let_chains)]
1110
#![feature(never_type)]
1211
#![feature(result_option_inspect)]
@@ -845,7 +844,7 @@ impl Handler {
845844
&self,
846845
msg: impl Into<DiagnosticMessage>,
847846
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
848-
DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(self, msg)
847+
DiagnosticBuilder::new_guaranteeing_error(self, msg)
849848
}
850849

851850
/// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors.

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+14-77
Original file line numberDiff line numberDiff line change
@@ -829,83 +829,20 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
829829
let ty = tcx.type_of(param.def_id).subst_identity();
830830

831831
if tcx.features().adt_const_params {
832-
if let Some(non_structural_match_ty) =
833-
traits::search_for_adt_const_param_violation(param.span, tcx, ty)
834-
{
835-
// We use the same error code in both branches, because this is really the same
836-
// issue: we just special-case the message for type parameters to make it
837-
// clearer.
838-
match non_structural_match_ty.kind() {
839-
ty::Param(_) => {
840-
// Const parameters may not have type parameters as their types,
841-
// because we cannot be sure that the type parameter derives `PartialEq`
842-
// and `Eq` (just implementing them is not enough for `structural_match`).
843-
struct_span_err!(
844-
tcx.sess,
845-
hir_ty.span,
846-
E0741,
847-
"`{ty}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \
848-
used as the type of a const parameter",
849-
)
850-
.span_label(
851-
hir_ty.span,
852-
format!("`{ty}` may not derive both `PartialEq` and `Eq`"),
853-
)
854-
.note(
855-
"it is not currently possible to use a type parameter as the type of a \
856-
const parameter",
857-
)
858-
.emit();
859-
}
860-
ty::Float(_) => {
861-
struct_span_err!(
862-
tcx.sess,
863-
hir_ty.span,
864-
E0741,
865-
"`{ty}` is forbidden as the type of a const generic parameter",
866-
)
867-
.note("floats do not derive `Eq` or `Ord`, which are required for const parameters")
868-
.emit();
869-
}
870-
ty::FnPtr(_) => {
871-
struct_span_err!(
872-
tcx.sess,
873-
hir_ty.span,
874-
E0741,
875-
"using function pointers as const generic parameters is forbidden",
876-
)
877-
.emit();
878-
}
879-
ty::RawPtr(_) => {
880-
struct_span_err!(
881-
tcx.sess,
882-
hir_ty.span,
883-
E0741,
884-
"using raw pointers as const generic parameters is forbidden",
885-
)
886-
.emit();
887-
}
888-
_ => {
889-
let mut diag = struct_span_err!(
890-
tcx.sess,
891-
hir_ty.span,
892-
E0741,
893-
"`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
894-
the type of a const parameter",
895-
non_structural_match_ty,
896-
);
897-
898-
if ty == non_structural_match_ty {
899-
diag.span_label(
900-
hir_ty.span,
901-
format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
902-
);
903-
}
904-
905-
diag.emit();
906-
}
907-
}
908-
}
832+
enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| {
833+
let trait_def_id =
834+
tcx.require_lang_item(LangItem::ConstParamTy, Some(hir_ty.span));
835+
wfcx.register_bound(
836+
ObligationCause::new(
837+
hir_ty.span,
838+
param.def_id,
839+
ObligationCauseCode::ConstParam(ty),
840+
),
841+
wfcx.param_env,
842+
ty,
843+
trait_def_id,
844+
);
845+
});
909846
} else {
910847
let err_ty_str;
911848
let mut is_ptr = true;

compiler/rustc_middle/src/traits/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,9 @@ pub enum ObligationCauseCode<'tcx> {
445445
/// Obligations to prove that a `std::ops::Drop` impl is not stronger than
446446
/// the ADT it's being implemented for.
447447
DropImpl,
448+
449+
/// Requirement for a `const N: Ty` to implement `Ty: ConstParamTy`
450+
ConstParam(Ty<'tcx>),
448451
}
449452

450453
/// The 'location' at which we try to perform HIR-based wf checking.

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+110
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ pub trait TypeErrCtxtExt<'tcx> {
149149
root_obligation: &PredicateObligation<'tcx>,
150150
error: &SelectionError<'tcx>,
151151
);
152+
153+
fn report_const_param_not_wf(
154+
&self,
155+
ty: Ty<'tcx>,
156+
obligation: &PredicateObligation<'tcx>,
157+
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
152158
}
153159

154160
impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
@@ -641,6 +647,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
641647
span = obligation.cause.span;
642648
}
643649
}
650+
644651
if let ObligationCauseCode::CompareImplItemObligation {
645652
impl_item_def_id,
646653
trait_item_def_id,
@@ -657,6 +664,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
657664
return;
658665
}
659666

667+
// Report a const-param specific error
668+
if let ObligationCauseCode::ConstParam(ty) = *obligation.cause.code().peel_derives()
669+
{
670+
self.report_const_param_not_wf(ty, &obligation).emit();
671+
return;
672+
}
673+
660674
let bound_predicate = obligation.predicate.kind();
661675
match bound_predicate.skip_binder() {
662676
ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
@@ -1163,6 +1177,102 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
11631177
self.point_at_returns_when_relevant(&mut err, &obligation);
11641178
err.emit();
11651179
}
1180+
1181+
fn report_const_param_not_wf(
1182+
&self,
1183+
ty: Ty<'tcx>,
1184+
obligation: &PredicateObligation<'tcx>,
1185+
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
1186+
let span = obligation.cause.span;
1187+
1188+
let mut diag = match ty.kind() {
1189+
_ if ty.has_param() => {
1190+
span_bug!(span, "const param tys cannot mention other generic parameters");
1191+
}
1192+
ty::Float(_) => {
1193+
struct_span_err!(
1194+
self.tcx.sess,
1195+
span,
1196+
E0741,
1197+
"`{ty}` is forbidden as the type of a const generic parameter",
1198+
)
1199+
}
1200+
ty::FnPtr(_) => {
1201+
struct_span_err!(
1202+
self.tcx.sess,
1203+
span,
1204+
E0741,
1205+
"using function pointers as const generic parameters is forbidden",
1206+
)
1207+
}
1208+
ty::RawPtr(_) => {
1209+
struct_span_err!(
1210+
self.tcx.sess,
1211+
span,
1212+
E0741,
1213+
"using raw pointers as const generic parameters is forbidden",
1214+
)
1215+
}
1216+
ty::Adt(def, _) => {
1217+
// We should probably see if we're *allowed* to derive `ConstParamTy` on the type...
1218+
let mut diag = struct_span_err!(
1219+
self.tcx.sess,
1220+
span,
1221+
E0741,
1222+
"`{ty}` must implement `ConstParamTy` to be used as the type of a const generic parameter",
1223+
);
1224+
// Only suggest derive if this isn't a derived obligation,
1225+
// and the struct is local.
1226+
if let Some(span) = self.tcx.hir().span_if_local(def.did())
1227+
&& obligation.cause.code().parent().is_none()
1228+
{
1229+
if ty.is_structural_eq_shallow(self.tcx) {
1230+
diag.span_suggestion(
1231+
span,
1232+
"add `#[derive(ConstParamTy)]` to the struct",
1233+
"#[derive(ConstParamTy)]\n",
1234+
Applicability::MachineApplicable,
1235+
);
1236+
} else {
1237+
// FIXME(adt_const_params): We should check there's not already an
1238+
// overlapping `Eq`/`PartialEq` impl.
1239+
diag.span_suggestion(
1240+
span,
1241+
"add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct",
1242+
"#[derive(ConstParamTy, PartialEq, Eq)]\n",
1243+
Applicability::MachineApplicable,
1244+
);
1245+
}
1246+
}
1247+
diag
1248+
}
1249+
_ => {
1250+
struct_span_err!(
1251+
self.tcx.sess,
1252+
span,
1253+
E0741,
1254+
"`{ty}` can't be used as a const parameter type",
1255+
)
1256+
}
1257+
};
1258+
1259+
let mut code = obligation.cause.code();
1260+
let mut pred = obligation.predicate.to_opt_poly_trait_pred();
1261+
while let Some((next_code, next_pred)) = code.parent() {
1262+
if let Some(pred) = pred {
1263+
let pred = self.instantiate_binder_with_placeholders(pred);
1264+
diag.note(format!(
1265+
"`{}` must implement `{}`, but it does not",
1266+
pred.self_ty(),
1267+
pred.print_modifiers_and_trait_path()
1268+
));
1269+
}
1270+
code = next_code;
1271+
pred = next_pred;
1272+
}
1273+
1274+
diag
1275+
}
11661276
}
11671277

11681278
trait InferCtxtPrivExt<'tcx> {

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2655,7 +2655,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
26552655
| ObligationCauseCode::BinOp { .. }
26562656
| ObligationCauseCode::AscribeUserTypeProvePredicate(..)
26572657
| ObligationCauseCode::RustCall
2658-
| ObligationCauseCode::DropImpl => {}
2658+
| ObligationCauseCode::DropImpl
2659+
| ObligationCauseCode::ConstParam(_) => {}
26592660
ObligationCauseCode::SliceOrArrayElem => {
26602661
err.note("slice and array elements must have `Sized` type");
26612662
}

compiler/rustc_trait_selection/src/traits/misc.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
100100
| ty::Str
101101
| ty::Array(..)
102102
| ty::Slice(_)
103-
| ty::Ref(.., hir::Mutability::Not) => return Ok(()),
103+
| ty::Ref(.., hir::Mutability::Not)
104+
| ty::Tuple(_) => return Ok(()),
104105

105106
&ty::Adt(adt, substs) => (adt, substs),
106107

compiler/rustc_trait_selection/src/traits/mod.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
6262
pub use self::specialize::{
6363
specialization_graph, translate_substs, translate_substs_with_cause, OverlapError,
6464
};
65-
pub use self::structural_match::{
66-
search_for_adt_const_param_violation, search_for_structural_match_violation,
67-
};
65+
pub use self::structural_match::search_for_structural_match_violation;
6866
pub use self::structural_normalize::StructurallyNormalizeExt;
6967
pub use self::util::elaborate;
7068
pub use self::util::{expand_trait_aliases, TraitAliasExpander};

0 commit comments

Comments
 (0)