Skip to content

Commit f8d2cce

Browse files
committed
Blame user type in pat type error.
1 parent f2c6a19 commit f8d2cce

File tree

12 files changed

+138
-20
lines changed

12 files changed

+138
-20
lines changed

src/librustc/infer/error_reporting/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -581,8 +581,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
581581
exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
582582
) {
583583
match cause.code {
584-
ObligationCauseCode::Pattern { span, ty } => {
585-
let ty = self.resolve_vars_if_possible(&ty);
584+
ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => {
585+
let ty = self.resolve_vars_if_possible(&root_ty);
586586
if ty.is_suggestable() {
587587
// don't show type `_`
588588
err.span_label(span, format!("this expression has type `{}`", ty));
@@ -600,6 +600,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
600600
}
601601
}
602602
}
603+
ObligationCauseCode::Pattern { origin_expr: false, span: Some(span), .. } => {
604+
err.span_label(span, "expected due to this");
605+
}
603606
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
604607
source,
605608
ref prior_arms,

src/librustc/traits/mod.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,12 @@ pub enum ObligationCauseCode<'tcx> {
251251

252252
/// Type error arising from type checking a pattern against an expected type.
253253
Pattern {
254-
span: Span,
255-
ty: Ty<'tcx>,
254+
/// The span of the scrutinee or type expression which caused the `root_ty` type.
255+
span: Option<Span>,
256+
/// The root expected type induced by a scrutinee or type expression.
257+
root_ty: Ty<'tcx>,
258+
/// Whether the `Span` came from an expression or a type expression.
259+
origin_expr: bool,
256260
},
257261

258262
/// Constants in patterns must have `Structural` type.

src/librustc/traits/structural_impls.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,9 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
521521
discrim_hir_id,
522522
})
523523
}),
524-
super::Pattern { span, ty } => tcx.lift(&ty).map(|ty| super::Pattern { span, ty }),
524+
super::Pattern { span, root_ty, origin_expr } => {
525+
tcx.lift(&root_ty).map(|root_ty| super::Pattern { span, root_ty, origin_expr })
526+
}
525527
super::IfExpression(box super::IfExpressionCause { then, outer, semicolon }) => {
526528
Some(super::IfExpression(box super::IfExpressionCause { then, outer, semicolon }))
527529
}

src/librustc_typeck/check/_match.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6161
.map(|arm| {
6262
let mut all_pats_diverge = Diverges::WarnedAlways;
6363
self.diverges.set(Diverges::Maybe);
64-
self.check_pat_top(&arm.pat, discrim_ty, Some(discrim.span));
64+
self.check_pat_top(&arm.pat, discrim_ty, Some(discrim.span), true);
6565
all_pats_diverge &= self.diverges.get();
6666

6767
// As discussed with @eddyb, this is for disabling unreachable_code

src/librustc_typeck/check/mod.rs

+17-4
Original file line numberDiff line numberDiff line change
@@ -1293,9 +1293,11 @@ fn check_fn<'a, 'tcx>(
12931293
};
12941294

12951295
// Add formal parameters.
1296-
for (param_ty, param) in fn_sig.inputs().iter().copied().chain(maybe_va_list).zip(body.params) {
1296+
let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs);
1297+
let inputs_fn = fn_sig.inputs().iter().copied();
1298+
for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
12971299
// Check the pattern.
1298-
fcx.check_pat_top(&param.pat, param_ty, None);
1300+
fcx.check_pat_top(&param.pat, param_ty, try { inputs_hir?.get(idx)?.span }, false);
12991301

13001302
// Check that argument is Sized.
13011303
// The check for a non-trivial pattern is a hack to avoid duplicate warnings
@@ -4276,16 +4278,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
42764278
}
42774279
}
42784280

4281+
/// Type check a `let` statement.
42794282
pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
4283+
// Determine and write the type which we'll check the pattern against.
42804284
let ty = self.local_ty(local.span, local.hir_id).decl_ty;
42814285
self.write_ty(local.hir_id, ty);
42824286

4287+
// Type check the initializer.
42834288
if let Some(ref init) = local.init {
42844289
let init_ty = self.check_decl_initializer(local, &init);
42854290
self.overwrite_local_ty_if_err(local, ty, init_ty);
42864291
}
42874292

4288-
self.check_pat_top(&local.pat, ty, local.init.map(|init| init.span));
4293+
// Does the expected pattern type originate from an expression and what is the span?
4294+
let (origin_expr, ty_span) = match (local.ty, local.init) {
4295+
(Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
4296+
(_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee.
4297+
_ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
4298+
};
4299+
4300+
// Type check the pattern. Override if necessary to avoid knock-on errors.
4301+
self.check_pat_top(&local.pat, ty, ty_span, origin_expr);
42894302
let pat_ty = self.node_ty(local.pat.hir_id);
42904303
self.overwrite_local_ty_if_err(local, ty, pat_ty);
42914304
}
@@ -4297,7 +4310,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
42974310
ty: Ty<'tcx>,
42984311
) {
42994312
if ty.references_error() {
4300-
// Override the types everywhere with `types.err` to avoid knock down errors.
4313+
// Override the types everywhere with `types.err` to avoid knock on errors.
43014314
self.write_ty(local.hir_id, ty);
43024315
self.write_ty(local.pat.hir_id, ty);
43034316
let local_ty = LocalTy { decl_ty, revealed_ty: ty };

src/librustc_typeck/check/pat.rs

+20-9
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,13 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
3737
struct TopInfo<'tcx> {
3838
/// The `expected` type at the top level of type checking a pattern.
3939
expected: Ty<'tcx>,
40+
/// Was the origin of the `span` from a scrutinee expression?
41+
///
42+
/// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter.
43+
origin_expr: bool,
4044
/// The span giving rise to the `expected` type, if one could be provided.
4145
///
42-
/// This is the span of the scrutinee as in:
46+
/// If `origin_expr` is `true`, then this is the span of the scrutinee as in:
4347
///
4448
/// - `match scrutinee { ... }`
4549
/// - `let _ = scrutinee;`
@@ -70,11 +74,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
7074
actual: Ty<'tcx>,
7175
ti: TopInfo<'tcx>,
7276
) -> Option<DiagnosticBuilder<'tcx>> {
73-
let cause = if let Some(span) = ti.span {
74-
self.cause(cause_span, Pattern { span, ty: ti.expected })
75-
} else {
76-
self.misc(cause_span)
77-
};
77+
let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr };
78+
let cause = self.cause(cause_span, code);
7879
self.demand_eqtype_with_origin(&cause, expected, actual)
7980
}
8081

@@ -92,11 +93,21 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
9293
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9394
/// Type check the given top level pattern against the `expected` type.
9495
///
95-
/// If a `Some(span)` is provided, then the `span` represents the scrutinee's span.
96+
/// If a `Some(span)` is provided and `origin_expr` holds,
97+
/// then the `span` represents the scrutinee's span.
9698
/// The scrutinee is found in e.g. `match scrutinee { ... }` and `let pat = scrutinee;`.
97-
pub fn check_pat_top(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, span: Option<Span>) {
99+
///
100+
/// Otherwise, `Some(span)` represents the span of a type expression
101+
/// which originated the `expected` type.
102+
pub fn check_pat_top(
103+
&self,
104+
pat: &'tcx Pat<'tcx>,
105+
expected: Ty<'tcx>,
106+
span: Option<Span>,
107+
origin_expr: bool,
108+
) {
98109
let def_bm = BindingMode::BindByValue(hir::Mutability::Not);
99-
self.check_pat(pat, expected, def_bm, TopInfo { expected, span });
110+
self.check_pat(pat, expected, def_bm, TopInfo { expected, origin_expr, span });
100111
}
101112

102113
/// Type check the given `pat` against the `expected` type

src/librustc_typeck/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ This API is completely unstable and subject to change.
6565
#![feature(in_band_lifetimes)]
6666
#![feature(nll)]
6767
#![feature(slice_patterns)]
68+
#![feature(try_blocks)]
6869
#![feature(never_type)]
6970
#![recursion_limit = "256"]
7071

src/test/ui/or-patterns/inconsistent-modes.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ error[E0308]: mismatched types
6060
--> $DIR/inconsistent-modes.rs:13:25
6161
|
6262
LL | let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0);
63-
| ^^^^^^^^^ ------ this expression has type `std::result::Result<&u8, &mut u8>`
63+
| ^^^^^^^^^ -------------------- expected due to this
6464
| |
6565
| types differ in mutability
6666
|
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Test the `.span_label(..)` to the type when there's a
2+
// type error in a pattern due to a the formal parameter.
3+
4+
fn main() {}
5+
6+
struct Tuple(u8);
7+
8+
fn foo(Tuple(_): String) {} //~ ERROR mismatched types
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/pat-type-err-formal-param.rs:8:8
3+
|
4+
LL | fn foo(Tuple(_): String) {}
5+
| ^^^^^^^^ ------ expected due to this
6+
| |
7+
| expected struct `std::string::String`, found struct `Tuple`
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Test the `.span_label` to the type / scrutinee
2+
// when there's a type error in checking a pattern.
3+
4+
fn main() {
5+
// We want to point at the `Option<u8>`.
6+
let Ok(0): Option<u8> = 42u8;
7+
//~^ ERROR mismatched types
8+
//~| ERROR mismatched types
9+
10+
// We want to point at the `Option<u8>`.
11+
let Ok(0): Option<u8>;
12+
//~^ ERROR mismatched types
13+
14+
// We want to point at the scrutinee.
15+
let Ok(0) = 42u8; //~ ERROR mismatched types
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/pat-type-err-let-stmt.rs:6:29
3+
|
4+
LL | let Ok(0): Option<u8> = 42u8;
5+
| ---------- ^^^^
6+
| | |
7+
| | expected enum `std::option::Option`, found `u8`
8+
| | help: try using a variant of the expected enum: `Some(42u8)`
9+
| expected due to this
10+
|
11+
= note: expected enum `std::option::Option<u8>`
12+
found type `u8`
13+
14+
error[E0308]: mismatched types
15+
--> $DIR/pat-type-err-let-stmt.rs:6:9
16+
|
17+
LL | let Ok(0): Option<u8> = 42u8;
18+
| ^^^^^ ---------- expected due to this
19+
| |
20+
| expected enum `std::option::Option`, found enum `std::result::Result`
21+
|
22+
= note: expected enum `std::option::Option<u8>`
23+
found enum `std::result::Result<_, _>`
24+
25+
error[E0308]: mismatched types
26+
--> $DIR/pat-type-err-let-stmt.rs:11:9
27+
|
28+
LL | let Ok(0): Option<u8>;
29+
| ^^^^^ ---------- expected due to this
30+
| |
31+
| expected enum `std::option::Option`, found enum `std::result::Result`
32+
|
33+
= note: expected enum `std::option::Option<u8>`
34+
found enum `std::result::Result<_, _>`
35+
36+
error[E0308]: mismatched types
37+
--> $DIR/pat-type-err-let-stmt.rs:15:9
38+
|
39+
LL | let Ok(0) = 42u8;
40+
| ^^^^^ ---- this expression has type `u8`
41+
| |
42+
| expected `u8`, found enum `std::result::Result`
43+
|
44+
= note: expected type `u8`
45+
found enum `std::result::Result<_, _>`
46+
47+
error: aborting due to 4 previous errors
48+
49+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)