Skip to content

Commit 923d95c

Browse files
committed
don't peel ADTs the pattern could match
This is the use for the previous commits' refactors; see the messages there for more information.
1 parent 1f40e9a commit 923d95c

File tree

4 files changed

+114
-14
lines changed

4 files changed

+114
-14
lines changed

compiler/rustc_hir_typeck/src/pat.rs

+35-14
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_errors::{
99
Applicability, Diag, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err,
1010
};
1111
use rustc_hir::def::{CtorKind, DefKind, Res};
12+
use rustc_hir::def_id::DefId;
1213
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
1314
use rustc_hir::{
1415
self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatExpr,
@@ -179,8 +180,16 @@ enum PeelKind {
179180
/// ADTs. In order to peel only as much as necessary for the pattern to match, the `until_adt`
180181
/// field contains the ADT def that the pattern is a constructor for, if applicable, so that we
181182
/// don't peel it. See [`ResolvedPat`] for more information.
182-
// TODO: add `ResolvedPat` and `until_adt`.
183-
Implicit,
183+
Implicit { until_adt: Option<DefId> },
184+
}
185+
186+
impl AdjustMode {
187+
const fn peel_until_adt(opt_adt_def: Option<DefId>) -> AdjustMode {
188+
AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def } }
189+
}
190+
const fn peel_all() -> AdjustMode {
191+
AdjustMode::peel_until_adt(None)
192+
}
184193
}
185194

186195
/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference.
@@ -294,7 +303,7 @@ impl<'tcx> ResolvedPat<'tcx> {
294303
// The remaining possible resolutions for path, struct, and tuple struct patterns are
295304
// ADT constructors. As such, we may peel references freely, but we must not peel the
296305
// ADT itself from the scrutinee if it's a smart pointer.
297-
AdjustMode::Peel { kind: PeelKind::Implicit }
306+
AdjustMode::peel_until_adt(self.ty.ty_adt_def().map(|adt| adt.did()))
298307
}
299308
}
300309
}
@@ -521,14 +530,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
521530
// If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
522531
// examples in `tests/ui/pattern/deref_patterns/`.
523532
_ if self.tcx.features().deref_patterns()
524-
&& let AdjustMode::Peel { kind: PeelKind::Implicit } = adjust_mode
533+
&& let AdjustMode::Peel { kind: PeelKind::Implicit { until_adt } } = adjust_mode
525534
&& pat.default_binding_modes
526535
// For simplicity, only apply overloaded derefs if `expected` is a known ADT.
527536
// FIXME(deref_patterns): we'll get better diagnostics for users trying to
528537
// implicitly deref generics if we allow them here, but primitives, tuples, and
529538
// inference vars definitely should be stopped. Figure out what makes most sense.
530-
// TODO: stop peeling if the pattern is a constructor for the scrutinee type
531-
&& expected.is_adt()
539+
&& let ty::Adt(scrutinee_adt, _) = *expected.kind()
540+
// Don't peel if the pattern type already matches the scrutinee. E.g., stop here if
541+
// matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
542+
&& until_adt != Some(scrutinee_adt.did())
532543
// At this point, the pattern isn't able to match `expected` without peeling. Check
533544
// that it implements `Deref` before assuming it's a smart pointer, to get a normal
534545
// 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> {
637648
match &pat.kind {
638649
// Type checking these product-like types successfully always require
639650
// that the expected type be of those types and not reference types.
640-
PatKind::Struct(..)
641-
| PatKind::TupleStruct(..)
642-
| PatKind::Tuple(..)
651+
PatKind::Tuple(..)
643652
| PatKind::Range(..)
644-
| PatKind::Slice(..) => AdjustMode::Peel { kind: PeelKind::Implicit },
653+
| PatKind::Slice(..) => AdjustMode::peel_all(),
645654
// When checking an explicit deref pattern, only peel reference types.
646655
// FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box
647656
// patterns may want `PeelKind::Implicit`, stopping on encountering a box.
648657
| PatKind::Box(_)
649658
| PatKind::Deref(_) => AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat },
650659
// A never pattern behaves somewhat like a literal or unit variant.
651-
PatKind::Never => AdjustMode::Peel { kind: PeelKind::Implicit },
660+
PatKind::Never => AdjustMode::peel_all(),
652661
// For patterns with paths, how we peel the scrutinee depends on the path's resolution.
653-
PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => {
662+
PatKind::Struct(..)
663+
| PatKind::TupleStruct(..)
664+
| PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => {
654665
// If there was an error resolving the path, default to peeling everything.
655-
opt_path_res.unwrap().map_or(AdjustMode::Peel { kind: PeelKind::Implicit }, |pr| pr.adjust_mode())
666+
opt_path_res.unwrap().map_or(AdjustMode::peel_all(), |pr| pr.adjust_mode())
656667
}
657668

658669
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
@@ -662,7 +673,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
662673
// Call `resolve_vars_if_possible` here for inline const blocks.
663674
PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() {
664675
ty::Ref(..) => AdjustMode::Pass,
665-
_ => AdjustMode::Peel { kind: PeelKind::Implicit },
676+
_ => {
677+
// Path patterns have already been handled, and inline const blocks currently
678+
// aren't possible to write, so any handling for them would be untested.
679+
if cfg!(debug_assertions)
680+
&& self.tcx.features().deref_patterns()
681+
&& !matches!(lt.kind, PatExprKind::Lit { .. })
682+
{
683+
span_bug!(lt.span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}", lt.kind);
684+
}
685+
AdjustMode::peel_all()
686+
}
666687
},
667688

668689
// Ref patterns are complicated, we handle them in `check_pat_ref`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//! Test that we get an error about structural equality rather than a type error when attempting to
2+
//! use const patterns of library pointer types. Currently there aren't any smart pointers that can
3+
//! be used in constant patterns, but we still need to make sure we don't implicitly dereference the
4+
//! scrutinee and end up with a type error; this would prevent us from reporting that only constants
5+
//! supporting structural equality can be used as patterns.
6+
#![feature(deref_patterns)]
7+
#![allow(incomplete_features)]
8+
9+
const EMPTY: Vec<()> = Vec::new();
10+
11+
fn main() {
12+
// FIXME(inline_const_pat): if `inline_const_pat` is reinstated, there should be a case here for
13+
// inline const block patterns as well; they're checked differently than named constants.
14+
match vec![()] {
15+
EMPTY => {}
16+
//~^ ERROR: constant of non-structural type `Vec<()>` in a pattern
17+
_ => {}
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: constant of non-structural type `Vec<()>` in a pattern
2+
--> $DIR/implicit-const-deref.rs:15:9
3+
|
4+
LL | const EMPTY: Vec<()> = Vec::new();
5+
| -------------------- constant defined here
6+
...
7+
LL | EMPTY => {}
8+
| ^^^^^ constant of non-structural type
9+
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
10+
|
11+
= note: `Vec<()>` must be annotated with `#[derive(PartialEq)]` to be usable in patterns
12+
|
13+
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
14+
15+
error: aborting due to 1 previous error
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//@ run-pass
2+
//! Test that implicit deref patterns interact as expected with `Cow` constructor patterns.
3+
#![feature(deref_patterns)]
4+
#![allow(incomplete_features)]
5+
6+
use std::borrow::Cow;
7+
8+
fn main() {
9+
let cow: Cow<'static, [u8]> = Cow::from(&[1, 2, 3]);
10+
11+
match cow {
12+
[..] => {}
13+
_ => unreachable!(),
14+
}
15+
16+
match cow {
17+
Cow::Borrowed(_) => {}
18+
Cow::Owned(_) => unreachable!(),
19+
}
20+
21+
match Box::new(&cow) {
22+
Cow::Borrowed { 0: _ } => {}
23+
Cow::Owned { 0: _ } => unreachable!(),
24+
_ => unreachable!(),
25+
}
26+
27+
let cow_of_cow: Cow<'_, Cow<'static, [u8]>> = Cow::Owned(cow);
28+
29+
match cow_of_cow {
30+
[..] => {}
31+
_ => unreachable!(),
32+
}
33+
34+
match cow_of_cow {
35+
Cow::Borrowed(_) => unreachable!(),
36+
Cow::Owned(_) => {}
37+
}
38+
39+
match Box::new(&cow_of_cow) {
40+
Cow::Borrowed { 0: _ } => unreachable!(),
41+
Cow::Owned { 0: _ } => {}
42+
_ => unreachable!(),
43+
}
44+
}

0 commit comments

Comments
 (0)