Blame user type in pat type error.
This commit is contained in:
parent
f2c6a19c0d
commit
f8d2cce0ce
12 changed files with 138 additions and 20 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 }))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
||||
|
|
|
|||
8
src/test/ui/pattern/pat-type-err-formal-param.rs
Normal file
8
src/test/ui/pattern/pat-type-err-formal-param.rs
Normal 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
|
||||
11
src/test/ui/pattern/pat-type-err-formal-param.stderr
Normal file
11
src/test/ui/pattern/pat-type-err-formal-param.stderr
Normal 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`.
|
||||
16
src/test/ui/pattern/pat-type-err-let-stmt.rs
Normal file
16
src/test/ui/pattern/pat-type-err-let-stmt.rs
Normal 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
|
||||
}
|
||||
49
src/test/ui/pattern/pat-type-err-let-stmt.stderr
Normal file
49
src/test/ui/pattern/pat-type-err-let-stmt.stderr
Normal 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`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue