Skip to content

Commit 84f1356

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 d73fd50 commit 84f1356

File tree

4 files changed

+116
-21
lines changed

4 files changed

+116
-21
lines changed

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_hir::{
1616
};
1717
use rustc_infer::infer;
1818
use rustc_middle::traits::PatternOriginExpr;
19-
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
19+
use rustc_middle::ty::{self, AdtDef, Ty, TypeVisitableExt};
2020
use rustc_middle::{bug, span_bug};
2121
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
2222
use rustc_session::parse::feature_err;
@@ -161,26 +161,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
161161

162162
/// Mode for adjusting the expected type and binding mode.
163163
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
164-
enum AdjustMode {
164+
enum AdjustMode<'tcx> {
165165
/// Peel off all immediate reference types. If the `deref_patterns` feature is enabled, this
166166
/// also peels smart pointer ADTs.
167-
Peel { kind: PeelKind },
167+
Peel { kind: PeelKind<'tcx> },
168168
/// Pass on the input binding mode and expected type.
169169
Pass,
170170
}
171171

172172
/// Restrictions on what types to peel when adjusting the expected type and binding mode.
173173
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
174-
enum PeelKind {
174+
enum PeelKind<'tcx> {
175175
/// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference
176176
/// any number of `&`/`&mut` references, plus a single smart pointer.
177177
ExplicitDerefPat,
178178
/// Implicitly peel any number of references, and if `deref_patterns` is enabled, smart pointer
179179
/// ADTs. In order to peel only as much as necessary for the pattern to match, the `until_adt`
180180
/// field contains the ADT def that the pattern is a constructor for, if applicable, so that we
181181
/// don't peel it. See [`ResolvedPat`] for more information.
182-
// TODO: add `ResolvedPat` and `until_adt`.
183-
Implicit,
182+
Implicit { until_adt: Option<AdtDef<'tcx>> },
183+
}
184+
185+
impl<'tcx> AdjustMode<'tcx> {
186+
const fn peel_until_adt(opt_adt_def: Option<AdtDef<'tcx>>) -> AdjustMode<'tcx> {
187+
AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def } }
188+
}
189+
const fn peel_all() -> AdjustMode<'tcx> {
190+
AdjustMode::peel_until_adt(None)
191+
}
184192
}
185193

186194
/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference.
@@ -282,7 +290,7 @@ enum ResolvedPatKind<'tcx> {
282290
}
283291

284292
impl<'tcx> ResolvedPat<'tcx> {
285-
fn adjust_mode(&self) -> AdjustMode {
293+
fn adjust_mode(&self) -> AdjustMode<'tcx> {
286294
if let ResolvedPatKind::Path { res, .. } = self.kind
287295
&& matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _))
288296
{
@@ -294,7 +302,7 @@ impl<'tcx> ResolvedPat<'tcx> {
294302
// The remaining possible resolutions for path, struct, and tuple struct patterns are
295303
// ADT constructors. As such, we may peel references freely, but we must not peel the
296304
// ADT itself from the scrutinee if it's a smart pointer.
297-
AdjustMode::Peel { kind: PeelKind::Implicit }
305+
AdjustMode::peel_until_adt(self.ty.ty_adt_def())
298306
}
299307
}
300308
}
@@ -452,7 +460,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
452460
&self,
453461
pat: &'tcx Pat<'tcx>,
454462
opt_path_res: Option<Result<ResolvedPat<'tcx>, ErrorGuaranteed>>,
455-
adjust_mode: AdjustMode,
463+
adjust_mode: AdjustMode<'tcx>,
456464
expected: Ty<'tcx>,
457465
pat_info: PatInfo<'tcx>,
458466
) -> Ty<'tcx> {
@@ -521,14 +529,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
521529
// If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
522530
// examples in `tests/ui/pattern/deref_patterns/`.
523531
_ if self.tcx.features().deref_patterns()
524-
&& let AdjustMode::Peel { kind: PeelKind::Implicit } = adjust_mode
532+
&& let AdjustMode::Peel { kind: PeelKind::Implicit { until_adt } } = adjust_mode
525533
&& pat.default_binding_modes
526534
// For simplicity, only apply overloaded derefs if `expected` is a known ADT.
527535
// FIXME(deref_patterns): we'll get better diagnostics for users trying to
528536
// implicitly deref generics if we allow them here, but primitives, tuples, and
529537
// 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()
538+
&& let ty::Adt(scrutinee_adt, _) = *expected.kind()
539+
// Don't peel if the pattern type already matches the scrutinee. E.g., stop here if
540+
// matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
541+
&& until_adt != Some(scrutinee_adt)
532542
// At this point, the pattern isn't able to match `expected` without peeling. Check
533543
// that it implements `Deref` before assuming it's a smart pointer, to get a normal
534544
// type error instead of a missing impl error if not. This only checks for `Deref`,
@@ -633,26 +643,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
633643
&self,
634644
pat: &'tcx Pat<'tcx>,
635645
opt_path_res: Option<Result<ResolvedPat<'tcx>, ErrorGuaranteed>>,
636-
) -> AdjustMode {
646+
) -> AdjustMode<'tcx> {
637647
match &pat.kind {
638648
// Type checking these product-like types successfully always require
639649
// that the expected type be of those types and not reference types.
640-
PatKind::Struct(..)
641-
| PatKind::TupleStruct(..)
642-
| PatKind::Tuple(..)
650+
PatKind::Tuple(..)
643651
| PatKind::Range(..)
644-
| PatKind::Slice(..) => AdjustMode::Peel { kind: PeelKind::Implicit },
652+
| PatKind::Slice(..) => AdjustMode::peel_all(),
645653
// When checking an explicit deref pattern, only peel reference types.
646654
// FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box
647655
// patterns may want `PeelKind::Implicit`, stopping on encountering a box.
648656
| PatKind::Box(_)
649657
| PatKind::Deref(_) => AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat },
650658
// A never pattern behaves somewhat like a literal or unit variant.
651-
PatKind::Never => AdjustMode::Peel { kind: PeelKind::Implicit },
659+
PatKind::Never => AdjustMode::peel_all(),
652660
// For patterns with paths, how we peel the scrutinee depends on the path's resolution.
653-
PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => {
661+
PatKind::Struct(..)
662+
| PatKind::TupleStruct(..)
663+
| PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => {
654664
// 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())
665+
opt_path_res.unwrap().map_or(AdjustMode::peel_all(), |pr| pr.adjust_mode())
656666
}
657667

658668
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
@@ -662,7 +672,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
662672
// Call `resolve_vars_if_possible` here for inline const blocks.
663673
PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() {
664674
ty::Ref(..) => AdjustMode::Pass,
665-
_ => AdjustMode::Peel { kind: PeelKind::Implicit },
675+
// If an inline const pattern has a library-defined pointer-like type and
676+
// `deref_patterns` is enabled, don't implicitly add deref patterns for its ADT.
677+
// Nothing in the library that implements `DerefPure` supports structural equality,
678+
// but we don't check that until `const_to_pat` in THIR construction. By avoiding
679+
// type errors here, we can get a more meaningful error there.
680+
ty::Adt(adt, _) => AdjustMode::peel_until_adt(Some(*adt)),
681+
_ => AdjustMode::peel_all()
666682
},
667683

668684
// Ref patterns are complicated, we handle them in `check_pat_ref`.
Lines changed: 19 additions & 0 deletions
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+
}
Lines changed: 16 additions & 0 deletions
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+
Lines changed: 44 additions & 0 deletions
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)