Skip to content

Commit d6df469

Browse files
committed
refactor path pattern checking to get info for peeling
See the doc comment on `ResolvedPat` for more information. This and the next couple commits split resolution apart from checking for path, struct, and tuple struct patterns, in order to find the pattern's type before peeling the scrutinee. This helps us avoid peeling the scrutinee when the pattern could match it. The reason this handles errors from resolution after peeling is for struct and tuple struct patterns: we check their subpatterns even when they fail to resolve, to potentially catch more resolution errors. By doing this after peeling, we're able to use the updated `PatInfo`. I don't know if there's currently any observable difference from using the outdated `PatInfo`, but it could potentially be a source of subtle diagnostic bugs in the future, so I'm opting to peel first.
1 parent 91d0b57 commit d6df469

File tree

1 file changed

+89
-48
lines changed
  • compiler/rustc_hir_typeck/src

1 file changed

+89
-48
lines changed

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 89 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use ty::adjustment::{PatAdjust, PatAdjustment};
3434
use super::report_unexpected_variant_res;
3535
use crate::expectation::Expectation;
3636
use crate::gather_locals::DeclOrigin;
37-
use crate::{FnCtxt, LoweredTy, errors};
37+
use crate::{FnCtxt, errors};
3838

3939
const CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\
4040
This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \
@@ -258,6 +258,44 @@ enum InheritedRefMatchRule {
258258
},
259259
}
260260

261+
/// When checking patterns containing paths, we need to know the path's resolution to determine
262+
/// whether to apply match ergonomics and implicitly dereference the scrutinee. For instance, when
263+
/// the `deref_patterns` feature is enabled and we're matching against a scrutinee of type
264+
/// `Cow<'a, Option<u8>>`, we insert an implicit dereference to allow the pattern `Some(_)` to type,
265+
/// but we must not dereference it when checking the pattern `Cow::Borrowed(_)`.
266+
///
267+
/// `ResolvedPat` contains the information from resolution needed to determine match ergonomics
268+
/// adjustments, and to finish checking the pattern once we know its adjusted type.
269+
#[derive(Clone, Copy, Debug)]
270+
struct ResolvedPat<'tcx> {
271+
/// The type of the pattern, to be checked against the type of the scrutinee after peeling. This
272+
/// is also used to avoid peeling the scrutinee's constructors (see the `Cow` example above).
273+
ty: Ty<'tcx>,
274+
kind: ResolvedPatKind<'tcx>,
275+
}
276+
277+
#[derive(Clone, Copy, Debug)]
278+
enum ResolvedPatKind<'tcx> {
279+
Path { res: Res, pat_res: Res, segments: &'tcx [hir::PathSegment<'tcx>] },
280+
}
281+
282+
impl<'tcx> ResolvedPat<'tcx> {
283+
fn adjust_mode(&self) -> AdjustMode {
284+
let ResolvedPatKind::Path { res, .. } = self.kind;
285+
if matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _)) {
286+
// These constants can be of a reference type, e.g. `const X: &u8 = &0;`.
287+
// Peeling the reference types too early will cause type checking failures.
288+
// Although it would be possible to *also* peel the types of the constants too.
289+
AdjustMode::Pass
290+
} else {
291+
// The remaining possible resolutions for path, struct, and tuple struct patterns are
292+
// ADT constructors. As such, we may peel references freely, but we must not peel the
293+
// ADT itself from the scrutinee if it's a smart pointer.
294+
AdjustMode::Peel { kind: PeelKind::Implicit }
295+
}
296+
}
297+
}
298+
261299
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
262300
/// Experimental pattern feature: after matching against a shared reference, do we limit the
263301
/// default binding mode in subpatterns to be `ref` when it would otherwise be `ref mut`?
@@ -334,13 +372,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
334372
/// Conversely, inside this module, `check_pat_top` should never be used.
335373
#[instrument(level = "debug", skip(self, pat_info))]
336374
fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx>) {
375+
// For patterns containing paths, we need the path's resolution to determine whether to
376+
// implicitly dereference the scrutinee before matching.
337377
let opt_path_res = match pat.kind {
338378
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
339-
Some(self.resolve_ty_and_res_fully_qualified_call(qpath, *hir_id, *span))
379+
Some(self.resolve_pat_path(*hir_id, *span, qpath))
340380
}
341381
_ => None,
342382
};
343-
let adjust_mode = self.calc_adjust_mode(pat, opt_path_res.map(|(res, ..)| res));
383+
let adjust_mode = self.calc_adjust_mode(pat, opt_path_res);
344384
let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info);
345385
self.write_ty(pat.hir_id, ty);
346386

@@ -406,7 +446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
406446
fn check_pat_inner(
407447
&self,
408448
pat: &'tcx Pat<'tcx>,
409-
opt_path_res: Option<(Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>])>,
449+
opt_path_res: Option<Result<ResolvedPat<'tcx>, ErrorGuaranteed>>,
410450
adjust_mode: AdjustMode,
411451
expected: Ty<'tcx>,
412452
pat_info: PatInfo<'tcx>,
@@ -515,16 +555,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
515555
PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected,
516556
// We allow any type here; we ensure that the type is uninhabited during match checking.
517557
PatKind::Never => expected,
518-
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
519-
let ty = self.check_pat_path(
520-
*hir_id,
521-
pat.hir_id,
522-
*span,
523-
qpath,
524-
opt_path_res.unwrap(),
525-
expected,
526-
&pat_info.top_info,
527-
);
558+
PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), hir_id, .. }) => {
559+
let ty = match opt_path_res.unwrap() {
560+
Ok(ref pr) => {
561+
self.check_pat_path(pat.hir_id, pat.span, pr, expected, &pat_info.top_info)
562+
}
563+
Err(guar) => Ty::new_error(self.tcx, guar),
564+
};
528565
self.write_ty(*hir_id, ty);
529566
ty
530567
}
@@ -566,8 +603,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
566603

567604
/// How should the binding mode and expected type be adjusted?
568605
///
569-
/// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
570-
fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode {
606+
/// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`.
607+
fn calc_adjust_mode(
608+
&self,
609+
pat: &'tcx Pat<'tcx>,
610+
opt_path_res: Option<Result<ResolvedPat<'tcx>, ErrorGuaranteed>>,
611+
) -> AdjustMode {
571612
match &pat.kind {
572613
// Type checking these product-like types successfully always require
573614
// that the expected type be of those types and not reference types.
@@ -583,17 +624,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
583624
| PatKind::Deref(_) => AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat },
584625
// A never pattern behaves somewhat like a literal or unit variant.
585626
PatKind::Never => AdjustMode::Peel { kind: PeelKind::Implicit },
586-
PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => match opt_path_res.unwrap() {
587-
// These constants can be of a reference type, e.g. `const X: &u8 = &0;`.
588-
// Peeling the reference types too early will cause type checking failures.
589-
// Although it would be possible to *also* peel the types of the constants too.
590-
Res::Def(DefKind::Const | DefKind::AssocConst, _) => AdjustMode::Pass,
591-
// In the `ValueNS`, we have `SelfCtor(..) | Ctor(_, Const), _)` remaining which
592-
// could successfully compile. The former being `Self` requires a unit struct.
593-
// In either case, and unlike constants, the pattern itself cannot be
594-
// a reference type wherefore peeling doesn't give up any expressiveness.
595-
_ => AdjustMode::Peel { kind: PeelKind::Implicit },
596-
},
627+
// For patterns with paths, how we peel the scrutinee depends on the path's resolution.
628+
PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => {
629+
// If there was an error resolving the path, default to peeling everything.
630+
opt_path_res.unwrap().map_or(AdjustMode::Peel { kind: PeelKind::Implicit }, |pr| pr.adjust_mode())
631+
}
597632

598633
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
599634
// All other literals result in non-reference types.
@@ -1216,31 +1251,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12161251
}
12171252
}
12181253

1219-
fn check_pat_path(
1254+
fn resolve_pat_path(
12201255
&self,
12211256
path_id: HirId,
1222-
pat_id_for_diag: HirId,
12231257
span: Span,
1224-
qpath: &hir::QPath<'_>,
1225-
path_resolution: (Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]),
1226-
expected: Ty<'tcx>,
1227-
ti: &TopInfo<'tcx>,
1228-
) -> Ty<'tcx> {
1258+
qpath: &'tcx hir::QPath<'_>,
1259+
) -> Result<ResolvedPat<'tcx>, ErrorGuaranteed> {
12291260
let tcx = self.tcx;
12301261

1231-
// We have already resolved the path.
1232-
let (res, opt_ty, segments) = path_resolution;
1262+
let (res, opt_ty, segments) =
1263+
self.resolve_ty_and_res_fully_qualified_call(qpath, path_id, span);
12331264
match res {
12341265
Res::Err => {
12351266
let e =
12361267
self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted");
12371268
self.set_tainted_by_errors(e);
1238-
return Ty::new_error(tcx, e);
1269+
return Err(e);
12391270
}
12401271
Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => {
12411272
let expected = "unit struct, unit variant or constant";
12421273
let e = report_unexpected_variant_res(tcx, res, None, qpath, span, E0533, expected);
1243-
return Ty::new_error(tcx, e);
1274+
return Err(e);
12441275
}
12451276
Res::SelfCtor(def_id) => {
12461277
if let ty::Adt(adt_def, _) = *tcx.type_of(def_id).skip_binder().kind()
@@ -1258,7 +1289,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12581289
E0533,
12591290
"unit struct",
12601291
);
1261-
return Ty::new_error(tcx, e);
1292+
return Err(e);
12621293
}
12631294
}
12641295
Res::Def(
@@ -1271,15 +1302,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12711302
_ => bug!("unexpected pattern resolution: {:?}", res),
12721303
}
12731304

1274-
// Type-check the path.
1305+
// Find the type of the path pattern, for later checking.
12751306
let (pat_ty, pat_res) =
12761307
self.instantiate_value_path(segments, opt_ty, res, span, span, path_id);
1308+
Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Path { res, pat_res, segments } })
1309+
}
1310+
1311+
fn check_pat_path(
1312+
&self,
1313+
pat_id_for_diag: HirId,
1314+
span: Span,
1315+
resolved: &ResolvedPat<'tcx>,
1316+
expected: Ty<'tcx>,
1317+
ti: &TopInfo<'tcx>,
1318+
) -> Ty<'tcx> {
12771319
if let Err(err) =
1278-
self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, pat_ty)
1320+
self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, resolved.ty)
12791321
{
1280-
self.emit_bad_pat_path(err, pat_id_for_diag, span, res, pat_res, pat_ty, segments);
1322+
self.emit_bad_pat_path(err, pat_id_for_diag, span, resolved);
12811323
}
1282-
pat_ty
1324+
resolved.ty
12831325
}
12841326

12851327
fn maybe_suggest_range_literal(
@@ -1322,11 +1364,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13221364
mut e: Diag<'_>,
13231365
hir_id: HirId,
13241366
pat_span: Span,
1325-
res: Res,
1326-
pat_res: Res,
1327-
pat_ty: Ty<'tcx>,
1328-
segments: &'tcx [hir::PathSegment<'tcx>],
1367+
resolved_pat: &ResolvedPat<'tcx>,
13291368
) {
1369+
let ResolvedPatKind::Path { res, pat_res, segments } = resolved_pat.kind;
1370+
13301371
if let Some(span) = self.tcx.hir_res_span(pat_res) {
13311372
e.span_label(span, format!("{} defined here", res.descr()));
13321373
if let [hir::PathSegment { ident, .. }] = &*segments {
@@ -1349,7 +1390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13491390
);
13501391
}
13511392
_ => {
1352-
let (type_def_id, item_def_id) = match pat_ty.kind() {
1393+
let (type_def_id, item_def_id) = match resolved_pat.ty.kind() {
13531394
ty::Adt(def, _) => match res {
13541395
Res::Def(DefKind::Const, def_id) => (Some(def.did()), Some(def_id)),
13551396
_ => (None, None),

0 commit comments

Comments
 (0)