about summary refs log tree commit diff
diff options
context:
space:
mode:
authoriDawer <ilnur.iskhakov.oss@outlook.com>2021-11-27 18:13:47 +0500
committeriDawer <ilnur.iskhakov.oss@outlook.com>2021-12-20 00:14:39 +0500
commitb17aefb83a09e9e298d560f9a270b7ca89790203 (patch)
tree25b024100b05596cd56f2d848c07c55d84d24ab9
parent1280961b51fb0b80867758dc5062bca3b9f0f6fe (diff)
downloadrust-b17aefb83a09e9e298d560f9a270b7ca89790203.tar.gz
rust-b17aefb83a09e9e298d560f9a270b7ca89790203.zip
internal: Normalize field type after substituting
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs1
-rw-r--r--crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs4
-rw-r--r--crates/hir_ty/src/diagnostics/match_check/usefulness.rs3
-rw-r--r--crates/hir_ty/src/infer.rs22
-rw-r--r--crates/ide_diagnostics/src/handlers/missing_match_arms.rs24
5 files changed, 50 insertions, 4 deletions
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 00c7b952157..a8c4026e31f 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -285,6 +285,7 @@ impl ExprValidator {
         let pattern_arena = Arena::new();
         let cx = MatchCheckCtx {
             module: self.owner.module(db.upcast()),
+            body: self.owner,
             db,
             pattern_arena: &pattern_arena,
         };
diff --git a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
index acb3280329f..84ded517ba7 100644
--- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
+++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
@@ -53,7 +53,7 @@ use smallvec::{smallvec, SmallVec};
 use stdx::never;
 use syntax::SmolStr;
 
-use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind};
+use crate::{infer::normalize, AdtId, Interner, Scalar, Ty, TyExt, TyKind};
 
 use super::{
     usefulness::{helper::Captures, MatchCheckCtx, PatCtxt},
@@ -753,8 +753,8 @@ impl<'p> Fields<'p> {
         let fields_len = variant.variant_data(cx.db.upcast()).fields().len() as u32;
 
         (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| {
-            // TODO check ty has been normalized
             let ty = field_ty[fid].clone().substitute(Interner, substs);
+            let ty = normalize(cx.db, cx.body, ty);
             let is_visible = matches!(adt, hir_def::AdtId::EnumId(..))
                 || visibility[fid].is_visible_from(cx.db.upcast(), cx.module);
             let is_uninhabited = cx.is_uninhabited(&ty);
diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
index 9b00c4dccf3..e8a13955d21 100644
--- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
+++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
@@ -273,7 +273,7 @@
 
 use std::iter::once;
 
-use hir_def::{AdtId, HasModule, ModuleId};
+use hir_def::{AdtId, DefWithBodyId, HasModule, ModuleId};
 use smallvec::{smallvec, SmallVec};
 use typed_arena::Arena;
 
@@ -285,6 +285,7 @@ use self::{helper::Captures, ArmType::*, Usefulness::*};
 
 pub(crate) struct MatchCheckCtx<'a, 'p> {
     pub(crate) module: ModuleId,
+    pub(crate) body: DefWithBodyId,
     pub(crate) db: &'a dyn HirDatabase,
     /// Lowered patterns from arms plus generated by the check.
     pub(crate) pattern_arena: &'p Arena<DeconstructedPat<'p>>,
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 1bc19323da9..95d9a6735a3 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -16,7 +16,7 @@
 use std::ops::Index;
 use std::sync::Arc;
 
-use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar};
+use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
 use hir_def::{
     body::Body,
     data::{ConstData, FunctionData, StaticData},
@@ -70,6 +70,26 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
     Arc::new(ctx.resolve_all())
 }
 
+/// Fully normalize all the types found within `ty` in context of `owner` body definition.
+///
+/// This is appropriate to use only after type-check: it assumes
+/// that normalization will succeed, for example.
+pub(crate) fn normalize(db: &dyn HirDatabase, owner: DefWithBodyId, ty: Ty) -> Ty {
+    if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) {
+        return ty;
+    }
+    let krate = owner.module(db.upcast()).krate();
+    let trait_env = owner
+        .as_generic_def_id()
+        .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
+    let mut table = unify::InferenceTable::new(db, trait_env.clone());
+
+    let ty_with_vars = table.normalize_associated_types_in(ty);
+    table.resolve_obligations_as_possible();
+    table.propagate_diverging_flag();
+    table.resolve_completely(ty_with_vars)
+}
+
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 enum ExprOrPatId {
     ExprId(ExprId),
diff --git a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs
index 5a357046970..5ef4d2a2266 100644
--- a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs
+++ b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs
@@ -863,6 +863,30 @@ fn main() {
         );
     }
 
+    #[test]
+    fn normalize_field_ty() {
+        check_diagnostics_no_bails(
+            r"
+trait Trait { type Projection; }
+enum E {Foo, Bar}
+struct A;
+impl Trait for A { type Projection = E; }
+struct Next<T: Trait>(T::Projection);
+static __: () = {
+    let n: Next<A> = Next(E::Foo);
+    match n { Next(E::Foo) => {} }
+    //    ^ error: missing match arm
+    match n { Next(E::Foo | E::Bar) => {} }
+    match n { Next(E::Foo | _     ) => {} }
+    match n { Next(_      | E::Bar) => {} }
+    match n {      _ | Next(E::Bar) => {} }
+    match &n { Next(E::Foo | E::Bar) => {} }
+    match &n {      _ | Next(E::Bar) => {} }
+};",
+
+        );
+    }
+
     mod false_negatives {
         //! The implementation of match checking here is a work in progress. As we roll this out, we
         //! prefer false negatives to false positives (ideally there would be no false positives). This