internal: Record mismatches of pattern types.

This commit is contained in:
Dawer 2021-05-19 09:23:16 +05:00
parent 49a5d6a8d4
commit 472317c008
6 changed files with 93 additions and 11 deletions

View File

@ -211,7 +211,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
// FIXME: Due to shortcomings in the current type system implementation, only emit this
// diagnostic if there are no type mismatches in the containing function.
if self.infer.type_mismatches.iter().next().is_some() {
if self.infer.expr_type_mismatches().next().is_some() {
return;
}

View File

@ -137,8 +137,12 @@ pub struct InferenceResult {
assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
diagnostics: Vec<InferenceDiagnostic>,
pub type_of_expr: ArenaMap<ExprId, Ty>,
/// For each pattern record the type it resolves to.
///
/// **Note**: When a pattern type is resolved it may still contain
/// unresolved or missing subpatterns or subpatterns of mismatched types.
pub type_of_pat: ArenaMap<PatId, Ty>,
pub(super) type_mismatches: ArenaMap<ExprId, TypeMismatch>,
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
/// Interned Unknown to return references to.
standard_types: InternedStandardTypes,
}
@ -163,7 +167,22 @@ impl InferenceResult {
self.assoc_resolutions.get(&id.into()).copied()
}
pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
self.type_mismatches.get(expr)
self.type_mismatches.get(&expr.into())
}
pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch> {
self.type_mismatches.get(&pat.into())
}
pub fn expr_type_mismatches(&self) -> impl Iterator<Item = (ExprId, &TypeMismatch)> {
self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat {
ExprOrPatId::ExprId(expr) => Some((expr, mismatch)),
_ => None,
})
}
pub fn pat_type_mismatches(&self) -> impl Iterator<Item = (PatId, &TypeMismatch)> {
self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat {
ExprOrPatId::PatId(pat) => Some((pat, mismatch)),
_ => None,
})
}
pub fn add_diagnostics(
&self,

View File

@ -42,7 +42,7 @@ impl<'a> InferenceContext<'a> {
let could_unify = self.unify(&ty, &expected.ty);
if !could_unify {
self.result.type_mismatches.insert(
tgt_expr,
tgt_expr.into(),
TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() },
);
}
@ -54,9 +54,10 @@ impl<'a> InferenceContext<'a> {
pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
let ty = self.infer_expr_inner(expr, &expected);
let ty = if !self.coerce(&ty, &expected.coercion_target()) {
self.result
.type_mismatches
.insert(expr, TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() });
self.result.type_mismatches.insert(
expr.into(),
TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() },
);
// Return actual type when type mismatch.
// This is needed for diagnostic when return type mismatch.
ty

View File

@ -10,7 +10,7 @@ use hir_def::{
};
use hir_expand::name::Name;
use super::{BindingMode, Expectation, InferenceContext};
use super::{BindingMode, Expectation, InferenceContext, TypeMismatch};
use crate::{
lower::lower_to_chalk_mutability, static_lifetime, Interner, Substitution, Ty, TyBuilder,
TyExt, TyKind,
@ -266,7 +266,10 @@ impl<'a> InferenceContext<'a> {
// use a new type variable if we got error type here
let ty = self.insert_type_vars_shallow(ty);
if !self.unify(&ty, expected) {
// FIXME record mismatch, we need to change the type of self.type_mismatches for that
self.result.type_mismatches.insert(
pat.into(),
TypeMismatch { expected: expected.clone(), actual: ty.clone() },
);
}
let ty = self.resolve_ty_as_possible(ty);
self.write_pat_ty(pat, ty.clone());

View File

@ -130,7 +130,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
}
Err(SyntheticSyntax) => continue,
};
types.push((syntax_ptr, ty));
types.push((syntax_ptr.clone(), ty));
if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat) {
mismatches.push((syntax_ptr, mismatch));
}
}
for (expr, ty) in inference_result.type_of_expr.iter() {

View File

@ -546,7 +546,9 @@ fn infer_const_pattern() {
273..276 'Bar': usize
280..283 'Bar': usize
200..223: expected (), got Foo
211..214: expected (), got Foo
262..285: expected (), got usize
273..276: expected (), got usize
"#]],
);
}
@ -703,7 +705,7 @@ fn box_pattern() {
#[test]
fn tuple_ellipsis_pattern() {
check_infer(
check_infer_with_mismatches(
r#"
fn foo(tuple: (u8, i16, f32)) {
match tuple {
@ -744,6 +746,8 @@ fn foo(tuple: (u8, i16, f32)) {
186..200 '{/*too long*/}': ()
209..210 '_': (u8, i16, f32)
214..216 '{}': ()
136..142: expected (u8, i16, f32), got (u8, i16)
170..182: expected (u8, i16, f32), got (u8, i16, f32, _)
"#]],
);
}
@ -851,3 +855,55 @@ fn f(e: Enum) {
"#,
)
}
#[test]
fn type_mismatch_in_or_pattern() {
check_infer_with_mismatches(
r#"
fn main() {
match (false,) {
(true | (),) => {}
(() | true,) => {}
(_ | (),) => {}
(() | _,) => {}
}
}
"#,
expect![[r#"
10..142 '{ ... } }': ()
16..140 'match ... }': ()
22..30 '(false,)': (bool,)
23..28 'false': bool
41..53 '(true | (),)': (bool,)
42..46 'true': bool
42..46 'true': bool
42..51 'true | ()': bool
49..51 '()': ()
57..59 '{}': ()
68..80 '(() | true,)': ((),)
69..71 '()': ()
69..78 '() | true': ()
74..78 'true': bool
74..78 'true': bool
84..86 '{}': ()
95..104 '(_ | (),)': (bool,)
96..97 '_': bool
96..102 '_ | ()': bool
100..102 '()': ()
108..110 '{}': ()
119..128 '(() | _,)': ((),)
120..122 '()': ()
120..126 '() | _': ()
125..126 '_': bool
132..134 '{}': ()
49..51: expected bool, got ()
68..80: expected (bool,), got ((),)
69..71: expected bool, got ()
69..78: expected bool, got ()
100..102: expected bool, got ()
119..128: expected (bool,), got ((),)
120..122: expected bool, got ()
120..126: expected bool, got ()
"#]],
);
}