Auto merge of #70743 - oli-obk:eager_const_to_pat_conversion, r=eddyb
Fully destructure constants into patterns
r? `@varkor`
as discussed in 192789924
we should probably crater it once reviewed
This commit is contained in:
commit
fd15e6180d
66 changed files with 796 additions and 435 deletions
|
@ -3,7 +3,7 @@ use rustc_index::vec::Idx;
|
|||
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
||||
use rustc_middle::mir::Field;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::traits::predicate_for_trait_def;
|
||||
|
@ -28,10 +28,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
|
||||
debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
|
||||
|
||||
self.tcx.infer_ctxt().enter(|infcx| {
|
||||
let pat = self.tcx.infer_ctxt().enter(|infcx| {
|
||||
let mut convert = ConstToPat::new(self, id, span, infcx);
|
||||
convert.to_pat(cv, mir_structural_match_violation)
|
||||
})
|
||||
});
|
||||
|
||||
debug!("const_to_pat: pat={:?}", pat);
|
||||
pat
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,17 +43,44 @@ struct ConstToPat<'a, 'tcx> {
|
|||
span: Span,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
||||
// This tracks if we signal some hard error for a given const value, so that
|
||||
// This tracks if we emitted some hard error for a given const value, so that
|
||||
// we will not subsequently issue an irrelevant lint for the same const
|
||||
// value.
|
||||
saw_const_match_error: Cell<bool>,
|
||||
|
||||
// This tracks if we emitted some diagnostic for a given const value, so that
|
||||
// we will not subsequently issue an irrelevant lint for the same const
|
||||
// value.
|
||||
saw_const_match_lint: Cell<bool>,
|
||||
|
||||
// For backcompat we need to keep allowing non-structurally-eq types behind references.
|
||||
// See also all the `cant-hide-behind` tests.
|
||||
behind_reference: Cell<bool>,
|
||||
|
||||
// inference context used for checking `T: Structural` bounds.
|
||||
infcx: InferCtxt<'a, 'tcx>,
|
||||
|
||||
include_lint_checks: bool,
|
||||
}
|
||||
|
||||
mod fallback_to_const_ref {
|
||||
#[derive(Debug)]
|
||||
/// This error type signals that we encountered a non-struct-eq situation behind a reference.
|
||||
/// We bubble this up in order to get back to the reference destructuring and make that emit
|
||||
/// a const pattern instead of a deref pattern. This allows us to simply call `PartialEq::eq`
|
||||
/// on such patterns (since that function takes a reference) and not have to jump through any
|
||||
/// hoops to get a reference to the value.
|
||||
pub(super) struct FallbackToConstRef(());
|
||||
|
||||
pub(super) fn fallback_to_const_ref<'a, 'tcx>(
|
||||
c2p: &super::ConstToPat<'a, 'tcx>,
|
||||
) -> FallbackToConstRef {
|
||||
assert!(c2p.behind_reference.get());
|
||||
FallbackToConstRef(())
|
||||
}
|
||||
}
|
||||
use fallback_to_const_ref::{fallback_to_const_ref, FallbackToConstRef};
|
||||
|
||||
impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
|
||||
fn new(
|
||||
pat_ctxt: &PatCtxt<'_, 'tcx>,
|
||||
|
@ -65,6 +95,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
|
|||
param_env: pat_ctxt.param_env,
|
||||
include_lint_checks: pat_ctxt.include_lint_checks,
|
||||
saw_const_match_error: Cell::new(false),
|
||||
saw_const_match_lint: Cell::new(false),
|
||||
behind_reference: Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,11 +104,44 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
|
|||
self.infcx.tcx
|
||||
}
|
||||
|
||||
fn search_for_structural_match_violation(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<traits::NonStructuralMatchTy<'tcx>> {
|
||||
traits::search_for_structural_match_violation(self.id, self.span, self.tcx(), ty)
|
||||
fn adt_derive_msg(&self, adt_def: &AdtDef) -> 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.id, self.span, self.tcx(), ty).map(
|
||||
|non_sm_ty| {
|
||||
with_no_trimmed_paths(|| match non_sm_ty {
|
||||
traits::NonStructuralMatchTy::Adt(adt) => self.adt_derive_msg(adt),
|
||||
traits::NonStructuralMatchTy::Dynamic => {
|
||||
"trait objects cannot be used in patterns".to_string()
|
||||
}
|
||||
traits::NonStructuralMatchTy::Opaque => {
|
||||
"opaque types cannot be used in patterns".to_string()
|
||||
}
|
||||
traits::NonStructuralMatchTy::Generator => {
|
||||
"generators cannot be used in patterns".to_string()
|
||||
}
|
||||
traits::NonStructuralMatchTy::Closure => {
|
||||
"closures cannot be used in patterns".to_string()
|
||||
}
|
||||
traits::NonStructuralMatchTy::Param => {
|
||||
bug!("use of a constant whose type is a parameter inside a pattern")
|
||||
}
|
||||
traits::NonStructuralMatchTy::Projection => {
|
||||
bug!("use of a constant whose type is a projection inside a pattern")
|
||||
}
|
||||
traits::NonStructuralMatchTy::Foreign => {
|
||||
bug!("use of a value of a foreign type inside a pattern")
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
|
||||
|
@ -95,7 +160,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
|
|||
// once indirect_structural_match is a full fledged error, this
|
||||
// level of indirection can be eliminated
|
||||
|
||||
let inlined_const_as_pat = self.recur(cv);
|
||||
let inlined_const_as_pat = self.recur(cv, mir_structural_match_violation).unwrap();
|
||||
|
||||
if self.include_lint_checks && !self.saw_const_match_error.get() {
|
||||
// If we were able to successfully convert the const to some pat,
|
||||
|
@ -118,70 +183,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
|
|||
return inlined_const_as_pat;
|
||||
}
|
||||
|
||||
if let Some(non_sm_ty) = structural {
|
||||
let msg = with_no_trimmed_paths(|| match non_sm_ty {
|
||||
traits::NonStructuralMatchTy::Adt(adt_def) => {
|
||||
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,
|
||||
)
|
||||
}
|
||||
traits::NonStructuralMatchTy::Dynamic => {
|
||||
"trait objects cannot be used in patterns".to_string()
|
||||
}
|
||||
traits::NonStructuralMatchTy::Opaque => {
|
||||
"opaque types cannot be used in patterns".to_string()
|
||||
}
|
||||
traits::NonStructuralMatchTy::Generator => {
|
||||
"generators cannot be used in patterns".to_string()
|
||||
}
|
||||
traits::NonStructuralMatchTy::Closure => {
|
||||
"closures cannot be used in patterns".to_string()
|
||||
}
|
||||
traits::NonStructuralMatchTy::Param => {
|
||||
bug!("use of a constant whose type is a parameter inside a pattern")
|
||||
}
|
||||
traits::NonStructuralMatchTy::Projection => {
|
||||
bug!("use of a constant whose type is a projection inside a pattern")
|
||||
}
|
||||
traits::NonStructuralMatchTy::Foreign => {
|
||||
bug!("use of a value of a foreign type inside a pattern")
|
||||
}
|
||||
});
|
||||
|
||||
// double-check there even *is* a semantic `PartialEq` to dispatch to.
|
||||
//
|
||||
// (If there isn't, then we can safely issue a hard
|
||||
// error, because that's never worked, due to compiler
|
||||
// using `PartialEq::eq` in this scenario in the past.)
|
||||
//
|
||||
// Note: To fix rust-lang/rust#65466, one could lift this check
|
||||
// *before* any structural-match checking, and unconditionally error
|
||||
// if `PartialEq` is not implemented. However, that breaks stable
|
||||
// code at the moment, because types like `for <'a> fn(&'a ())` do
|
||||
// not *yet* implement `PartialEq`. So for now we leave this here.
|
||||
let ty_is_partial_eq: bool = {
|
||||
let partial_eq_trait_id =
|
||||
self.tcx().require_lang_item(hir::LangItem::PartialEq, Some(self.span));
|
||||
let obligation: PredicateObligation<'_> = predicate_for_trait_def(
|
||||
self.tcx(),
|
||||
self.param_env,
|
||||
ObligationCause::misc(self.span, self.id),
|
||||
partial_eq_trait_id,
|
||||
0,
|
||||
cv.ty,
|
||||
&[],
|
||||
);
|
||||
// FIXME: should this call a `predicate_must_hold` variant instead?
|
||||
self.infcx.predicate_may_hold(&obligation)
|
||||
};
|
||||
|
||||
if !ty_is_partial_eq {
|
||||
if let Some(msg) = 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);
|
||||
} else if mir_structural_match_violation {
|
||||
} else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
|
||||
self.tcx().struct_span_lint_hir(
|
||||
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
|
||||
self.id,
|
||||
|
@ -200,19 +206,57 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
|
|||
inlined_const_as_pat
|
||||
}
|
||||
|
||||
fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
|
||||
// double-check there even *is* a semantic `PartialEq` to dispatch to.
|
||||
//
|
||||
// (If there isn't, then we can safely issue a hard
|
||||
// error, because that's never worked, due to compiler
|
||||
// using `PartialEq::eq` in this scenario in the past.)
|
||||
let partial_eq_trait_id =
|
||||
self.tcx().require_lang_item(hir::LangItem::PartialEq, Some(self.span));
|
||||
let obligation: PredicateObligation<'_> = predicate_for_trait_def(
|
||||
self.tcx(),
|
||||
self.param_env,
|
||||
ObligationCause::misc(self.span, self.id),
|
||||
partial_eq_trait_id,
|
||||
0,
|
||||
ty,
|
||||
&[],
|
||||
);
|
||||
// FIXME: should this call a `predicate_must_hold` variant instead?
|
||||
|
||||
let has_impl = self.infcx.predicate_may_hold(&obligation);
|
||||
|
||||
// Note: To fix rust-lang/rust#65466, we could just remove this type
|
||||
// walk hack for function pointers, and unconditionally error
|
||||
// if `PartialEq` is not implemented. However, that breaks stable
|
||||
// code at the moment, because types like `for <'a> fn(&'a ())` do
|
||||
// not *yet* implement `PartialEq`. So for now we leave this here.
|
||||
has_impl
|
||||
|| ty.walk().any(|t| match t.unpack() {
|
||||
ty::subst::GenericArgKind::Lifetime(_) => false,
|
||||
ty::subst::GenericArgKind::Type(t) => t.is_fn_ptr(),
|
||||
ty::subst::GenericArgKind::Const(_) => false,
|
||||
})
|
||||
}
|
||||
|
||||
// Recursive helper for `to_pat`; invoke that (instead of calling this directly).
|
||||
fn recur(&self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
|
||||
fn recur(
|
||||
&self,
|
||||
cv: &'tcx ty::Const<'tcx>,
|
||||
mir_structural_match_violation: bool,
|
||||
) -> Result<Pat<'tcx>, FallbackToConstRef> {
|
||||
let id = self.id;
|
||||
let span = self.span;
|
||||
let tcx = self.tcx();
|
||||
let param_env = self.param_env;
|
||||
|
||||
let field_pats = |vals: &[&'tcx ty::Const<'tcx>]| {
|
||||
let field_pats = |vals: &[&'tcx ty::Const<'tcx>]| -> Result<_, _> {
|
||||
vals.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, val)| {
|
||||
let field = Field::new(idx);
|
||||
FieldPat { field, pattern: self.recur(val) }
|
||||
Ok(FieldPat { field, pattern: self.recur(val, false)? })
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
@ -230,10 +274,63 @@ impl<'a, 'tcx> ConstToPat<'a, '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);
|
||||
tcx.sess.span_err(span, "cannot use unions in constant patterns");
|
||||
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)
|
||||
}
|
||||
PatKind::Wild
|
||||
}
|
||||
// keep old code until future-compat upgraded to errors.
|
||||
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 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)
|
||||
}
|
||||
PatKind::Wild
|
||||
}
|
||||
// If the type is not structurally comparable, just emit the constant directly,
|
||||
// causing the pattern match code to treat it opaquely.
|
||||
// FIXME: This code doesn't emit errors itself, the caller emits the errors.
|
||||
// So instead of specific errors, you just get blanket errors about the whole
|
||||
// const type. See
|
||||
// https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for
|
||||
// details.
|
||||
// Backwards compatibility hack because we can't cause hard errors on these
|
||||
// types, so we compare them via `PartialEq::eq` at runtime.
|
||||
ty::Adt(..) if !self.type_marked_structural(cv.ty) && 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 = format!(
|
||||
"to use a constant of type `{}` in a pattern, \
|
||||
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
|
||||
cv.ty, cv.ty,
|
||||
);
|
||||
tcx.struct_span_lint_hir(
|
||||
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
|
||||
id,
|
||||
span,
|
||||
|lint| lint.build(&msg).emit(),
|
||||
);
|
||||
}
|
||||
// Since we are behind a reference, we can just bubble the error up so we get a
|
||||
// constant at reference type, making it easy to let the fallback call
|
||||
// `PartialEq::eq` on it.
|
||||
return Err(fallback_to_const_ref(self));
|
||||
}
|
||||
ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty) => {
|
||||
debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, cv.ty);
|
||||
let path = tcx.def_path_str(adt_def.did);
|
||||
|
@ -243,29 +340,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
|
|||
path, path,
|
||||
);
|
||||
self.saw_const_match_error.set(true);
|
||||
tcx.sess.span_err(span, &msg);
|
||||
PatKind::Wild
|
||||
}
|
||||
// keep old code until future-compat upgraded to errors.
|
||||
ty::Ref(_, adt_ty, _) if adt_ty.is_adt() && !self.type_marked_structural(adt_ty) => {
|
||||
let adt_def =
|
||||
if let ty::Adt(adt_def, _) = adt_ty.kind() { adt_def } else { unreachable!() };
|
||||
|
||||
debug!(
|
||||
"adt_def {:?} has !type_marked_structural for adt_ty: {:?}",
|
||||
adt_def, adt_ty
|
||||
);
|
||||
|
||||
// HACK(estebank): Side-step ICE #53708, but anything other than erroring here
|
||||
// would be wrong. Returnging `PatKind::Wild` is not technically correct.
|
||||
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);
|
||||
tcx.sess.span_err(span, &msg);
|
||||
if self.include_lint_checks {
|
||||
tcx.sess.span_err(span, &msg);
|
||||
} else {
|
||||
tcx.sess.delay_span_bug(span, &msg)
|
||||
}
|
||||
PatKind::Wild
|
||||
}
|
||||
ty::Adt(adt_def, substs) if adt_def.is_enum() => {
|
||||
|
@ -276,30 +355,181 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
|
|||
variant_index: destructured
|
||||
.variant
|
||||
.expect("destructed const of adt without variant id"),
|
||||
subpatterns: field_pats(destructured.fields),
|
||||
subpatterns: field_pats(destructured.fields)?,
|
||||
}
|
||||
}
|
||||
ty::Adt(_, _) => {
|
||||
ty::Tuple(_) | ty::Adt(_, _) => {
|
||||
let destructured = tcx.destructure_const(param_env.and(cv));
|
||||
PatKind::Leaf { subpatterns: field_pats(destructured.fields) }
|
||||
}
|
||||
ty::Tuple(_) => {
|
||||
let destructured = tcx.destructure_const(param_env.and(cv));
|
||||
PatKind::Leaf { subpatterns: field_pats(destructured.fields) }
|
||||
PatKind::Leaf { subpatterns: field_pats(destructured.fields)? }
|
||||
}
|
||||
ty::Array(..) => PatKind::Array {
|
||||
prefix: tcx
|
||||
.destructure_const(param_env.and(cv))
|
||||
.fields
|
||||
.iter()
|
||||
.map(|val| self.recur(val))
|
||||
.collect(),
|
||||
.map(|val| self.recur(val, false))
|
||||
.collect::<Result<_, _>>()?,
|
||||
slice: None,
|
||||
suffix: Vec::new(),
|
||||
},
|
||||
_ => PatKind::Constant { value: cv },
|
||||
ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
|
||||
// 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)
|
||||
}
|
||||
PatKind::Wild
|
||||
}
|
||||
// `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this
|
||||
// optimization for now.
|
||||
ty::Str => PatKind::Constant { value: cv },
|
||||
ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
|
||||
// `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
|
||||
// matching against references, you can only use byte string literals.
|
||||
// FIXME: clean this up, likely by permitting array patterns when matching on slices
|
||||
ty::Array(elem_ty, _) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
|
||||
// Cannot merge this with the catch all branch below, because the `const_deref`
|
||||
// changes the type from slice to array, and slice patterns behave differently from
|
||||
// array patterns.
|
||||
ty::Slice(..) => {
|
||||
let old = self.behind_reference.replace(true);
|
||||
let array = tcx.deref_const(self.param_env.and(cv));
|
||||
let val = PatKind::Deref {
|
||||
subpattern: Pat {
|
||||
kind: Box::new(PatKind::Slice {
|
||||
prefix: tcx
|
||||
.destructure_const(param_env.and(array))
|
||||
.fields
|
||||
.iter()
|
||||
.map(|val| self.recur(val, false))
|
||||
.collect::<Result<_, _>>()?,
|
||||
slice: None,
|
||||
suffix: vec![],
|
||||
}),
|
||||
span,
|
||||
ty: pointee_ty,
|
||||
},
|
||||
};
|
||||
self.behind_reference.set(old);
|
||||
val
|
||||
}
|
||||
// Backwards compatibility hack: support references to non-structural types.
|
||||
// We'll lower
|
||||
// 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) => {
|
||||
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(
|
||||
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
|
||||
self.id,
|
||||
self.span,
|
||||
|lint| lint.build(&msg).emit(),
|
||||
);
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
PatKind::Wild
|
||||
}
|
||||
}
|
||||
// All other references are converted into deref patterns and then recursively
|
||||
// convert the dereferenced constant to a pattern that is the sub-pattern of the
|
||||
// deref pattern.
|
||||
_ => {
|
||||
let old = self.behind_reference.replace(true);
|
||||
// In case there are structural-match violations somewhere in this subpattern,
|
||||
// we fall back to a const pattern. If we do not do this, we may end up with
|
||||
// a !structural-match constant that is not of reference type, which makes it
|
||||
// very hard to invoke `PartialEq::eq` on it as a fallback.
|
||||
let val = match self.recur(tcx.deref_const(self.param_env.and(cv)), false) {
|
||||
Ok(subpattern) => PatKind::Deref { subpattern },
|
||||
Err(_) => PatKind::Constant { value: cv },
|
||||
};
|
||||
self.behind_reference.set(old);
|
||||
val
|
||||
}
|
||||
},
|
||||
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
|
||||
PatKind::Constant { value: cv }
|
||||
}
|
||||
ty::RawPtr(pointee) if pointee.ty.is_sized(tcx.at(span), param_env) => {
|
||||
PatKind::Constant { value: cv }
|
||||
}
|
||||
// FIXME: these can have very suprising behaviour where optimization levels or other
|
||||
// compilation choices change the runtime behaviour of the match.
|
||||
// See https://github.com/rust-lang/rust/issues/70861 for examples.
|
||||
ty::FnPtr(..) | ty::RawPtr(..) => {
|
||||
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 = "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(
|
||||
lint::builtin::POINTER_STRUCTURAL_MATCH,
|
||||
id,
|
||||
span,
|
||||
|lint| lint.build(&msg).emit(),
|
||||
);
|
||||
}
|
||||
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)
|
||||
}
|
||||
PatKind::Wild
|
||||
}
|
||||
};
|
||||
|
||||
Pat { span, ty: cv.ty, kind: Box::new(kind) }
|
||||
if self.include_lint_checks
|
||||
&& !self.saw_const_match_error.get()
|
||||
&& !self.saw_const_match_lint.get()
|
||||
&& 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(
|
||||
lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
|
||||
id,
|
||||
span,
|
||||
|lint| lint.build(&msg).emit(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Pat { span, ty: cv.ty, kind: Box::new(kind) })
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue