Skip to content

Commit f257854

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 c529678 commit f257854

File tree

1 file changed

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

1 file changed

+90
-48
lines changed

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 90 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use ty::VariantDef;
3333
use super::report_unexpected_variant_res;
3434
use crate::expectation::Expectation;
3535
use crate::gather_locals::DeclOrigin;
36-
use crate::{FnCtxt, LoweredTy, errors};
36+
use crate::{FnCtxt, errors};
3737

3838
const CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\
3939
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::Overloaded }
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`?
@@ -336,13 +374,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
336374
fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx>) {
337375
let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info;
338376

339-
let path_res = match pat.kind {
377+
// For patterns containing paths, we need the path's resolution to determine whether to
378+
// implicitly dereference the scrutinee before matching.
379+
let opt_res_pat_or_err = match pat.kind {
340380
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
341-
Some(self.resolve_ty_and_res_fully_qualified_call(qpath, *hir_id, *span))
381+
Some(self.resolve_pat_path(*hir_id, *span, qpath))
342382
}
343383
_ => None,
344384
};
345-
let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
385+
let opt_res_pat = opt_res_pat_or_err.map(Result::ok).flatten();
386+
387+
let adjust_mode = self.calc_adjust_mode(pat, opt_res_pat.as_ref());
346388
let (expected, binding_mode, max_ref_mutbl) =
347389
self.calc_default_binding_mode(pat, expected, binding_mode, adjust_mode, max_ref_mutbl);
348390
let pat_info = PatInfo {
@@ -357,16 +399,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
357399
PatKind::Wild | PatKind::Err(_) => expected,
358400
// We allow any type here; we ensure that the type is uninhabited during match checking.
359401
PatKind::Never => expected,
360-
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
361-
let ty = self.check_pat_path(
362-
*hir_id,
363-
pat.hir_id,
364-
*span,
365-
qpath,
366-
path_res.unwrap(),
367-
expected,
368-
&pat_info.top_info,
369-
);
402+
PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), hir_id, .. }) => {
403+
let ty = match opt_res_pat_or_err.unwrap() {
404+
Ok(ref res_pat) => {
405+
self.check_pat_path(pat.hir_id, pat.span, res_pat, expected, &ti)
406+
}
407+
Err(guar) => Ty::new_error(self.tcx, guar),
408+
};
370409
self.write_ty(*hir_id, ty);
371410
ty
372411
}
@@ -490,8 +529,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
490529

491530
/// How should the binding mode and expected type be adjusted?
492531
///
493-
/// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
494-
fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode {
532+
/// When the pattern contains a path, `opt_resolved_pat` must be `Some(resolved_pat)`.
533+
fn calc_adjust_mode(
534+
&self,
535+
pat: &'tcx Pat<'tcx>,
536+
opt_resolved_pat: Option<&ResolvedPat<'tcx>>,
537+
) -> AdjustMode {
495538
// When we perform destructuring assignment, we disable default match bindings, which are
496539
// unintuitive in this context.
497540
if !pat.default_binding_modes {
@@ -512,17 +555,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
512555
| PatKind::Deref(_) => AdjustMode::Peel { kind: PeelKind::RefOnly },
513556
// A never pattern behaves somewhat like a literal or unit variant.
514557
PatKind::Never => AdjustMode::Peel { kind: PeelKind::Overloaded },
515-
PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => match opt_path_res.unwrap() {
516-
// These constants can be of a reference type, e.g. `const X: &u8 = &0;`.
517-
// Peeling the reference types too early will cause type checking failures.
518-
// Although it would be possible to *also* peel the types of the constants too.
519-
Res::Def(DefKind::Const | DefKind::AssocConst, _) => AdjustMode::Pass,
520-
// In the `ValueNS`, we have `SelfCtor(..) | Ctor(_, Const), _)` remaining which
521-
// could successfully compile. The former being `Self` requires a unit struct.
522-
// In either case, and unlike constants, the pattern itself cannot be
523-
// a reference type wherefore peeling doesn't give up any expressiveness.
524-
_ => AdjustMode::Peel { kind: PeelKind::Overloaded },
525-
},
558+
// For patterns with paths, how we peel the scrutinee depends on the path's resolution.
559+
PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => {
560+
opt_resolved_pat.map_or(AdjustMode::Peel { kind: PeelKind::Overloaded }, ResolvedPat::adjust_mode)
561+
}
526562

527563
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
528564
// All other literals result in non-reference types.
@@ -1241,31 +1277,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12411277
}
12421278
}
12431279

1244-
fn check_pat_path(
1280+
fn resolve_pat_path(
12451281
&self,
12461282
path_id: HirId,
1247-
pat_id_for_diag: HirId,
12481283
span: Span,
1249-
qpath: &hir::QPath<'_>,
1250-
path_resolution: (Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]),
1251-
expected: Ty<'tcx>,
1252-
ti: &TopInfo<'tcx>,
1253-
) -> Ty<'tcx> {
1284+
qpath: &'tcx hir::QPath<'_>,
1285+
) -> Result<ResolvedPat<'tcx>, ErrorGuaranteed> {
12541286
let tcx = self.tcx;
12551287

1256-
// We have already resolved the path.
1257-
let (res, opt_ty, segments) = path_resolution;
1288+
let (res, opt_ty, segments) =
1289+
self.resolve_ty_and_res_fully_qualified_call(qpath, path_id, span);
12581290
match res {
12591291
Res::Err => {
12601292
let e =
12611293
self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted");
12621294
self.set_tainted_by_errors(e);
1263-
return Ty::new_error(tcx, e);
1295+
return Err(e);
12641296
}
12651297
Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => {
12661298
let expected = "unit struct, unit variant or constant";
12671299
let e = report_unexpected_variant_res(tcx, res, None, qpath, span, E0533, expected);
1268-
return Ty::new_error(tcx, e);
1300+
return Err(e);
12691301
}
12701302
Res::SelfCtor(def_id) => {
12711303
if let ty::Adt(adt_def, _) = *tcx.type_of(def_id).skip_binder().kind()
@@ -1283,7 +1315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12831315
E0533,
12841316
"unit struct",
12851317
);
1286-
return Ty::new_error(tcx, e);
1318+
return Err(e);
12871319
}
12881320
}
12891321
Res::Def(
@@ -1296,15 +1328,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12961328
_ => bug!("unexpected pattern resolution: {:?}", res),
12971329
}
12981330

1299-
// Type-check the path.
1331+
// Find the type of the path pattern, for later checking.
13001332
let (pat_ty, pat_res) =
13011333
self.instantiate_value_path(segments, opt_ty, res, span, span, path_id);
1334+
Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Path { res, pat_res, segments } })
1335+
}
1336+
1337+
fn check_pat_path(
1338+
&self,
1339+
pat_id_for_diag: HirId,
1340+
span: Span,
1341+
resolved: &ResolvedPat<'tcx>,
1342+
expected: Ty<'tcx>,
1343+
ti: &TopInfo<'tcx>,
1344+
) -> Ty<'tcx> {
13021345
if let Err(err) =
1303-
self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, pat_ty)
1346+
self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, resolved.ty)
13041347
{
1305-
self.emit_bad_pat_path(err, pat_id_for_diag, span, res, pat_res, pat_ty, segments);
1348+
self.emit_bad_pat_path(err, pat_id_for_diag, span, resolved);
13061349
}
1307-
pat_ty
1350+
resolved.ty
13081351
}
13091352

13101353
fn maybe_suggest_range_literal(
@@ -1347,11 +1390,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13471390
mut e: Diag<'_>,
13481391
hir_id: HirId,
13491392
pat_span: Span,
1350-
res: Res,
1351-
pat_res: Res,
1352-
pat_ty: Ty<'tcx>,
1353-
segments: &'tcx [hir::PathSegment<'tcx>],
1393+
resolved_pat: &ResolvedPat<'tcx>,
13541394
) {
1395+
let ResolvedPatKind::Path { res, pat_res, segments } = resolved_pat.kind;
1396+
13551397
if let Some(span) = self.tcx.hir_res_span(pat_res) {
13561398
e.span_label(span, format!("{} defined here", res.descr()));
13571399
if let [hir::PathSegment { ident, .. }] = &*segments {
@@ -1374,7 +1416,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13741416
);
13751417
}
13761418
_ => {
1377-
let (type_def_id, item_def_id) = match pat_ty.kind() {
1419+
let (type_def_id, item_def_id) = match resolved_pat.ty.kind() {
13781420
ty::Adt(def, _) => match res {
13791421
Res::Def(DefKind::Const, def_id) => (Some(def.did()), Some(def_id)),
13801422
_ => (None, None),

0 commit comments

Comments
 (0)