Skip to content

Commit

Permalink
Rollup merge of #98907 - compiler-errors:plz-no-float, r=oli-obk
Browse files Browse the repository at this point in the history
Deny float const params even when `adt_const_params` is enabled

Supersedes #98825
Fixes #98813

r? ``@oli-obk``
  • Loading branch information
Dylan-DPC authored Jul 11, 2022
2 parents 9431520 + a163464 commit 92b8adf
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 66 deletions.
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).is_some()
traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, true).is_some()
}

fn in_adt_inherently<'tcx>(
Expand Down
57 changes: 31 additions & 26 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,32 +120,37 @@ 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).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::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, 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")
}
})
},
)
}

fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
Expand Down
22 changes: 20 additions & 2 deletions compiler/rustc_trait_selection/src/traits/structural_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub enum NonStructuralMatchTyKind<'tcx> {
Closure,
Generator,
Projection,
Float,
}

/// This method traverses the structure of `ty`, trying to find an
Expand Down Expand Up @@ -53,12 +54,16 @@ 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() }).break_value()
ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), floats_allowed })
.break_value()
}

/// This method returns true if and only if `adt_ty` itself has been marked as
Expand Down Expand Up @@ -119,6 +124,8 @@ struct Search<'tcx> {
/// Tracks ADTs previously encountered during search, so that
/// we will not recur on them again.
seen: FxHashSet<hir::def_id::DefId>,

floats_allowed: bool,
}

impl<'tcx> Search<'tcx> {
Expand Down Expand Up @@ -192,13 +199,24 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
// for empty array.
return ControlFlow::CONTINUE;
}
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => {
// These primitive types are always structural match.
//
// `Never` is kind of special here, but as it is not inhabitable, this should be fine.
return ControlFlow::CONTINUE;
}

ty::Float(_) => {
if self.floats_allowed {
return ControlFlow::CONTINUE;
} else {
return ControlFlow::Break(NonStructuralMatchTy {
ty,
kind: NonStructuralMatchTyKind::Float,
});
}
}

ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => {
// First check all contained types and then tell the caller to continue searching.
return ty.super_visit_with(self);
Expand Down
86 changes: 49 additions & 37 deletions compiler/rustc_typeck/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,50 +824,62 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
}

if let Some(non_structural_match_ty) =
traits::search_for_structural_match_violation(param.span, tcx, ty)
traits::search_for_structural_match_violation(param.span, tcx, ty, false)
{
// 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.
if let ty::Param(_) = ty.peel_refs().kind() {
// 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,
"`{}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \
used as the type of a const parameter",
ty,
)
.span_label(
hir_ty.span,
format!("`{}` may not derive both `PartialEq` and `Eq`", ty),
)
.note(
"it is not currently possible to use a type parameter as the type of a \
const parameter",
)
.emit();
} else {
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,
);

if ty == non_structural_match_ty.ty {
diag.span_label(
match ty.peel_refs().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,
format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
);
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();
}
_ => {
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,
);

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

diag.emit();
}
}
}
} else {
Expand Down
11 changes: 11 additions & 0 deletions src/test/ui/const-generics/float-generic.adt_const_params.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0741]: `f32` is forbidden as the type of a const generic parameter
--> $DIR/float-generic.rs:5:17
|
LL | fn foo<const F: f32>() {}
| ^^^
|
= note: floats do not derive `Eq` or `Ord`, which are required for const parameters

error: aborting due to previous error

For more information about this error, try `rustc --explain E0741`.
12 changes: 12 additions & 0 deletions src/test/ui/const-generics/float-generic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// revisions: simple adt_const_params
#![cfg_attr(adt_const_params, feature(adt_const_params))]
#![cfg_attr(adt_const_params, allow(incomplete_features))]

fn foo<const F: f32>() {}
//~^ ERROR `f32` is forbidden as the type of a const generic parameter

const C: f32 = 1.0;

fn main() {
foo::<C>();
}
11 changes: 11 additions & 0 deletions src/test/ui/const-generics/float-generic.simple.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: `f32` is forbidden as the type of a const generic parameter
--> $DIR/float-generic.rs:5:17
|
LL | fn foo<const F: f32>() {}
| ^^^
|
= note: the only supported types are integers, `bool` and `char`
= help: more complex types are supported with `#![feature(adt_const_params)]`

error: aborting due to previous error

0 comments on commit 92b8adf

Please sign in to comment.