Blame user type in pat type error.

This commit is contained in:
Mazdak Farrokhzad 2019-12-30 11:39:13 +01:00
parent f2c6a19c0d
commit f8d2cce0ce
12 changed files with 138 additions and 20 deletions

View file

@ -581,8 +581,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
) {
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,

View file

@ -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<Span>,
/// 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.

View file

@ -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 }))
}

View file

@ -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

View file

@ -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(&param.pat, param_ty, None);
fcx.check_pat_top(&param.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 };

View file

@ -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<DiagnosticBuilder<'tcx>> {
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<Span>) {
///
/// 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<Span>,
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

View file

@ -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"]

View file

@ -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
|

View file

@ -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

View file

@ -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`.

View file

@ -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<u8>`.
let Ok(0): Option<u8> = 42u8;
//~^ ERROR mismatched types
//~| ERROR mismatched types
// We want to point at the `Option<u8>`.
let Ok(0): Option<u8>;
//~^ ERROR mismatched types
// We want to point at the scrutinee.
let Ok(0) = 42u8; //~ ERROR mismatched types
}

View file

@ -0,0 +1,49 @@
error[E0308]: mismatched types
--> $DIR/pat-type-err-let-stmt.rs:6:29
|
LL | let Ok(0): Option<u8> = 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<u8>`
found type `u8`
error[E0308]: mismatched types
--> $DIR/pat-type-err-let-stmt.rs:6:9
|
LL | let Ok(0): Option<u8> = 42u8;
| ^^^^^ ---------- expected due to this
| |
| expected enum `std::option::Option`, found enum `std::result::Result`
|
= note: expected enum `std::option::Option<u8>`
found enum `std::result::Result<_, _>`
error[E0308]: mismatched types
--> $DIR/pat-type-err-let-stmt.rs:11:9
|
LL | let Ok(0): Option<u8>;
| ^^^^^ ---------- expected due to this
| |
| expected enum `std::option::Option`, found enum `std::result::Result`
|
= note: expected enum `std::option::Option<u8>`
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`.