don't peel ADTs the pattern could match
This is the use for the previous commits' refactors; see the messages there for more information.
This commit is contained in:
parent
1f40e9aa6a
commit
923d95cc9f
4 changed files with 114 additions and 14 deletions
|
@ -9,6 +9,7 @@ use rustc_errors::{
|
|||
Applicability, Diag, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err,
|
||||
};
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
|
||||
use rustc_hir::{
|
||||
self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatExpr,
|
||||
|
@ -179,8 +180,16 @@ enum PeelKind {
|
|||
/// ADTs. In order to peel only as much as necessary for the pattern to match, the `until_adt`
|
||||
/// field contains the ADT def that the pattern is a constructor for, if applicable, so that we
|
||||
/// don't peel it. See [`ResolvedPat`] for more information.
|
||||
// TODO: add `ResolvedPat` and `until_adt`.
|
||||
Implicit,
|
||||
Implicit { until_adt: Option<DefId> },
|
||||
}
|
||||
|
||||
impl AdjustMode {
|
||||
const fn peel_until_adt(opt_adt_def: Option<DefId>) -> AdjustMode {
|
||||
AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def } }
|
||||
}
|
||||
const fn peel_all() -> AdjustMode {
|
||||
AdjustMode::peel_until_adt(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference.
|
||||
|
@ -294,7 +303,7 @@ impl<'tcx> ResolvedPat<'tcx> {
|
|||
// The remaining possible resolutions for path, struct, and tuple struct patterns are
|
||||
// ADT constructors. As such, we may peel references freely, but we must not peel the
|
||||
// ADT itself from the scrutinee if it's a smart pointer.
|
||||
AdjustMode::Peel { kind: PeelKind::Implicit }
|
||||
AdjustMode::peel_until_adt(self.ty.ty_adt_def().map(|adt| adt.did()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -521,14 +530,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
|
||||
// examples in `tests/ui/pattern/deref_patterns/`.
|
||||
_ if self.tcx.features().deref_patterns()
|
||||
&& let AdjustMode::Peel { kind: PeelKind::Implicit } = adjust_mode
|
||||
&& let AdjustMode::Peel { kind: PeelKind::Implicit { until_adt } } = adjust_mode
|
||||
&& pat.default_binding_modes
|
||||
// For simplicity, only apply overloaded derefs if `expected` is a known ADT.
|
||||
// FIXME(deref_patterns): we'll get better diagnostics for users trying to
|
||||
// implicitly deref generics if we allow them here, but primitives, tuples, and
|
||||
// inference vars definitely should be stopped. Figure out what makes most sense.
|
||||
// TODO: stop peeling if the pattern is a constructor for the scrutinee type
|
||||
&& expected.is_adt()
|
||||
&& let ty::Adt(scrutinee_adt, _) = *expected.kind()
|
||||
// Don't peel if the pattern type already matches the scrutinee. E.g., stop here if
|
||||
// matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
|
||||
&& until_adt != Some(scrutinee_adt.did())
|
||||
// At this point, the pattern isn't able to match `expected` without peeling. Check
|
||||
// that it implements `Deref` before assuming it's a smart pointer, to get a normal
|
||||
// type error instead of a missing impl error if not. This only checks for `Deref`,
|
||||
|
@ -637,22 +648,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
match &pat.kind {
|
||||
// Type checking these product-like types successfully always require
|
||||
// that the expected type be of those types and not reference types.
|
||||
PatKind::Struct(..)
|
||||
| PatKind::TupleStruct(..)
|
||||
| PatKind::Tuple(..)
|
||||
PatKind::Tuple(..)
|
||||
| PatKind::Range(..)
|
||||
| PatKind::Slice(..) => AdjustMode::Peel { kind: PeelKind::Implicit },
|
||||
| PatKind::Slice(..) => AdjustMode::peel_all(),
|
||||
// When checking an explicit deref pattern, only peel reference types.
|
||||
// FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box
|
||||
// patterns may want `PeelKind::Implicit`, stopping on encountering a box.
|
||||
| PatKind::Box(_)
|
||||
| PatKind::Deref(_) => AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat },
|
||||
// A never pattern behaves somewhat like a literal or unit variant.
|
||||
PatKind::Never => AdjustMode::Peel { kind: PeelKind::Implicit },
|
||||
PatKind::Never => AdjustMode::peel_all(),
|
||||
// For patterns with paths, how we peel the scrutinee depends on the path's resolution.
|
||||
PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => {
|
||||
PatKind::Struct(..)
|
||||
| PatKind::TupleStruct(..)
|
||||
| PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => {
|
||||
// If there was an error resolving the path, default to peeling everything.
|
||||
opt_path_res.unwrap().map_or(AdjustMode::Peel { kind: PeelKind::Implicit }, |pr| pr.adjust_mode())
|
||||
opt_path_res.unwrap().map_or(AdjustMode::peel_all(), |pr| pr.adjust_mode())
|
||||
}
|
||||
|
||||
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
|
||||
|
@ -662,7 +673,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Call `resolve_vars_if_possible` here for inline const blocks.
|
||||
PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() {
|
||||
ty::Ref(..) => AdjustMode::Pass,
|
||||
_ => AdjustMode::Peel { kind: PeelKind::Implicit },
|
||||
_ => {
|
||||
// Path patterns have already been handled, and inline const blocks currently
|
||||
// aren't possible to write, so any handling for them would be untested.
|
||||
if cfg!(debug_assertions)
|
||||
&& self.tcx.features().deref_patterns()
|
||||
&& !matches!(lt.kind, PatExprKind::Lit { .. })
|
||||
{
|
||||
span_bug!(lt.span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}", lt.kind);
|
||||
}
|
||||
AdjustMode::peel_all()
|
||||
}
|
||||
},
|
||||
|
||||
// Ref patterns are complicated, we handle them in `check_pat_ref`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue