fix: silence mismatches involving unresolved projections

This commit is contained in:
roife 2024-03-29 13:13:31 +08:00
parent 899db83128
commit 8d6b65c544
4 changed files with 58 additions and 22 deletions

View file

@ -29,6 +29,7 @@ pub trait TyExt {
fn contains_unknown(&self) -> bool; fn contains_unknown(&self) -> bool;
fn is_ty_var(&self) -> bool; fn is_ty_var(&self) -> bool;
fn is_union(&self) -> bool; fn is_union(&self) -> bool;
fn is_projection(&self) -> bool;
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
fn as_builtin(&self) -> Option<BuiltinType>; fn as_builtin(&self) -> Option<BuiltinType>;
@ -101,6 +102,13 @@ impl TyExt for Ty {
matches!(self.adt_id(Interner), Some(AdtId(hir_def::AdtId::UnionId(_)))) matches!(self.adt_id(Interner), Some(AdtId(hir_def::AdtId::UnionId(_))))
} }
fn is_projection(&self) -> bool {
matches!(
self.kind(Interner),
TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(_, _)
)
}
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
match self.kind(Interner) { match self.kind(Interner) {
TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),

View file

@ -429,6 +429,8 @@ pub struct InferenceResult {
/// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop. /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop.
pub type_of_for_iterator: FxHashMap<ExprId, Ty>, pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
/// Whether there are any type-mismatching errors in the result.
pub(crate) has_errors: bool,
/// Interned common types to return references to. /// Interned common types to return references to.
standard_types: InternedStandardTypes, standard_types: InternedStandardTypes,
/// Stores the types which were implicitly dereferenced in pattern binding modes. /// Stores the types which were implicitly dereferenced in pattern binding modes.
@ -654,6 +656,7 @@ impl<'a> InferenceContext<'a> {
type_of_rpit, type_of_rpit,
type_of_for_iterator, type_of_for_iterator,
type_mismatches, type_mismatches,
has_errors,
standard_types: _, standard_types: _,
pat_adjustments, pat_adjustments,
binding_modes: _, binding_modes: _,
@ -695,16 +698,33 @@ impl<'a> InferenceContext<'a> {
for ty in type_of_for_iterator.values_mut() { for ty in type_of_for_iterator.values_mut() {
*ty = table.resolve_completely(ty.clone()); *ty = table.resolve_completely(ty.clone());
} }
*has_errors = !type_mismatches.is_empty();
type_mismatches.retain(|_, mismatch| { type_mismatches.retain(|_, mismatch| {
mismatch.expected = table.resolve_completely(mismatch.expected.clone()); mismatch.expected = table.resolve_completely(mismatch.expected.clone());
mismatch.actual = table.resolve_completely(mismatch.actual.clone()); mismatch.actual = table.resolve_completely(mismatch.actual.clone());
let unresolved_ty_mismatch = || {
chalk_ir::zip::Zip::zip_with( chalk_ir::zip::Zip::zip_with(
&mut UnknownMismatch(self.db), &mut UnknownMismatch(self.db, |ty| matches!(ty.kind(Interner), TyKind::Error)),
Variance::Invariant, Variance::Invariant,
&mismatch.expected, &mismatch.expected,
&mismatch.actual, &mismatch.actual,
) )
.is_ok() .is_ok()
};
let unresolved_projections_mismatch = || {
chalk_ir::zip::Zip::zip_with(
&mut UnknownMismatch(self.db, |ty| ty.contains_unknown() && ty.is_projection()),
chalk_ir::Variance::Invariant,
&mismatch.expected,
&mismatch.actual,
)
.is_ok()
};
unresolved_ty_mismatch() && unresolved_projections_mismatch()
}); });
diagnostics.retain_mut(|diagnostic| { diagnostics.retain_mut(|diagnostic| {
use InferenceDiagnostic::*; use InferenceDiagnostic::*;
@ -1646,11 +1666,16 @@ impl std::ops::BitOrAssign for Diverges {
*self = *self | other; *self = *self | other;
} }
} }
/// A zipper that checks for unequal `{unknown}` occurrences in the two types. Used to filter out /// A zipper that checks for unequal `{unknown}` occurrences in the two types.
/// mismatch diagnostics that only differ in `{unknown}`. These mismatches are usually not helpful. /// Types that have different constructors are filtered out and tested by the
/// As the cause is usually an underlying name resolution problem. /// provided closure `F`. Commonly used to filter out mismatch diagnostics that
struct UnknownMismatch<'db>(&'db dyn HirDatabase); /// only differ in `{unknown}`. These mismatches are usually not helpful, as the
impl chalk_ir::zip::Zipper<Interner> for UnknownMismatch<'_> { /// cause is usually an underlying name resolution problem.
///
/// E.g. when F is `|ty| matches!(ty.kind(Interer), TyKind::Unknown)`, the zipper
/// will skip over all mismatches that only differ in `{unknown}`.
struct UnknownMismatch<'db, F: Fn(&Ty) -> bool>(&'db dyn HirDatabase, F);
impl<F: Fn(&Ty) -> bool> chalk_ir::zip::Zipper<Interner> for UnknownMismatch<'_, F> {
fn zip_tys(&mut self, variance: Variance, a: &Ty, b: &Ty) -> chalk_ir::Fallible<()> { fn zip_tys(&mut self, variance: Variance, a: &Ty, b: &Ty) -> chalk_ir::Fallible<()> {
let zip_substs = |this: &mut Self, let zip_substs = |this: &mut Self,
variances, variances,
@ -1721,7 +1746,7 @@ impl chalk_ir::zip::Zipper<Interner> for UnknownMismatch<'_> {
zip_substs(self, None, &fn_ptr_a.substitution.0, &fn_ptr_b.substitution.0)? zip_substs(self, None, &fn_ptr_a.substitution.0, &fn_ptr_b.substitution.0)?
} }
(TyKind::Error, TyKind::Error) => (), (TyKind::Error, TyKind::Error) => (),
(TyKind::Error, _) | (_, TyKind::Error) => return Err(chalk_ir::NoSolution), _ if (self.1)(a) || (self.1)(b) => return Err(chalk_ir::NoSolution),
_ => (), _ => (),
} }

View file

@ -90,7 +90,7 @@ pub enum MirLowerError {
UnresolvedField, UnresolvedField,
UnsizedTemporary(Ty), UnsizedTemporary(Ty),
MissingFunctionDefinition(DefWithBodyId, ExprId), MissingFunctionDefinition(DefWithBodyId, ExprId),
TypeMismatch(TypeMismatch), TypeMismatch(Option<TypeMismatch>),
/// This should never happen. Type mismatch should catch everything. /// This should never happen. Type mismatch should catch everything.
TypeError(&'static str), TypeError(&'static str),
NotSupported(String), NotSupported(String),
@ -170,14 +170,15 @@ impl MirLowerError {
body.pretty_print_expr(db.upcast(), *owner, *it) body.pretty_print_expr(db.upcast(), *owner, *it)
)?; )?;
} }
MirLowerError::TypeMismatch(e) => { MirLowerError::TypeMismatch(e) => match e {
writeln!( Some(e) => writeln!(
f, f,
"Type mismatch: Expected {}, found {}", "Type mismatch: Expected {}, found {}",
e.expected.display(db), e.expected.display(db),
e.actual.display(db), e.actual.display(db),
)?; )?,
} None => writeln!(f, "Type mismatch: types mismatch with {{unknown}}",)?,
},
MirLowerError::GenericArgNotProvided(id, subst) => { MirLowerError::GenericArgNotProvided(id, subst) => {
let parent = id.parent; let parent = id.parent;
let param = &db.generic_params(parent).type_or_consts[id.local_id]; let param = &db.generic_params(parent).type_or_consts[id.local_id];
@ -2152,8 +2153,10 @@ pub fn lower_to_mir(
// need to take this input explicitly. // need to take this input explicitly.
root_expr: ExprId, root_expr: ExprId,
) -> Result<MirBody> { ) -> Result<MirBody> {
if let Some((_, it)) = infer.type_mismatches().next() { if infer.has_errors {
return Err(MirLowerError::TypeMismatch(it.clone())); return Err(MirLowerError::TypeMismatch(
infer.type_mismatches().next().map(|(_, it)| it.clone()),
));
} }
let mut ctx = MirLowerCtx::new(db, owner, body, infer); let mut ctx = MirLowerCtx::new(db, owner, body, infer);
// 0 is return local // 0 is return local

View file

@ -73,8 +73,7 @@ use hir_ty::{
traits::FnTrait, traits::FnTrait,
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg, AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution, GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId, WhereClause,
WhereClause,
}; };
use itertools::Itertools; use itertools::Itertools;
use nameres::diagnostics::DefDiagnosticKind; use nameres::diagnostics::DefDiagnosticKind;
@ -1678,6 +1677,7 @@ impl DefWithBody {
for d in &infer.diagnostics { for d in &infer.diagnostics {
acc.extend(AnyDiagnostic::inference_diagnostic(db, self.into(), d, &source_map)); acc.extend(AnyDiagnostic::inference_diagnostic(db, self.into(), d, &source_map));
} }
for (pat_or_expr, mismatch) in infer.type_mismatches() { for (pat_or_expr, mismatch) in infer.type_mismatches() {
let expr_or_pat = match pat_or_expr { let expr_or_pat = match pat_or_expr {
ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left), ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left),