mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-25 11:17:13 +00:00
Normalize when checking for uninhabited types for pattern exhaustiveness checking
This commit is contained in:
parent
1511c5b7fd
commit
f17bbfc48b
@ -25,7 +25,7 @@ use triomphe::Arc;
|
||||
use typed_arena::Arena;
|
||||
|
||||
use crate::{
|
||||
Adjust, InferenceResult, Interner, Ty, TyExt, TyKind,
|
||||
Adjust, InferenceResult, Interner, TraitEnvironment, Ty, TyExt, TyKind,
|
||||
db::HirDatabase,
|
||||
diagnostics::match_check::{
|
||||
self,
|
||||
@ -74,8 +74,9 @@ impl BodyValidationDiagnostic {
|
||||
let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered();
|
||||
let infer = db.infer(owner);
|
||||
let body = db.body(owner);
|
||||
let env = db.trait_environment_for_body(owner);
|
||||
let mut validator =
|
||||
ExprValidator { owner, body, infer, diagnostics: Vec::new(), validate_lints };
|
||||
ExprValidator { owner, body, infer, diagnostics: Vec::new(), validate_lints, env };
|
||||
validator.validate_body(db);
|
||||
validator.diagnostics
|
||||
}
|
||||
@ -85,6 +86,7 @@ struct ExprValidator {
|
||||
owner: DefWithBodyId,
|
||||
body: Arc<Body>,
|
||||
infer: Arc<InferenceResult>,
|
||||
env: Arc<TraitEnvironment>,
|
||||
diagnostics: Vec<BodyValidationDiagnostic>,
|
||||
validate_lints: bool,
|
||||
}
|
||||
@ -190,7 +192,7 @@ impl ExprValidator {
|
||||
return;
|
||||
}
|
||||
|
||||
let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db);
|
||||
let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone());
|
||||
|
||||
let pattern_arena = Arena::new();
|
||||
let mut m_arms = Vec::with_capacity(arms.len());
|
||||
@ -317,7 +319,7 @@ impl ExprValidator {
|
||||
return;
|
||||
};
|
||||
let pattern_arena = Arena::new();
|
||||
let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db);
|
||||
let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone());
|
||||
for stmt in &**statements {
|
||||
let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else {
|
||||
continue;
|
||||
|
@ -12,9 +12,10 @@ use rustc_pattern_analysis::{
|
||||
};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use stdx::never;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AdtId, Interner, Scalar, Ty, TyExt, TyKind,
|
||||
AdtId, Interner, Scalar, TraitEnvironment, Ty, TyExt, TyKind,
|
||||
db::HirDatabase,
|
||||
infer::normalize,
|
||||
inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from},
|
||||
@ -69,13 +70,19 @@ pub(crate) struct MatchCheckCtx<'db> {
|
||||
body: DefWithBodyId,
|
||||
pub(crate) db: &'db dyn HirDatabase,
|
||||
exhaustive_patterns: bool,
|
||||
env: Arc<TraitEnvironment>,
|
||||
}
|
||||
|
||||
impl<'db> MatchCheckCtx<'db> {
|
||||
pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self {
|
||||
pub(crate) fn new(
|
||||
module: ModuleId,
|
||||
body: DefWithBodyId,
|
||||
db: &'db dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
) -> Self {
|
||||
let def_map = module.crate_def_map(db);
|
||||
let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
|
||||
Self { module, body, db, exhaustive_patterns }
|
||||
Self { module, body, db, exhaustive_patterns, env }
|
||||
}
|
||||
|
||||
pub(crate) fn compute_match_usefulness(
|
||||
@ -100,7 +107,7 @@ impl<'db> MatchCheckCtx<'db> {
|
||||
}
|
||||
|
||||
fn is_uninhabited(&self, ty: &Ty) -> bool {
|
||||
is_ty_uninhabited_from(self.db, ty, self.module)
|
||||
is_ty_uninhabited_from(self.db, ty, self.module, self.env.clone())
|
||||
}
|
||||
|
||||
/// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`.
|
||||
@ -459,8 +466,13 @@ impl PatCx for MatchCheckCtx<'_> {
|
||||
} else {
|
||||
let mut variants = IndexVec::with_capacity(enum_data.variants.len());
|
||||
for &(variant, _) in enum_data.variants.iter() {
|
||||
let is_uninhabited =
|
||||
is_enum_variant_uninhabited_from(cx.db, variant, subst, cx.module);
|
||||
let is_uninhabited = is_enum_variant_uninhabited_from(
|
||||
cx.db,
|
||||
variant,
|
||||
subst,
|
||||
cx.module,
|
||||
self.env.clone(),
|
||||
);
|
||||
let visibility = if is_uninhabited {
|
||||
VariantVisibility::Empty
|
||||
} else {
|
||||
|
@ -7,17 +7,24 @@ use chalk_ir::{
|
||||
};
|
||||
use hir_def::{AdtId, EnumVariantId, ModuleId, VariantId, visibility::Visibility};
|
||||
use rustc_hash::FxHashSet;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
Binders, Interner, Substitution, Ty, TyKind, consteval::try_const_usize, db::HirDatabase,
|
||||
AliasTy, Binders, Interner, Substitution, TraitEnvironment, Ty, TyKind,
|
||||
consteval::try_const_usize, db::HirDatabase,
|
||||
};
|
||||
|
||||
// FIXME: Turn this into a query, it can be quite slow
|
||||
/// Checks whether a type is visibly uninhabited from a particular module.
|
||||
pub(crate) fn is_ty_uninhabited_from(db: &dyn HirDatabase, ty: &Ty, target_mod: ModuleId) -> bool {
|
||||
pub(crate) fn is_ty_uninhabited_from(
|
||||
db: &dyn HirDatabase,
|
||||
ty: &Ty,
|
||||
target_mod: ModuleId,
|
||||
env: Arc<TraitEnvironment>,
|
||||
) -> bool {
|
||||
let _p = tracing::info_span!("is_ty_uninhabited_from", ?ty).entered();
|
||||
let mut uninhabited_from =
|
||||
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
|
||||
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env };
|
||||
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
|
||||
inhabitedness == BREAK_VISIBLY_UNINHABITED
|
||||
}
|
||||
@ -29,11 +36,12 @@ pub(crate) fn is_enum_variant_uninhabited_from(
|
||||
variant: EnumVariantId,
|
||||
subst: &Substitution,
|
||||
target_mod: ModuleId,
|
||||
env: Arc<TraitEnvironment>,
|
||||
) -> bool {
|
||||
let _p = tracing::info_span!("is_enum_variant_uninhabited_from").entered();
|
||||
|
||||
let mut uninhabited_from =
|
||||
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
|
||||
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env };
|
||||
let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst);
|
||||
inhabitedness == BREAK_VISIBLY_UNINHABITED
|
||||
}
|
||||
@ -44,6 +52,7 @@ struct UninhabitedFrom<'a> {
|
||||
// guard for preventing stack overflow in non trivial non terminating types
|
||||
max_depth: usize,
|
||||
db: &'a dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
}
|
||||
|
||||
const CONTINUE_OPAQUELY_INHABITED: ControlFlow<VisiblyUninhabited> = Continue(());
|
||||
@ -78,6 +87,12 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
|
||||
Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
|
||||
Some(1..) => item_ty.super_visit_with(self, outer_binder),
|
||||
},
|
||||
TyKind::Alias(AliasTy::Projection(projection)) => {
|
||||
// FIXME: I think this currently isn't used for monomorphized bodies, so there is no need to handle
|
||||
// `TyKind::AssociatedType`, but perhaps in the future it will.
|
||||
let normalized = self.db.normalize_projection(projection.clone(), self.env.clone());
|
||||
self.visit_ty(&normalized, outer_binder)
|
||||
}
|
||||
_ => CONTINUE_OPAQUELY_INHABITED,
|
||||
};
|
||||
self.recursive_ty.remove(ty);
|
||||
|
@ -25,7 +25,7 @@ use syntax::TextRange;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
|
||||
Adjust, Adjustment, AutoBorrow, CallableDefId, TraitEnvironment, TyBuilder, TyExt,
|
||||
consteval::ConstEvalError,
|
||||
db::{HirDatabase, InternedClosure, InternedClosureId},
|
||||
display::{DisplayTarget, HirDisplay, hir_display_with_store},
|
||||
@ -79,6 +79,7 @@ struct MirLowerCtx<'db> {
|
||||
infer: &'db InferenceResult,
|
||||
resolver: Resolver<'db>,
|
||||
drop_scopes: Vec<DropScope>,
|
||||
env: Arc<TraitEnvironment>,
|
||||
}
|
||||
|
||||
// FIXME: Make this smaller, its stored in database queries
|
||||
@ -288,6 +289,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
closures: vec![],
|
||||
};
|
||||
let resolver = owner.resolver(db);
|
||||
let env = db.trait_environment_for_body(owner);
|
||||
|
||||
MirLowerCtx {
|
||||
result: mir,
|
||||
@ -300,6 +302,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
labeled_loop_blocks: Default::default(),
|
||||
discr_temp: None,
|
||||
drop_scopes: vec![DropScope::default()],
|
||||
env,
|
||||
}
|
||||
}
|
||||
|
||||
@ -944,10 +947,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
let cast_kind = if source_ty.as_reference().is_some() {
|
||||
CastKind::PointerCoercion(PointerCast::ArrayToPointer)
|
||||
} else {
|
||||
let mut table = InferenceTable::new(
|
||||
self.db,
|
||||
self.db.trait_environment_for_body(self.owner),
|
||||
);
|
||||
let mut table = InferenceTable::new(self.db, self.env.clone());
|
||||
cast_kind(&mut table, &source_ty, &target_ty)?
|
||||
};
|
||||
|
||||
@ -1412,11 +1412,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
}
|
||||
|
||||
fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> {
|
||||
let size = || {
|
||||
self.db
|
||||
.layout_of_ty(ty.clone(), self.db.trait_environment_for_body(self.owner))
|
||||
.map(|it| it.size.bytes_usize())
|
||||
};
|
||||
let size =
|
||||
|| self.db.layout_of_ty(ty.clone(), self.env.clone()).map(|it| it.size.bytes_usize());
|
||||
const USIZE_SIZE: usize = size_of::<usize>();
|
||||
let bytes: Box<[_]> = match l {
|
||||
hir_def::hir::Literal::String(b) => {
|
||||
@ -1723,7 +1720,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
}
|
||||
|
||||
fn is_uninhabited(&self, expr_id: ExprId) -> bool {
|
||||
is_ty_uninhabited_from(self.db, &self.infer[expr_id], self.owner.module(self.db))
|
||||
is_ty_uninhabited_from(
|
||||
self.db,
|
||||
&self.infer[expr_id],
|
||||
self.owner.module(self.db),
|
||||
self.env.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
/// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and
|
||||
|
@ -106,4 +106,29 @@ fn test(x: Result<i32, &'static !>) {
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_patterns_normalize() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
enum Infallible {}
|
||||
|
||||
trait Foo {
|
||||
type Assoc;
|
||||
}
|
||||
enum Enum<T: Foo> {
|
||||
A,
|
||||
B(T::Assoc),
|
||||
}
|
||||
|
||||
impl Foo for () {
|
||||
type Assoc = Infallible;
|
||||
}
|
||||
|
||||
fn foo(v: Enum<()>) {
|
||||
let Enum::A = v;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user