Skip to content

Commit

Permalink
Rollup merge of #111670 - compiler-errors:const-param-ty, r=BoxyUwU
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
compiler-errors authored Jun 2, 2023
2 parents 774a3d1 + 97bacba commit 24404e6
Show file tree
Hide file tree
Showing 63 changed files with 569 additions and 316 deletions.
12 changes: 8 additions & 4 deletions compiler/rustc_error_codes/src/error_codes/E0741.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,19 @@ struct A;
struct B<const X: A>; // error!
```

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

To fix the previous code example, we derive `PartialEq` and `Eq`:
To fix the previous code example, we derive `PartialEq`, `Eq`, and
`ConstParamTy`:

```
#![feature(adt_const_params)]
#[derive(PartialEq, Eq)] // We derive both traits here.
use std::marker::ConstParamTy;
#[derive(PartialEq, Eq, ConstParamTy)] // We derive both traits here.
struct A;
struct B<const X: A>; // ok!
Expand Down
32 changes: 8 additions & 24 deletions compiler/rustc_errors/src/diagnostic_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,36 +115,22 @@ pub trait EmissionGuarantee: Sized {
) -> DiagnosticBuilder<'_, Self>;
}

/// Private module for sealing the `IsError` helper trait.
mod sealed_level_is_error {
use crate::Level;

/// Sealed helper trait for statically checking that a `Level` is an error.
pub(crate) trait IsError<const L: Level> {}

impl IsError<{ Level::Bug }> for () {}
impl IsError<{ Level::DelayedBug }> for () {}
impl IsError<{ Level::Fatal }> for () {}
// NOTE(eddyb) `Level::Error { lint: true }` is also an error, but lints
// don't need error guarantees, as their levels are always dynamic.
impl IsError<{ Level::Error { lint: false } }> for () {}
}

impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
/// Convenience function for internal use, clients should use one of the
/// `struct_*` methods on [`Handler`].
#[track_caller]
pub(crate) fn new_guaranteeing_error<M: Into<DiagnosticMessage>, const L: Level>(
pub(crate) fn new_guaranteeing_error<M: Into<DiagnosticMessage>>(
handler: &'a Handler,
message: M,
) -> Self
where
(): sealed_level_is_error::IsError<L>,
{
) -> Self {
Self {
inner: DiagnosticBuilderInner {
state: DiagnosticBuilderState::Emittable(handler),
diagnostic: Box::new(Diagnostic::new_with_code(L, None, message)),
diagnostic: Box::new(Diagnostic::new_with_code(
Level::Error { lint: false },
None,
message,
)),
},
_marker: PhantomData,
}
Expand Down Expand Up @@ -203,9 +189,7 @@ impl EmissionGuarantee for ErrorGuaranteed {
handler: &Handler,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, Self> {
DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(
handler, msg,
)
DiagnosticBuilder::new_guaranteeing_error(handler, msg)
}
}

Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#![feature(array_windows)]
#![feature(drain_filter)]
#![feature(if_let_guard)]
#![feature(adt_const_params)]
#![feature(let_chains)]
#![feature(never_type)]
#![feature(result_option_inspect)]
Expand Down Expand Up @@ -845,7 +844,7 @@ impl Handler {
&self,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(self, msg)
DiagnosticBuilder::new_guaranteeing_error(self, msg)
}

/// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors.
Expand Down
91 changes: 14 additions & 77 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -829,83 +829,20 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
let ty = tcx.type_of(param.def_id).subst_identity();

if tcx.features().adt_const_params {
if let Some(non_structural_match_ty) =
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 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`
// and `Eq` (just implementing them is not enough for `structural_match`).
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"`{ty}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \
used as the type of a const parameter",
)
.span_label(
hir_ty.span,
format!("`{ty}` may not derive both `PartialEq` and `Eq`"),
)
.note(
"it is not currently possible to use a type parameter as the type of a \
const parameter",
)
.emit();
}
ty::Float(_) => {
struct_span_err!(
tcx.sess,
hir_ty.span,
E0741,
"`{ty}` is forbidden as the type of a const generic parameter",
)
.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,
);

if ty == non_structural_match_ty {
diag.span_label(
hir_ty.span,
format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
);
}

diag.emit();
}
}
}
enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| {
let trait_def_id =
tcx.require_lang_item(LangItem::ConstParamTy, Some(hir_ty.span));
wfcx.register_bound(
ObligationCause::new(
hir_ty.span,
param.def_id,
ObligationCauseCode::ConstParam(ty),
),
wfcx.param_env,
ty,
trait_def_id,
);
});
} else {
let err_ty_str;
let mut is_ptr = true;
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,9 @@ pub enum ObligationCauseCode<'tcx> {
/// Obligations to prove that a `std::ops::Drop` impl is not stronger than
/// the ADT it's being implemented for.
DropImpl,

/// Requirement for a `const N: Ty` to implement `Ty: ConstParamTy`
ConstParam(Ty<'tcx>),
}

/// The 'location' at which we try to perform HIR-based wf checking.
Expand Down
110 changes: 110 additions & 0 deletions compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ pub trait TypeErrCtxtExt<'tcx> {
root_obligation: &PredicateObligation<'tcx>,
error: &SelectionError<'tcx>,
);

fn report_const_param_not_wf(
&self,
ty: Ty<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
}

impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
Expand Down Expand Up @@ -641,6 +647,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
span = obligation.cause.span;
}
}

if let ObligationCauseCode::CompareImplItemObligation {
impl_item_def_id,
trait_item_def_id,
Expand All @@ -657,6 +664,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return;
}

// Report a const-param specific error
if let ObligationCauseCode::ConstParam(ty) = *obligation.cause.code().peel_derives()
{
self.report_const_param_not_wf(ty, &obligation).emit();
return;
}

let bound_predicate = obligation.predicate.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
Expand Down Expand Up @@ -1163,6 +1177,102 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.point_at_returns_when_relevant(&mut err, &obligation);
err.emit();
}

fn report_const_param_not_wf(
&self,
ty: Ty<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let span = obligation.cause.span;

let mut diag = match ty.kind() {
_ if ty.has_param() => {
span_bug!(span, "const param tys cannot mention other generic parameters");
}
ty::Float(_) => {
struct_span_err!(
self.tcx.sess,
span,
E0741,
"`{ty}` is forbidden as the type of a const generic parameter",
)
}
ty::FnPtr(_) => {
struct_span_err!(
self.tcx.sess,
span,
E0741,
"using function pointers as const generic parameters is forbidden",
)
}
ty::RawPtr(_) => {
struct_span_err!(
self.tcx.sess,
span,
E0741,
"using raw pointers as const generic parameters is forbidden",
)
}
ty::Adt(def, _) => {
// We should probably see if we're *allowed* to derive `ConstParamTy` on the type...
let mut diag = struct_span_err!(
self.tcx.sess,
span,
E0741,
"`{ty}` must implement `ConstParamTy` to be used as the type of a const generic parameter",
);
// Only suggest derive if this isn't a derived obligation,
// and the struct is local.
if let Some(span) = self.tcx.hir().span_if_local(def.did())
&& obligation.cause.code().parent().is_none()
{
if ty.is_structural_eq_shallow(self.tcx) {
diag.span_suggestion(
span,
"add `#[derive(ConstParamTy)]` to the struct",
"#[derive(ConstParamTy)]\n",
Applicability::MachineApplicable,
);
} else {
// FIXME(adt_const_params): We should check there's not already an
// overlapping `Eq`/`PartialEq` impl.
diag.span_suggestion(
span,
"add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct",
"#[derive(ConstParamTy, PartialEq, Eq)]\n",
Applicability::MachineApplicable,
);
}
}
diag
}
_ => {
struct_span_err!(
self.tcx.sess,
span,
E0741,
"`{ty}` can't be used as a const parameter type",
)
}
};

let mut code = obligation.cause.code();
let mut pred = obligation.predicate.to_opt_poly_trait_pred();
while let Some((next_code, next_pred)) = code.parent() {
if let Some(pred) = pred {
let pred = self.instantiate_binder_with_placeholders(pred);
diag.note(format!(
"`{}` must implement `{}`, but it does not",
pred.self_ty(),
pred.print_modifiers_and_trait_path()
));
}
code = next_code;
pred = next_pred;
}

diag
}
}

trait InferCtxtPrivExt<'tcx> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2655,7 +2655,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| ObligationCauseCode::BinOp { .. }
| ObligationCauseCode::AscribeUserTypeProvePredicate(..)
| ObligationCauseCode::RustCall
| ObligationCauseCode::DropImpl => {}
| ObligationCauseCode::DropImpl
| ObligationCauseCode::ConstParam(_) => {}
ObligationCauseCode::SliceOrArrayElem => {
err.note("slice and array elements must have `Sized` type");
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_trait_selection/src/traits/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
| ty::Str
| ty::Array(..)
| ty::Slice(_)
| ty::Ref(.., hir::Mutability::Not) => return Ok(()),
| ty::Ref(.., hir::Mutability::Not)
| ty::Tuple(_) => return Ok(()),

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

Expand Down
4 changes: 1 addition & 3 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
pub use self::specialize::{
specialization_graph, translate_substs, translate_substs_with_cause, OverlapError,
};
pub use self::structural_match::{
search_for_adt_const_param_violation, search_for_structural_match_violation,
};
pub use self::structural_match::search_for_structural_match_violation;
pub use self::structural_normalize::StructurallyNormalizeExt;
pub use self::util::elaborate;
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
Expand Down
Loading

0 comments on commit 24404e6

Please sign in to comment.