From f8d2cce0ce24981269e6994c332bff7f033b59e0 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 30 Dec 2019 11:39:13 +0100 Subject: [PATCH] Blame user type in pat type error. --- src/librustc/infer/error_reporting/mod.rs | 7 ++- src/librustc/traits/mod.rs | 8 ++- src/librustc/traits/structural_impls.rs | 4 +- src/librustc_typeck/check/_match.rs | 2 +- src/librustc_typeck/check/mod.rs | 21 ++++++-- src/librustc_typeck/check/pat.rs | 29 +++++++---- src/librustc_typeck/lib.rs | 1 + .../ui/or-patterns/inconsistent-modes.stderr | 2 +- .../ui/pattern/pat-type-err-formal-param.rs | 8 +++ .../pattern/pat-type-err-formal-param.stderr | 11 +++++ src/test/ui/pattern/pat-type-err-let-stmt.rs | 16 ++++++ .../ui/pattern/pat-type-err-let-stmt.stderr | 49 +++++++++++++++++++ 12 files changed, 138 insertions(+), 20 deletions(-) create mode 100644 src/test/ui/pattern/pat-type-err-formal-param.rs create mode 100644 src/test/ui/pattern/pat-type-err-formal-param.stderr create mode 100644 src/test/ui/pattern/pat-type-err-let-stmt.rs create mode 100644 src/test/ui/pattern/pat-type-err-let-stmt.stderr diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 5c561a87b054..7254bb52d703 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -581,8 +581,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { exp_found: Option>>, ) { match cause.code { - ObligationCauseCode::Pattern { span, ty } => { - let ty = self.resolve_vars_if_possible(&ty); + ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => { + let ty = self.resolve_vars_if_possible(&root_ty); if ty.is_suggestable() { // don't show type `_` err.span_label(span, format!("this expression has type `{}`", ty)); @@ -600,6 +600,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } } + ObligationCauseCode::Pattern { origin_expr: false, span: Some(span), .. } => { + err.span_label(span, "expected due to this"); + } ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { source, ref prior_arms, diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 6142dc25f222..f726a52946ed 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -251,8 +251,12 @@ pub enum ObligationCauseCode<'tcx> { /// Type error arising from type checking a pattern against an expected type. Pattern { - span: Span, - ty: Ty<'tcx>, + /// The span of the scrutinee or type expression which caused the `root_ty` type. + span: Option, + /// The root expected type induced by a scrutinee or type expression. + root_ty: Ty<'tcx>, + /// Whether the `Span` came from an expression or a type expression. + origin_expr: bool, }, /// Constants in patterns must have `Structural` type. diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index cfc27438b50f..7b14ada8469a 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -521,7 +521,9 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { discrim_hir_id, }) }), - super::Pattern { span, ty } => tcx.lift(&ty).map(|ty| super::Pattern { span, ty }), + super::Pattern { span, root_ty, origin_expr } => { + tcx.lift(&root_ty).map(|root_ty| super::Pattern { span, root_ty, origin_expr }) + } super::IfExpression(box super::IfExpressionCause { then, outer, semicolon }) => { Some(super::IfExpression(box super::IfExpressionCause { then, outer, semicolon })) } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 0d7ddd0fc481..e4cf54f629c5 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -61,7 +61,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|arm| { let mut all_pats_diverge = Diverges::WarnedAlways; self.diverges.set(Diverges::Maybe); - self.check_pat_top(&arm.pat, discrim_ty, Some(discrim.span)); + self.check_pat_top(&arm.pat, discrim_ty, Some(discrim.span), true); all_pats_diverge &= self.diverges.get(); // As discussed with @eddyb, this is for disabling unreachable_code diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 41313ffbab6c..2a1297966a94 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1293,9 +1293,11 @@ fn check_fn<'a, 'tcx>( }; // Add formal parameters. - for (param_ty, param) in fn_sig.inputs().iter().copied().chain(maybe_va_list).zip(body.params) { + let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs); + let inputs_fn = fn_sig.inputs().iter().copied(); + for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { // Check the pattern. - fcx.check_pat_top(¶m.pat, param_ty, None); + fcx.check_pat_top(¶m.pat, param_ty, try { inputs_hir?.get(idx)?.span }, false); // Check that argument is Sized. // The check for a non-trivial pattern is a hack to avoid duplicate warnings @@ -4276,16 +4278,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Type check a `let` statement. pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { + // Determine and write the type which we'll check the pattern against. let ty = self.local_ty(local.span, local.hir_id).decl_ty; self.write_ty(local.hir_id, ty); + // Type check the initializer. if let Some(ref init) = local.init { let init_ty = self.check_decl_initializer(local, &init); self.overwrite_local_ty_if_err(local, ty, init_ty); } - self.check_pat_top(&local.pat, ty, local.init.map(|init| init.span)); + // Does the expected pattern type originate from an expression and what is the span? + let (origin_expr, ty_span) = match (local.ty, local.init) { + (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. + (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee. + _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. + }; + + // Type check the pattern. Override if necessary to avoid knock-on errors. + self.check_pat_top(&local.pat, ty, ty_span, origin_expr); let pat_ty = self.node_ty(local.pat.hir_id); self.overwrite_local_ty_if_err(local, ty, pat_ty); } @@ -4297,7 +4310,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty: Ty<'tcx>, ) { if ty.references_error() { - // Override the types everywhere with `types.err` to avoid knock down errors. + // Override the types everywhere with `types.err` to avoid knock on errors. self.write_ty(local.hir_id, ty); self.write_ty(local.pat.hir_id, ty); let local_ty = LocalTy { decl_ty, revealed_ty: ty }; diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index a3ef41f0de5b..759118933960 100644 --- a/src/librustc_typeck/check/pat.rs +++ b/src/librustc_typeck/check/pat.rs @@ -37,9 +37,13 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; struct TopInfo<'tcx> { /// The `expected` type at the top level of type checking a pattern. expected: Ty<'tcx>, + /// Was the origin of the `span` from a scrutinee expression? + /// + /// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter. + origin_expr: bool, /// The span giving rise to the `expected` type, if one could be provided. /// - /// This is the span of the scrutinee as in: + /// If `origin_expr` is `true`, then this is the span of the scrutinee as in: /// /// - `match scrutinee { ... }` /// - `let _ = scrutinee;` @@ -70,11 +74,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> { actual: Ty<'tcx>, ti: TopInfo<'tcx>, ) -> Option> { - let cause = if let Some(span) = ti.span { - self.cause(cause_span, Pattern { span, ty: ti.expected }) - } else { - self.misc(cause_span) - }; + let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr }; + let cause = self.cause(cause_span, code); self.demand_eqtype_with_origin(&cause, expected, actual) } @@ -92,11 +93,21 @@ impl<'tcx> FnCtxt<'_, 'tcx> { impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Type check the given top level pattern against the `expected` type. /// - /// If a `Some(span)` is provided, then the `span` represents the scrutinee's span. + /// If a `Some(span)` is provided and `origin_expr` holds, + /// then the `span` represents the scrutinee's span. /// The scrutinee is found in e.g. `match scrutinee { ... }` and `let pat = scrutinee;`. - pub fn check_pat_top(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, span: Option) { + /// + /// Otherwise, `Some(span)` represents the span of a type expression + /// which originated the `expected` type. + pub fn check_pat_top( + &self, + pat: &'tcx Pat<'tcx>, + expected: Ty<'tcx>, + span: Option, + origin_expr: bool, + ) { let def_bm = BindingMode::BindByValue(hir::Mutability::Not); - self.check_pat(pat, expected, def_bm, TopInfo { expected, span }); + self.check_pat(pat, expected, def_bm, TopInfo { expected, origin_expr, span }); } /// Type check the given `pat` against the `expected` type diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index d9b7e98ea75c..f06fe1e4bf21 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -65,6 +65,7 @@ This API is completely unstable and subject to change. #![feature(in_band_lifetimes)] #![feature(nll)] #![feature(slice_patterns)] +#![feature(try_blocks)] #![feature(never_type)] #![recursion_limit = "256"] diff --git a/src/test/ui/or-patterns/inconsistent-modes.stderr b/src/test/ui/or-patterns/inconsistent-modes.stderr index 80d914b8d236..7c1638ff94d0 100644 --- a/src/test/ui/or-patterns/inconsistent-modes.stderr +++ b/src/test/ui/or-patterns/inconsistent-modes.stderr @@ -60,7 +60,7 @@ error[E0308]: mismatched types --> $DIR/inconsistent-modes.rs:13:25 | LL | let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0); - | ^^^^^^^^^ ------ this expression has type `std::result::Result<&u8, &mut u8>` + | ^^^^^^^^^ -------------------- expected due to this | | | types differ in mutability | diff --git a/src/test/ui/pattern/pat-type-err-formal-param.rs b/src/test/ui/pattern/pat-type-err-formal-param.rs new file mode 100644 index 000000000000..54336b349232 --- /dev/null +++ b/src/test/ui/pattern/pat-type-err-formal-param.rs @@ -0,0 +1,8 @@ +// Test the `.span_label(..)` to the type when there's a +// type error in a pattern due to a the formal parameter. + +fn main() {} + +struct Tuple(u8); + +fn foo(Tuple(_): String) {} //~ ERROR mismatched types diff --git a/src/test/ui/pattern/pat-type-err-formal-param.stderr b/src/test/ui/pattern/pat-type-err-formal-param.stderr new file mode 100644 index 000000000000..2d7eb62faef2 --- /dev/null +++ b/src/test/ui/pattern/pat-type-err-formal-param.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/pat-type-err-formal-param.rs:8:8 + | +LL | fn foo(Tuple(_): String) {} + | ^^^^^^^^ ------ expected due to this + | | + | expected struct `std::string::String`, found struct `Tuple` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/pattern/pat-type-err-let-stmt.rs b/src/test/ui/pattern/pat-type-err-let-stmt.rs new file mode 100644 index 000000000000..6e9850b655cb --- /dev/null +++ b/src/test/ui/pattern/pat-type-err-let-stmt.rs @@ -0,0 +1,16 @@ +// Test the `.span_label` to the type / scrutinee +// when there's a type error in checking a pattern. + +fn main() { + // We want to point at the `Option`. + let Ok(0): Option = 42u8; + //~^ ERROR mismatched types + //~| ERROR mismatched types + + // We want to point at the `Option`. + let Ok(0): Option; + //~^ ERROR mismatched types + + // We want to point at the scrutinee. + let Ok(0) = 42u8; //~ ERROR mismatched types +} diff --git a/src/test/ui/pattern/pat-type-err-let-stmt.stderr b/src/test/ui/pattern/pat-type-err-let-stmt.stderr new file mode 100644 index 000000000000..d75fa3f247c4 --- /dev/null +++ b/src/test/ui/pattern/pat-type-err-let-stmt.stderr @@ -0,0 +1,49 @@ +error[E0308]: mismatched types + --> $DIR/pat-type-err-let-stmt.rs:6:29 + | +LL | let Ok(0): Option = 42u8; + | ---------- ^^^^ + | | | + | | expected enum `std::option::Option`, found `u8` + | | help: try using a variant of the expected enum: `Some(42u8)` + | expected due to this + | + = note: expected enum `std::option::Option` + found type `u8` + +error[E0308]: mismatched types + --> $DIR/pat-type-err-let-stmt.rs:6:9 + | +LL | let Ok(0): Option = 42u8; + | ^^^^^ ---------- expected due to this + | | + | expected enum `std::option::Option`, found enum `std::result::Result` + | + = note: expected enum `std::option::Option` + found enum `std::result::Result<_, _>` + +error[E0308]: mismatched types + --> $DIR/pat-type-err-let-stmt.rs:11:9 + | +LL | let Ok(0): Option; + | ^^^^^ ---------- expected due to this + | | + | expected enum `std::option::Option`, found enum `std::result::Result` + | + = note: expected enum `std::option::Option` + found enum `std::result::Result<_, _>` + +error[E0308]: mismatched types + --> $DIR/pat-type-err-let-stmt.rs:15:9 + | +LL | let Ok(0) = 42u8; + | ^^^^^ ---- this expression has type `u8` + | | + | expected `u8`, found enum `std::result::Result` + | + = note: expected type `u8` + found enum `std::result::Result<_, _>` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`.