Skip to content

Commit 1accd9c

Browse files
committed
internal: Normalize field type after substituting
1 parent 3a7531d commit 1accd9c

File tree

5 files changed

+50
-4
lines changed

5 files changed

+50
-4
lines changed

crates/hir_ty/src/diagnostics/expr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ impl ExprValidator {
272272
let pattern_arena = Arena::new();
273273
let cx = MatchCheckCtx {
274274
module: self.owner.module(db.upcast()),
275+
body: self.owner,
275276
db,
276277
pattern_arena: &pattern_arena,
277278
};

crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId};
5252
use smallvec::{smallvec, SmallVec};
5353
use stdx::never;
5454

55-
use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind};
55+
use crate::{infer::normalize, AdtId, Interner, Scalar, Ty, TyExt, TyKind};
5656

5757
use super::{
5858
usefulness::{helper::Captures, MatchCheckCtx, PatCtxt},
@@ -752,8 +752,8 @@ impl<'p> Fields<'p> {
752752
let fields_len = variant.variant_data(cx.db.upcast()).fields().len() as u32;
753753

754754
(0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| {
755-
// TODO check ty has been normalized
756755
let ty = field_ty[fid].clone().substitute(&Interner, substs);
756+
let ty = normalize(cx.db, cx.body, ty);
757757
let is_visible = matches!(adt, hir_def::AdtId::EnumId(..))
758758
|| visibility[fid].is_visible_from(cx.db.upcast(), cx.module);
759759
let is_uninhabited = cx.is_uninhabited(&ty);

crates/hir_ty/src/diagnostics/match_check/usefulness.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@
273273
274274
use std::iter::once;
275275

276-
use hir_def::{AdtId, HasModule, ModuleId};
276+
use hir_def::{AdtId, DefWithBodyId, HasModule, ModuleId};
277277
use smallvec::{smallvec, SmallVec};
278278
use typed_arena::Arena;
279279

@@ -285,6 +285,7 @@ use self::{helper::Captures, ArmType::*, Usefulness::*};
285285

286286
pub(crate) struct MatchCheckCtx<'a, 'p> {
287287
pub(crate) module: ModuleId,
288+
pub(crate) body: DefWithBodyId,
288289
pub(crate) db: &'a dyn HirDatabase,
289290
/// Lowered patterns from arms plus generated by the check.
290291
pub(crate) pattern_arena: &'p Arena<DeconstructedPat<'p>>,

crates/hir_ty/src/infer.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use std::ops::Index;
1717
use std::sync::Arc;
1818

19-
use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar};
19+
use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
2020
use hir_def::{
2121
body::Body,
2222
data::{ConstData, FunctionData, StaticData},
@@ -71,6 +71,26 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
7171
Arc::new(ctx.resolve_all())
7272
}
7373

74+
/// Fully normalize all the types found within `ty` in context of `owner` body definition.
75+
///
76+
/// This is appropriate to use only after type-check: it assumes
77+
/// that normalization will succeed, for example.
78+
pub(crate) fn normalize(db: &dyn HirDatabase, owner: DefWithBodyId, ty: Ty) -> Ty {
79+
if !ty.data(&Interner).flags.intersects(TypeFlags::HAS_PROJECTION) {
80+
return ty;
81+
}
82+
let krate = owner.module(db.upcast()).krate();
83+
let trait_env = owner
84+
.as_generic_def_id()
85+
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
86+
let mut table = unify::InferenceTable::new(db, trait_env.clone());
87+
88+
let ty_with_vars = table.normalize_associated_types_in(ty);
89+
table.resolve_obligations_as_possible();
90+
table.propagate_diverging_flag();
91+
table.resolve_completely(ty_with_vars)
92+
}
93+
7494
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
7595
enum ExprOrPatId {
7696
ExprId(ExprId),

crates/ide_diagnostics/src/handlers/missing_match_arms.rs

+24
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,30 @@ fn main() {
863863
);
864864
}
865865

866+
#[test]
867+
fn normalize_field_ty() {
868+
check_diagnostics_no_bails(
869+
r"
870+
trait Trait { type Projection; }
871+
enum E {Foo, Bar}
872+
struct A;
873+
impl Trait for A { type Projection = E; }
874+
struct Next<T: Trait>(T::Projection);
875+
static __: () = {
876+
let n: Next<A> = Next(E::Foo);
877+
match n { Next(E::Foo) => {} }
878+
// ^ error: missing match arm
879+
match n { Next(E::Foo | E::Bar) => {} }
880+
match n { Next(E::Foo | _ ) => {} }
881+
match n { Next(_ | E::Bar) => {} }
882+
match n { _ | Next(E::Bar) => {} }
883+
match &n { Next(E::Foo | E::Bar) => {} }
884+
match &n { _ | Next(E::Bar) => {} }
885+
};",
886+
887+
);
888+
}
889+
866890
mod false_negatives {
867891
//! The implementation of match checking here is a work in progress. As we roll this out, we
868892
//! prefer false negatives to false positives (ideally there would be no false positives). This

0 commit comments

Comments
 (0)