Translate const_to_pat.rs

This commit is contained in:
mejrs 2022-12-20 00:28:33 +01:00 committed by David Tolnay
parent ef4046e4f3
commit 519b1abd19
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
3 changed files with 122 additions and 138 deletions

View File

@ -299,3 +299,22 @@ mir_build_multiple_mut_borrows = cannot borrow value as mutable more than once a
.mutable_borrow = another mutable borrow, by `{$name_mut}`, occurs here
.immutable_borrow = also borrowed as immutable, by `{$name_immut}`, here
.moved = also moved into `{$name_moved}` here
mir_build_union_pattern = cannot use unions in constant patterns
mir_build_type_not_structural =
to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
mir_build_unsized_pattern = cannot use unsized non-slice type `{$non_sm_ty}` in constant patterns
mir_build_invalid_pattern = `{$non_sm_ty}` cannot be used in patterns
mir_build_float_pattern = floating-point types cannot be used in patterns
mir_build_pointer_pattern = function pointers and unsized pointers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details.
mir_build_indirect_structural_match =
to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
mir_build_nontrivial_structural_match =
to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`

View File

@ -614,3 +614,54 @@ pub enum MultipleMutBorrowOccurence {
name_moved: Ident,
},
}
#[derive(Diagnostic)]
#[diag(mir_build_union_pattern)]
pub struct UnionPattern {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_type_not_structural)]
pub struct TypeNotStructural<'tcx> {
#[primary_span]
pub span: Span,
pub non_sm_ty: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag(mir_build_invalid_pattern)]
pub struct InvalidPattern<'tcx> {
#[primary_span]
pub span: Span,
pub non_sm_ty: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag(mir_build_unsized_pattern)]
pub struct UnsizedPattern<'tcx> {
#[primary_span]
pub span: Span,
pub non_sm_ty: Ty<'tcx>,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_float_pattern)]
pub struct FloatPattern;
#[derive(LintDiagnostic)]
#[diag(mir_build_pointer_pattern)]
pub struct PointerPattern;
#[derive(LintDiagnostic)]
#[diag(mir_build_indirect_structural_match)]
pub struct IndirectStructuralMatch<'tcx> {
pub non_sm_ty: Ty<'tcx>,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_nontrivial_structural_match)]
pub struct NontrivialStructuralMatch<'tcx> {
pub non_sm_ty: Ty<'tcx>,
}

View File

@ -1,11 +1,9 @@
use rustc_errors::DelayDm;
use rustc_hir as hir;
use rustc_index::vec::Idx;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_middle::mir::{self, Field};
use rustc_middle::thir::{FieldPat, Pat, PatKind};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::lint;
use rustc_span::Span;
use rustc_trait_selection::traits::predicate_for_trait_def;
@ -15,6 +13,10 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
use std::cell::Cell;
use super::PatCtxt;
use crate::errors::{
FloatPattern, IndirectStructuralMatch, InvalidPattern, NontrivialStructuralMatch,
PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern,
};
impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
/// Converts an evaluated constant to a pattern (if possible).
@ -105,47 +107,6 @@ impl<'tcx> ConstToPat<'tcx> {
self.infcx.tcx
}
fn adt_derive_msg(&self, adt_def: AdtDef<'tcx>) -> String {
let path = self.tcx().def_path_str(adt_def.did());
format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
path, path,
)
}
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() {
ty::Adt(adt, _) => self.adt_derive_msg(*adt),
ty::Dynamic(..) => {
"trait objects cannot be used in patterns".to_string()
}
ty::Alias(ty::Opaque, ..) => {
"opaque types cannot be used in patterns".to_string()
}
ty::Closure(..) => {
"closures cannot be used in patterns".to_string()
}
ty::Generator(..) | ty::GeneratorWitness(..) => {
"generators cannot be used in patterns".to_string()
}
ty::Float(..) => {
"floating-point numbers cannot be used in patterns".to_string()
}
ty::FnPtr(..) => {
"function pointers cannot be used in patterns".to_string()
}
ty::RawPtr(..) => {
"raw pointers cannot be used in patterns".to_string()
}
_ => {
bug!("use of a value of `{non_sm_ty}` inside a pattern")
}
})
})
}
fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
ty.is_structural_eq_shallow(self.infcx.tcx)
}
@ -176,7 +137,8 @@ impl<'tcx> ConstToPat<'tcx> {
// If we were able to successfully convert the const to some pat,
// double-check that all types in the const implement `Structural`.
let structural = self.search_for_structural_match_violation(cv.ty());
let structural =
traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty());
debug!(
"search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
cv.ty(),
@ -194,17 +156,18 @@ impl<'tcx> ConstToPat<'tcx> {
return inlined_const_as_pat;
}
if let Some(msg) = structural {
if let Some(non_sm_ty) = structural {
if !self.type_may_have_partial_eq_impl(cv.ty()) {
// span_fatal avoids ICE from resolution of non-existent method (rare case).
self.tcx().sess.span_fatal(self.span, &msg);
// fatal avoids ICE from resolution of non-existent method (rare case).
self.tcx()
.sess
.emit_fatal(TypeNotStructural { span: self.span, non_sm_ty: non_sm_ty });
} else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
self.tcx().struct_span_lint_hir(
self.tcx().emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
self.span,
msg,
|lint| lint,
IndirectStructuralMatch { non_sm_ty },
);
} else {
debug!(
@ -278,12 +241,11 @@ impl<'tcx> ConstToPat<'tcx> {
let kind = match cv.ty().kind() {
ty::Float(_) => {
if self.include_lint_checks {
tcx.struct_span_lint_hir(
tcx.emit_spanned_lint(
lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
id,
span,
"floating-point types cannot be used in patterns",
|lint| lint,
FloatPattern,
);
}
PatKind::Constant { value: cv }
@ -291,29 +253,22 @@ impl<'tcx> ConstToPat<'tcx> {
ty::Adt(adt_def, _) if adt_def.is_union() => {
// Matching on union fields is unsafe, we can't hide it in constants
self.saw_const_match_error.set(true);
let msg = "cannot use unions in constant patterns";
if self.include_lint_checks {
tcx.sess.span_err(span, msg);
} else {
tcx.sess.delay_span_bug(span, msg);
}
let err = UnionPattern { span };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
ty::Adt(..)
if !self.type_may_have_partial_eq_impl(cv.ty())
// FIXME(#73448): Find a way to bring const qualification into parity with
// `search_for_structural_match_violation` and then remove this condition.
&& self.search_for_structural_match_violation(cv.ty()).is_some() =>
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
// could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
&& let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) =>
{
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
// could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
let msg = self.search_for_structural_match_violation(cv.ty()).unwrap();
self.saw_const_match_error.set(true);
if self.include_lint_checks {
tcx.sess.span_err(self.span, &msg);
} else {
tcx.sess.delay_span_bug(self.span, &msg);
}
let err = TypeNotStructural { span, non_sm_ty };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
// If the type is not structurally comparable, just emit the constant directly,
@ -331,19 +286,11 @@ impl<'tcx> ConstToPat<'tcx> {
&& !self.saw_const_match_lint.get()
{
self.saw_const_match_lint.set(true);
tcx.struct_span_lint_hir(
tcx.emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
id,
span,
DelayDm(|| {
format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
cv.ty(),
cv.ty(),
)
}),
|lint| lint,
IndirectStructuralMatch { non_sm_ty: cv.ty() },
);
}
// Since we are behind a reference, we can just bubble the error up so we get a
@ -357,18 +304,9 @@ impl<'tcx> ConstToPat<'tcx> {
adt_def,
cv.ty()
);
let path = tcx.def_path_str(adt_def.did());
let msg = format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
path, path,
);
self.saw_const_match_error.set(true);
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
tcx.sess.delay_span_bug(span, &msg);
}
let err = TypeNotStructural { span, non_sm_ty: cv.ty() };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
ty::Adt(adt_def, substs) if adt_def.is_enum() => {
@ -401,12 +339,8 @@ impl<'tcx> ConstToPat<'tcx> {
// These are not allowed and will error elsewhere anyway.
ty::Dynamic(..) => {
self.saw_const_match_error.set(true);
let msg = format!("`{}` cannot be used in patterns", cv.ty());
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
tcx.sess.delay_span_bug(span, &msg);
}
let err = InvalidPattern { span, non_sm_ty: cv.ty() };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
// `&str` is represented as `ConstValue::Slice`, let's keep using this
@ -471,32 +405,26 @@ impl<'tcx> ConstToPat<'tcx> {
// this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a
// reference. This makes the rest of the matching logic simpler as it doesn't have
// to figure out how to get a reference again.
ty::Adt(adt_def, _) if !self.type_marked_structural(*pointee_ty) => {
ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => {
if self.behind_reference.get() {
if self.include_lint_checks
&& !self.saw_const_match_error.get()
&& !self.saw_const_match_lint.get()
{
self.saw_const_match_lint.set(true);
let msg = self.adt_derive_msg(adt_def);
self.tcx().struct_span_lint_hir(
self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
self.span,
msg,
|lint| lint,
span,
IndirectStructuralMatch { non_sm_ty: *pointee_ty },
);
}
PatKind::Constant { value: cv }
} else {
if !self.saw_const_match_error.get() {
self.saw_const_match_error.set(true);
let msg = self.adt_derive_msg(adt_def);
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
tcx.sess.delay_span_bug(span, &msg);
}
let err = TypeNotStructural { span, non_sm_ty: *pointee_ty };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
}
PatKind::Wild
}
@ -507,13 +435,11 @@ impl<'tcx> ConstToPat<'tcx> {
_ => {
if !pointee_ty.is_sized(tcx, param_env) {
// `tcx.deref_mir_constant()` below will ICE with an unsized type
// (except slices, which are handled in a separate arm above).
let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty);
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
tcx.sess.delay_span_bug(span, &msg);
}
// (except slices, which are handled in a separate arm above).
let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
} else {
let old = self.behind_reference.replace(true);
@ -545,27 +471,19 @@ impl<'tcx> ConstToPat<'tcx> {
&& !self.saw_const_match_lint.get()
{
self.saw_const_match_lint.set(true);
let msg = "function pointers and unsized pointers in patterns behave \
unpredictably and should not be relied upon. \
See https://github.com/rust-lang/rust/issues/70861 for details.";
tcx.struct_span_lint_hir(
tcx.emit_spanned_lint(
lint::builtin::POINTER_STRUCTURAL_MATCH,
id,
span,
msg,
|lint| lint,
PointerPattern
);
}
PatKind::Constant { value: cv }
}
_ => {
self.saw_const_match_error.set(true);
let msg = format!("`{}` cannot be used in patterns", cv.ty());
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
tcx.sess.delay_span_bug(span, &msg);
}
let err = InvalidPattern { span, non_sm_ty: cv.ty() };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
};
@ -576,21 +494,17 @@ impl<'tcx> ConstToPat<'tcx> {
&& mir_structural_match_violation
// FIXME(#73448): Find a way to bring const qualification into parity with
// `search_for_structural_match_violation` and then remove this condition.
&& self.search_for_structural_match_violation(cv.ty()).is_some()
{
self.saw_const_match_lint.set(true);
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
// could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
let msg = self.search_for_structural_match_violation(cv.ty()).unwrap().replace(
"in a pattern,",
"in a pattern, the constant's initializer must be trivial or",
);
tcx.struct_span_lint_hir(
&& let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty())
{
self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint(
lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
id,
span,
msg,
|lint| lint,
NontrivialStructuralMatch {non_sm_ty}
);
}