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 // FIXME: Due to shortcomings in the current type system implementation, only emit this
// diagnostic if there are no type mismatches in the containing function. // 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; return;
} }

View file

@ -137,8 +137,12 @@ pub struct InferenceResult {
assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
diagnostics: Vec<InferenceDiagnostic>, diagnostics: Vec<InferenceDiagnostic>,
pub type_of_expr: ArenaMap<ExprId, Ty>, 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 type_of_pat: ArenaMap<PatId, Ty>,
pub(super) type_mismatches: ArenaMap<ExprId, TypeMismatch>, type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
/// Interned Unknown to return references to. /// Interned Unknown to return references to.
standard_types: InternedStandardTypes, standard_types: InternedStandardTypes,
} }
@ -163,7 +167,22 @@ impl InferenceResult {
self.assoc_resolutions.get(&id.into()).copied() self.assoc_resolutions.get(&id.into()).copied()
} }
pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> { 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( pub fn add_diagnostics(
&self, &self,

View file

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

View file

@ -10,7 +10,7 @@ use hir_def::{
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
use super::{BindingMode, Expectation, InferenceContext}; use super::{BindingMode, Expectation, InferenceContext, TypeMismatch};
use crate::{ use crate::{
lower::lower_to_chalk_mutability, static_lifetime, Interner, Substitution, Ty, TyBuilder, lower::lower_to_chalk_mutability, static_lifetime, Interner, Substitution, Ty, TyBuilder,
TyExt, TyKind, TyExt, TyKind,
@ -266,7 +266,10 @@ impl<'a> InferenceContext<'a> {
// use a new type variable if we got error type here // use a new type variable if we got error type here
let ty = self.insert_type_vars_shallow(ty); let ty = self.insert_type_vars_shallow(ty);
if !self.unify(&ty, expected) { 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); let ty = self.resolve_ty_as_possible(ty);
self.write_pat_ty(pat, ty.clone()); 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, 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() { for (expr, ty) in inference_result.type_of_expr.iter() {

View file

@ -546,7 +546,9 @@ fn infer_const_pattern() {
273..276 'Bar': usize 273..276 'Bar': usize
280..283 'Bar': usize 280..283 'Bar': usize
200..223: expected (), got Foo 200..223: expected (), got Foo
211..214: expected (), got Foo
262..285: expected (), got usize 262..285: expected (), got usize
273..276: expected (), got usize
"#]], "#]],
); );
} }
@ -703,7 +705,7 @@ fn box_pattern() {
#[test] #[test]
fn tuple_ellipsis_pattern() { fn tuple_ellipsis_pattern() {
check_infer( check_infer_with_mismatches(
r#" r#"
fn foo(tuple: (u8, i16, f32)) { fn foo(tuple: (u8, i16, f32)) {
match tuple { match tuple {
@ -744,6 +746,8 @@ fn foo(tuple: (u8, i16, f32)) {
186..200 '{/*too long*/}': () 186..200 '{/*too long*/}': ()
209..210 '_': (u8, i16, f32) 209..210 '_': (u8, i16, f32)
214..216 '{}': () 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 ()
"#]],
);
}