Skip to content

Commit d7c1e0d

Browse files
authored
Rollup merge of #57366 - estebank:point-match-discrim, r=varkor
Point at match discriminant on type error in match arm pattern ``` error[E0308]: mismatched types --> src/main.rs:5:9 | 4 | let temp: usize = match a + b { | ----- this expression has type `usize` 5 | Ok(num) => num, | ^^^^^^^ expected usize, found enum `std::result::Result` | = note: expected type `usize` found type `std::result::Result<_, _>` ``` Fix #57279.
2 parents 45d3572 + 10fbdbf commit d7c1e0d

23 files changed

+194
-80
lines changed

src/librustc/infer/error_reporting/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
487487

488488
fn note_error_origin(&self, err: &mut DiagnosticBuilder<'tcx>, cause: &ObligationCause<'tcx>) {
489489
match cause.code {
490+
ObligationCauseCode::MatchExpressionArmPattern { span, ty } => {
491+
if ty.is_suggestable() { // don't show type `_`
492+
err.span_label(span, format!("this match expression has type `{}`", ty));
493+
}
494+
}
490495
ObligationCauseCode::MatchExpressionArm { arm_span, source } => match source {
491496
hir::MatchSource::IfLetDesugar { .. } => {
492497
let msg = "`if let` arm with an incompatible type";

src/librustc/traits/error_reporting.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1444,15 +1444,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
14441444
match *cause_code {
14451445
ObligationCauseCode::ExprAssignable |
14461446
ObligationCauseCode::MatchExpressionArm { .. } |
1447+
ObligationCauseCode::MatchExpressionArmPattern { .. } |
14471448
ObligationCauseCode::IfExpression |
14481449
ObligationCauseCode::IfExpressionWithNoElse |
14491450
ObligationCauseCode::MainFunctionType |
14501451
ObligationCauseCode::StartFunctionType |
14511452
ObligationCauseCode::IntrinsicType |
14521453
ObligationCauseCode::MethodReceiver |
14531454
ObligationCauseCode::ReturnNoExpression |
1454-
ObligationCauseCode::MiscObligation => {
1455-
}
1455+
ObligationCauseCode::MiscObligation => {}
14561456
ObligationCauseCode::SliceOrArrayElem => {
14571457
err.note("slice and array elements must have `Sized` type");
14581458
}

src/librustc/traits/mod.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,13 @@ pub enum ObligationCauseCode<'tcx> {
220220
ExprAssignable,
221221

222222
/// Computing common supertype in the arms of a match expression
223-
MatchExpressionArm { arm_span: Span,
224-
source: hir::MatchSource },
223+
MatchExpressionArm {
224+
arm_span: Span,
225+
source: hir::MatchSource,
226+
},
227+
228+
/// Computing common supertype in the pattern guard for the arms of a match expression
229+
MatchExpressionArmPattern { span: Span, ty: Ty<'tcx> },
225230

226231
/// Computing common supertype in an if expression
227232
IfExpression,

src/librustc/traits/structural_impls.rs

+3
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,9 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
517517
arm_span,
518518
source: source,
519519
}),
520+
super::MatchExpressionArmPattern { span, ty } => {
521+
tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty })
522+
}
520523
super::IfExpression => Some(super::IfExpression),
521524
super::IfExpressionWithNoElse => Some(super::IfExpressionWithNoElse),
522525
super::MainFunctionType => Some(super::MainFunctionType),

src/librustc_typeck/check/_match.rs

+104-69
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,33 @@ use std::cmp;
2020
use super::report_unexpected_variant_def;
2121

2222
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
23-
/// The `is_arg` argument indicates whether this pattern is the
24-
/// *outermost* pattern in an argument (e.g., in `fn foo(&x:
25-
/// &u32)`, it is true for the `&x` pattern but not `x`). This is
26-
/// used to tailor error reporting.
23+
/// `match_discrim_span` argument having a `Span` indicates that this pattern is part of
24+
/// a match expression arm guard, and it points to the match discriminant to add context
25+
/// in type errors. In the folloowing example, `match_discrim_span` corresponds to the
26+
/// `a + b` expression:
27+
///
28+
/// ```text
29+
/// error[E0308]: mismatched types
30+
/// --> src/main.rs:5:9
31+
/// |
32+
/// 4 | let temp: usize = match a + b {
33+
/// | ----- this expression has type `usize`
34+
/// 5 | Ok(num) => num,
35+
/// | ^^^^^^^ expected usize, found enum `std::result::Result`
36+
/// |
37+
/// = note: expected type `usize`
38+
/// found type `std::result::Result<_, _>`
39+
/// ```
2740
pub fn check_pat_walk(
2841
&self,
2942
pat: &'gcx hir::Pat,
3043
mut expected: Ty<'tcx>,
3144
mut def_bm: ty::BindingMode,
32-
is_arg: bool)
33-
{
45+
match_discrim_span: Option<Span>,
46+
) {
3447
let tcx = self.tcx;
3548

36-
debug!("check_pat_walk(pat={:?},expected={:?},def_bm={:?},is_arg={})",
37-
pat, expected, def_bm, is_arg);
49+
debug!("check_pat_walk(pat={:?},expected={:?},def_bm={:?})", pat, expected, def_bm);
3850

3951
let is_non_ref_pat = match pat.node {
4052
PatKind::Struct(..) |
@@ -210,8 +222,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
210222
let common_type = self.resolve_type_vars_if_possible(&lhs_ty);
211223

212224
// subtyping doesn't matter here, as the value is some kind of scalar
213-
self.demand_eqtype(pat.span, expected, lhs_ty);
214-
self.demand_eqtype(pat.span, expected, rhs_ty);
225+
self.demand_eqtype_pat(pat.span, expected, lhs_ty, match_discrim_span);
226+
self.demand_eqtype_pat(pat.span, expected, rhs_ty, match_discrim_span);
215227
common_type
216228
}
217229
PatKind::Binding(ba, var_id, _, ref sub) => {
@@ -240,37 +252,45 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
240252
// `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` is
241253
// required. However, we use equality, which is stronger. See (*) for
242254
// an explanation.
243-
self.demand_eqtype(pat.span, region_ty, local_ty);
255+
self.demand_eqtype_pat(pat.span, region_ty, local_ty, match_discrim_span);
244256
}
245257
// otherwise the type of x is the expected type T
246258
ty::BindByValue(_) => {
247259
// As above, `T <: typeof(x)` is required but we
248260
// use equality, see (*) below.
249-
self.demand_eqtype(pat.span, expected, local_ty);
261+
self.demand_eqtype_pat(pat.span, expected, local_ty, match_discrim_span);
250262
}
251263
}
252264

253265
// if there are multiple arms, make sure they all agree on
254266
// what the type of the binding `x` ought to be
255267
if var_id != pat.id {
256268
let vt = self.local_ty(pat.span, var_id).decl_ty;
257-
self.demand_eqtype(pat.span, vt, local_ty);
269+
self.demand_eqtype_pat(pat.span, vt, local_ty, match_discrim_span);
258270
}
259271

260272
if let Some(ref p) = *sub {
261-
self.check_pat_walk(&p, expected, def_bm, true);
273+
self.check_pat_walk(&p, expected, def_bm, match_discrim_span);
262274
}
263275

264276
local_ty
265277
}
266278
PatKind::TupleStruct(ref qpath, ref subpats, ddpos) => {
267-
self.check_pat_tuple_struct(pat, qpath, &subpats, ddpos, expected, def_bm)
279+
self.check_pat_tuple_struct(
280+
pat,
281+
qpath,
282+
&subpats,
283+
ddpos,
284+
expected,
285+
def_bm,
286+
match_discrim_span,
287+
)
268288
}
269289
PatKind::Path(ref qpath) => {
270290
self.check_pat_path(pat, qpath, expected)
271291
}
272292
PatKind::Struct(ref qpath, ref fields, etc) => {
273-
self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm)
293+
self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, match_discrim_span)
274294
}
275295
PatKind::Tuple(ref elements, ddpos) => {
276296
let mut expected_len = elements.len();
@@ -295,12 +315,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
295315
// further errors being emitted when using the bindings. #50333
296316
let element_tys_iter = (0..max_len).map(|_| tcx.types.err);
297317
for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
298-
self.check_pat_walk(elem, &tcx.types.err, def_bm, true);
318+
self.check_pat_walk(elem, &tcx.types.err, def_bm, match_discrim_span);
299319
}
300320
tcx.mk_tup(element_tys_iter)
301321
} else {
302322
for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
303-
self.check_pat_walk(elem, &element_tys[i], def_bm, true);
323+
self.check_pat_walk(elem, &element_tys[i], def_bm, match_discrim_span);
304324
}
305325
pat_ty
306326
}
@@ -313,11 +333,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
313333
// Here, `demand::subtype` is good enough, but I don't
314334
// think any errors can be introduced by using
315335
// `demand::eqtype`.
316-
self.demand_eqtype(pat.span, expected, uniq_ty);
317-
self.check_pat_walk(&inner, inner_ty, def_bm, true);
336+
self.demand_eqtype_pat(pat.span, expected, uniq_ty, match_discrim_span);
337+
self.check_pat_walk(&inner, inner_ty, def_bm, match_discrim_span);
318338
uniq_ty
319339
} else {
320-
self.check_pat_walk(&inner, tcx.types.err, def_bm, true);
340+
self.check_pat_walk(&inner, tcx.types.err, def_bm, match_discrim_span);
321341
tcx.types.err
322342
}
323343
}
@@ -349,15 +369,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
349369
// Look for a case like `fn foo(&foo: u32)` and suggest
350370
// `fn foo(foo: &u32)`
351371
if let Some(mut err) = err {
352-
if is_arg {
353-
if let PatKind::Binding(..) = inner.node {
354-
if let Ok(snippet) = tcx.sess.source_map()
355-
.span_to_snippet(pat.span)
356-
{
357-
err.help(&format!("did you mean `{}: &{}`?",
358-
&snippet[1..],
359-
expected));
360-
}
372+
if let PatKind::Binding(..) = inner.node {
373+
if let Ok(snippet) = tcx.sess.source_map()
374+
.span_to_snippet(pat.span)
375+
{
376+
err.help(&format!("did you mean `{}: &{}`?",
377+
&snippet[1..],
378+
expected));
361379
}
362380
}
363381
err.emit();
@@ -366,10 +384,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
366384
}
367385
};
368386

369-
self.check_pat_walk(&inner, inner_ty, def_bm, true);
387+
self.check_pat_walk(&inner, inner_ty, def_bm, match_discrim_span);
370388
rptr_ty
371389
} else {
372-
self.check_pat_walk(&inner, tcx.types.err, def_bm, true);
390+
self.check_pat_walk(&inner, tcx.types.err, def_bm, match_discrim_span);
373391
tcx.types.err
374392
}
375393
}
@@ -427,13 +445,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
427445
};
428446

429447
for elt in before {
430-
self.check_pat_walk(&elt, inner_ty, def_bm, true);
448+
self.check_pat_walk(&elt, inner_ty, def_bm, match_discrim_span);
431449
}
432450
if let Some(ref slice) = *slice {
433-
self.check_pat_walk(&slice, slice_ty, def_bm, true);
451+
self.check_pat_walk(&slice, slice_ty, def_bm, match_discrim_span);
434452
}
435453
for elt in after {
436-
self.check_pat_walk(&elt, inner_ty, def_bm, true);
454+
self.check_pat_walk(&elt, inner_ty, def_bm, match_discrim_span);
437455
}
438456
expected_ty
439457
}
@@ -524,12 +542,14 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
524542
true
525543
}
526544

527-
pub fn check_match(&self,
528-
expr: &'gcx hir::Expr,
529-
discrim: &'gcx hir::Expr,
530-
arms: &'gcx [hir::Arm],
531-
expected: Expectation<'tcx>,
532-
match_src: hir::MatchSource) -> Ty<'tcx> {
545+
pub fn check_match(
546+
&self,
547+
expr: &'gcx hir::Expr,
548+
discrim: &'gcx hir::Expr,
549+
arms: &'gcx [hir::Arm],
550+
expected: Expectation<'tcx>,
551+
match_src: hir::MatchSource,
552+
) -> Ty<'tcx> {
533553
let tcx = self.tcx;
534554

535555
// Not entirely obvious: if matches may create ref bindings, we want to
@@ -624,8 +644,12 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
624644
let mut all_pats_diverge = Diverges::WarnedAlways;
625645
for p in &arm.pats {
626646
self.diverges.set(Diverges::Maybe);
627-
self.check_pat_walk(&p, discrim_ty,
628-
ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), true);
647+
self.check_pat_walk(
648+
&p,
649+
discrim_ty,
650+
ty::BindingMode::BindByValue(hir::Mutability::MutImmutable),
651+
Some(discrim.span),
652+
);
629653
all_pats_diverge &= self.diverges.get();
630654
}
631655

@@ -703,26 +727,34 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
703727
coercion.complete(self)
704728
}
705729

706-
fn check_pat_struct(&self,
707-
pat: &'gcx hir::Pat,
708-
qpath: &hir::QPath,
709-
fields: &'gcx [Spanned<hir::FieldPat>],
710-
etc: bool,
711-
expected: Ty<'tcx>,
712-
def_bm: ty::BindingMode) -> Ty<'tcx>
730+
fn check_pat_struct(
731+
&self,
732+
pat: &'gcx hir::Pat,
733+
qpath: &hir::QPath,
734+
fields: &'gcx [Spanned<hir::FieldPat>],
735+
etc: bool,
736+
expected: Ty<'tcx>,
737+
def_bm: ty::BindingMode,
738+
match_discrim_span: Option<Span>,
739+
) -> Ty<'tcx>
713740
{
714741
// Resolve the path and check the definition for errors.
715742
let (variant, pat_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, pat.id) {
716743
variant_ty
717744
} else {
718745
for field in fields {
719-
self.check_pat_walk(&field.node.pat, self.tcx.types.err, def_bm, true);
746+
self.check_pat_walk(
747+
&field.node.pat,
748+
self.tcx.types.err,
749+
def_bm,
750+
match_discrim_span,
751+
);
720752
}
721753
return self.tcx.types.err;
722754
};
723755

724756
// Type-check the path.
725-
self.demand_eqtype(pat.span, expected, pat_ty);
757+
self.demand_eqtype_pat(pat.span, expected, pat_ty, match_discrim_span);
726758

727759
// Type-check subpatterns.
728760
if self.check_struct_pat_fields(pat_ty, pat.id, pat.span, variant, fields, etc, def_bm) {
@@ -732,11 +764,12 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
732764
}
733765
}
734766

735-
fn check_pat_path(&self,
736-
pat: &hir::Pat,
737-
qpath: &hir::QPath,
738-
expected: Ty<'tcx>) -> Ty<'tcx>
739-
{
767+
fn check_pat_path(
768+
&self,
769+
pat: &hir::Pat,
770+
qpath: &hir::QPath,
771+
expected: Ty<'tcx>,
772+
) -> Ty<'tcx> {
740773
let tcx = self.tcx;
741774

742775
// Resolve the path and check the definition for errors.
@@ -767,18 +800,20 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
767800
pat_ty
768801
}
769802

770-
fn check_pat_tuple_struct(&self,
771-
pat: &hir::Pat,
772-
qpath: &hir::QPath,
773-
subpats: &'gcx [P<hir::Pat>],
774-
ddpos: Option<usize>,
775-
expected: Ty<'tcx>,
776-
def_bm: ty::BindingMode) -> Ty<'tcx>
777-
{
803+
fn check_pat_tuple_struct(
804+
&self,
805+
pat: &hir::Pat,
806+
qpath: &hir::QPath,
807+
subpats: &'gcx [P<hir::Pat>],
808+
ddpos: Option<usize>,
809+
expected: Ty<'tcx>,
810+
def_bm: ty::BindingMode,
811+
match_arm_pat_span: Option<Span>,
812+
) -> Ty<'tcx> {
778813
let tcx = self.tcx;
779814
let on_error = || {
780815
for pat in subpats {
781-
self.check_pat_walk(&pat, tcx.types.err, def_bm, true);
816+
self.check_pat_walk(&pat, tcx.types.err, def_bm, match_arm_pat_span);
782817
}
783818
};
784819
let report_unexpected_def = |def: Def| {
@@ -826,7 +861,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
826861
let pat_ty = pat_ty.fn_sig(tcx).output();
827862
let pat_ty = pat_ty.no_bound_vars().expect("expected fn type");
828863

829-
self.demand_eqtype(pat.span, expected, pat_ty);
864+
self.demand_eqtype_pat(pat.span, expected, pat_ty, match_arm_pat_span);
830865

831866
// Type-check subpatterns.
832867
if subpats.len() == variant.fields.len() ||
@@ -837,7 +872,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
837872
};
838873
for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) {
839874
let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs);
840-
self.check_pat_walk(&subpat, field_ty, def_bm, true);
875+
self.check_pat_walk(&subpat, field_ty, def_bm, match_arm_pat_span);
841876

842877
self.tcx.check_stability(variant.fields[i].did, Some(pat.id), subpat.span);
843878
}
@@ -917,7 +952,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
917952
}
918953
};
919954

920-
self.check_pat_walk(&field.pat, field_ty, def_bm, true);
955+
self.check_pat_walk(&field.pat, field_ty, def_bm, None);
921956
}
922957
let mut unmentioned_fields = variant.fields
923958
.iter()

0 commit comments

Comments
 (0)