Skip to content

Commit a873337

Browse files
committed
Point at the match discriminant when arm pattern has a type mismatch
1 parent c2d381d commit a873337

27 files changed

+183
-77
lines changed

src/librustc/infer/error_reporting/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,9 @@ 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+
err.span_label(span, format!("this match expression evaluates to `{}`", ty));
492+
}
490493
ObligationCauseCode::MatchExpressionArm { arm_span, source } => match source {
491494
hir::MatchSource::IfLetDesugar { .. } => {
492495
let msg = "`if let` arm with an incompatible type";

src/librustc/traits/error_reporting.rs

Lines changed: 2 additions & 2 deletions
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

Lines changed: 7 additions & 2 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 82 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
2929
pat: &'gcx hir::Pat,
3030
mut expected: Ty<'tcx>,
3131
mut def_bm: ty::BindingMode,
32-
is_arg: bool)
33-
{
32+
match_discrim_span: Option<Span>,
33+
) {
3434
let tcx = self.tcx;
3535

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

3938
let is_non_ref_pat = match pat.node {
4039
PatKind::Struct(..) |
@@ -210,8 +209,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
210209
let common_type = self.resolve_type_vars_if_possible(&lhs_ty);
211210

212211
// 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);
212+
self.demand_eqtype_pat(pat.span, expected, lhs_ty, match_discrim_span);
213+
self.demand_eqtype_pat(pat.span, expected, rhs_ty, match_discrim_span);
215214
common_type
216215
}
217216
PatKind::Binding(ba, var_id, _, ref sub) => {
@@ -240,37 +239,45 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
240239
// `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` is
241240
// required. However, we use equality, which is stronger. See (*) for
242241
// an explanation.
243-
self.demand_eqtype(pat.span, region_ty, local_ty);
242+
self.demand_eqtype_pat(pat.span, region_ty, local_ty, match_discrim_span);
244243
}
245244
// otherwise the type of x is the expected type T
246245
ty::BindByValue(_) => {
247246
// As above, `T <: typeof(x)` is required but we
248247
// use equality, see (*) below.
249-
self.demand_eqtype(pat.span, expected, local_ty);
248+
self.demand_eqtype_pat(pat.span, expected, local_ty, match_discrim_span);
250249
}
251250
}
252251

253252
// if there are multiple arms, make sure they all agree on
254253
// what the type of the binding `x` ought to be
255254
if var_id != pat.id {
256255
let vt = self.local_ty(pat.span, var_id).decl_ty;
257-
self.demand_eqtype(pat.span, vt, local_ty);
256+
self.demand_eqtype_pat(pat.span, vt, local_ty, match_discrim_span);
258257
}
259258

260259
if let Some(ref p) = *sub {
261-
self.check_pat_walk(&p, expected, def_bm, true);
260+
self.check_pat_walk(&p, expected, def_bm, match_discrim_span);
262261
}
263262

264263
local_ty
265264
}
266265
PatKind::TupleStruct(ref qpath, ref subpats, ddpos) => {
267-
self.check_pat_tuple_struct(pat, qpath, &subpats, ddpos, expected, def_bm)
266+
self.check_pat_tuple_struct(
267+
pat,
268+
qpath,
269+
&subpats,
270+
ddpos,
271+
expected,
272+
def_bm,
273+
match_discrim_span,
274+
)
268275
}
269276
PatKind::Path(ref qpath) => {
270277
self.check_pat_path(pat, qpath, expected)
271278
}
272279
PatKind::Struct(ref qpath, ref fields, etc) => {
273-
self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm)
280+
self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, match_discrim_span)
274281
}
275282
PatKind::Tuple(ref elements, ddpos) => {
276283
let mut expected_len = elements.len();
@@ -295,12 +302,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
295302
// further errors being emitted when using the bindings. #50333
296303
let element_tys_iter = (0..max_len).map(|_| tcx.types.err);
297304
for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
298-
self.check_pat_walk(elem, &tcx.types.err, def_bm, true);
305+
self.check_pat_walk(elem, &tcx.types.err, def_bm, match_discrim_span);
299306
}
300307
tcx.mk_tup(element_tys_iter)
301308
} else {
302309
for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
303-
self.check_pat_walk(elem, &element_tys[i], def_bm, true);
310+
self.check_pat_walk(elem, &element_tys[i], def_bm, match_discrim_span);
304311
}
305312
pat_ty
306313
}
@@ -313,11 +320,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
313320
// Here, `demand::subtype` is good enough, but I don't
314321
// think any errors can be introduced by using
315322
// `demand::eqtype`.
316-
self.demand_eqtype(pat.span, expected, uniq_ty);
317-
self.check_pat_walk(&inner, inner_ty, def_bm, true);
323+
self.demand_eqtype_pat(pat.span, expected, uniq_ty, match_discrim_span);
324+
self.check_pat_walk(&inner, inner_ty, def_bm, match_discrim_span);
318325
uniq_ty
319326
} else {
320-
self.check_pat_walk(&inner, tcx.types.err, def_bm, true);
327+
self.check_pat_walk(&inner, tcx.types.err, def_bm, match_discrim_span);
321328
tcx.types.err
322329
}
323330
}
@@ -349,15 +356,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
349356
// Look for a case like `fn foo(&foo: u32)` and suggest
350357
// `fn foo(foo: &u32)`
351358
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-
}
359+
if let PatKind::Binding(..) = inner.node {
360+
if let Ok(snippet) = tcx.sess.source_map()
361+
.span_to_snippet(pat.span)
362+
{
363+
err.help(&format!("did you mean `{}: &{}`?",
364+
&snippet[1..],
365+
expected));
361366
}
362367
}
363368
err.emit();
@@ -366,10 +371,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
366371
}
367372
};
368373

369-
self.check_pat_walk(&inner, inner_ty, def_bm, true);
374+
self.check_pat_walk(&inner, inner_ty, def_bm, match_discrim_span);
370375
rptr_ty
371376
} else {
372-
self.check_pat_walk(&inner, tcx.types.err, def_bm, true);
377+
self.check_pat_walk(&inner, tcx.types.err, def_bm, match_discrim_span);
373378
tcx.types.err
374379
}
375380
}
@@ -427,13 +432,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
427432
};
428433

429434
for elt in before {
430-
self.check_pat_walk(&elt, inner_ty, def_bm, true);
435+
self.check_pat_walk(&elt, inner_ty, def_bm, match_discrim_span);
431436
}
432437
if let Some(ref slice) = *slice {
433-
self.check_pat_walk(&slice, slice_ty, def_bm, true);
438+
self.check_pat_walk(&slice, slice_ty, def_bm, match_discrim_span);
434439
}
435440
for elt in after {
436-
self.check_pat_walk(&elt, inner_ty, def_bm, true);
441+
self.check_pat_walk(&elt, inner_ty, def_bm, match_discrim_span);
437442
}
438443
expected_ty
439444
}
@@ -524,12 +529,14 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
524529
true
525530
}
526531

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> {
532+
pub fn check_match(
533+
&self,
534+
expr: &'gcx hir::Expr,
535+
discrim: &'gcx hir::Expr,
536+
arms: &'gcx [hir::Arm],
537+
expected: Expectation<'tcx>,
538+
match_src: hir::MatchSource,
539+
) -> Ty<'tcx> {
533540
let tcx = self.tcx;
534541

535542
// Not entirely obvious: if matches may create ref bindings, we want to
@@ -624,8 +631,12 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
624631
let mut all_pats_diverge = Diverges::WarnedAlways;
625632
for p in &arm.pats {
626633
self.diverges.set(Diverges::Maybe);
627-
self.check_pat_walk(&p, discrim_ty,
628-
ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), true);
634+
self.check_pat_walk(
635+
&p,
636+
discrim_ty,
637+
ty::BindingMode::BindByValue(hir::Mutability::MutImmutable),
638+
Some(discrim.span),
639+
);
629640
all_pats_diverge &= self.diverges.get();
630641
}
631642

@@ -703,26 +714,29 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
703714
coercion.complete(self)
704715
}
705716

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>
717+
fn check_pat_struct(
718+
&self,
719+
pat: &'gcx hir::Pat,
720+
qpath: &hir::QPath,
721+
fields: &'gcx [Spanned<hir::FieldPat>],
722+
etc: bool,
723+
expected: Ty<'tcx>,
724+
def_bm: ty::BindingMode,
725+
match_discrim_span: Option<Span>,
726+
) -> Ty<'tcx>
713727
{
714728
// Resolve the path and check the definition for errors.
715729
let (variant, pat_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, pat.id) {
716730
variant_ty
717731
} else {
718732
for field in fields {
719-
self.check_pat_walk(&field.node.pat, self.tcx.types.err, def_bm, true);
733+
self.check_pat_walk(&field.node.pat, self.tcx.types.err, def_bm, match_discrim_span);
720734
}
721735
return self.tcx.types.err;
722736
};
723737

724738
// Type-check the path.
725-
self.demand_eqtype(pat.span, expected, pat_ty);
739+
self.demand_eqtype_pat(pat.span, expected, pat_ty, match_discrim_span);
726740

727741
// Type-check subpatterns.
728742
if self.check_struct_pat_fields(pat_ty, pat.id, pat.span, variant, fields, etc, def_bm) {
@@ -732,11 +746,12 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
732746
}
733747
}
734748

735-
fn check_pat_path(&self,
736-
pat: &hir::Pat,
737-
qpath: &hir::QPath,
738-
expected: Ty<'tcx>) -> Ty<'tcx>
739-
{
749+
fn check_pat_path(
750+
&self,
751+
pat: &hir::Pat,
752+
qpath: &hir::QPath,
753+
expected: Ty<'tcx>,
754+
) -> Ty<'tcx> {
740755
let tcx = self.tcx;
741756

742757
// Resolve the path and check the definition for errors.
@@ -767,18 +782,20 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
767782
pat_ty
768783
}
769784

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-
{
785+
fn check_pat_tuple_struct(
786+
&self,
787+
pat: &hir::Pat,
788+
qpath: &hir::QPath,
789+
subpats: &'gcx [P<hir::Pat>],
790+
ddpos: Option<usize>,
791+
expected: Ty<'tcx>,
792+
def_bm: ty::BindingMode,
793+
match_arm_pat_span: Option<Span>,
794+
) -> Ty<'tcx> {
778795
let tcx = self.tcx;
779796
let on_error = || {
780797
for pat in subpats {
781-
self.check_pat_walk(&pat, tcx.types.err, def_bm, true);
798+
self.check_pat_walk(&pat, tcx.types.err, def_bm, match_arm_pat_span);
782799
}
783800
};
784801
let report_unexpected_def = |def: Def| {
@@ -826,7 +843,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
826843
let pat_ty = pat_ty.fn_sig(tcx).output();
827844
let pat_ty = pat_ty.no_bound_vars().expect("expected fn type");
828845

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

831848
// Type-check subpatterns.
832849
if subpats.len() == variant.fields.len() ||
@@ -837,7 +854,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
837854
};
838855
for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) {
839856
let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs);
840-
self.check_pat_walk(&subpat, field_ty, def_bm, true);
857+
self.check_pat_walk(&subpat, field_ty, def_bm, match_arm_pat_span);
841858

842859
self.tcx.check_stability(variant.fields[i].did, Some(pat.id), subpat.span);
843860
}
@@ -917,7 +934,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
917934
}
918935
};
919936

920-
self.check_pat_walk(&field.pat, field_ty, def_bm, true);
937+
self.check_pat_walk(&field.pat, field_ty, def_bm, None);
921938
}
922939
let mut unmentioned_fields = variant.fields
923940
.iter()

src/librustc_typeck/check/demand.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use check::FnCtxt;
22
use rustc::infer::InferOk;
3-
use rustc::traits::ObligationCause;
3+
use rustc::traits::{ObligationCause, ObligationCauseCode};
44

55
use syntax::ast;
66
use syntax::util::parser::PREC_POSTFIX;
@@ -66,6 +66,25 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
6666
}
6767
}
6868

69+
pub fn demand_eqtype_pat(
70+
&self,
71+
cause_span: Span,
72+
expected: Ty<'tcx>,
73+
actual: Ty<'tcx>,
74+
match_expr_span: Option<Span>,
75+
) {
76+
let cause = if let Some(span) = match_expr_span {
77+
self.cause(
78+
cause_span,
79+
ObligationCauseCode::MatchExpressionArmPattern { span, ty: expected },
80+
)
81+
} else {
82+
self.misc(cause_span)
83+
};
84+
self.demand_eqtype_with_origin(&cause, expected, actual).map(|mut err| err.emit());
85+
}
86+
87+
6988
pub fn demand_coerce(&self,
7089
expr: &hir::Expr,
7190
checked_ty: Ty<'tcx>,

0 commit comments

Comments
 (0)