mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +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 typed_arena::Arena;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Adjust, InferenceResult, Interner, Ty, TyExt, TyKind,
|
Adjust, InferenceResult, Interner, TraitEnvironment, Ty, TyExt, TyKind,
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
diagnostics::match_check::{
|
diagnostics::match_check::{
|
||||||
self,
|
self,
|
||||||
@ -74,8 +74,9 @@ impl BodyValidationDiagnostic {
|
|||||||
let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered();
|
let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered();
|
||||||
let infer = db.infer(owner);
|
let infer = db.infer(owner);
|
||||||
let body = db.body(owner);
|
let body = db.body(owner);
|
||||||
|
let env = db.trait_environment_for_body(owner);
|
||||||
let mut validator =
|
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.validate_body(db);
|
||||||
validator.diagnostics
|
validator.diagnostics
|
||||||
}
|
}
|
||||||
@ -85,6 +86,7 @@ struct ExprValidator {
|
|||||||
owner: DefWithBodyId,
|
owner: DefWithBodyId,
|
||||||
body: Arc<Body>,
|
body: Arc<Body>,
|
||||||
infer: Arc<InferenceResult>,
|
infer: Arc<InferenceResult>,
|
||||||
|
env: Arc<TraitEnvironment>,
|
||||||
diagnostics: Vec<BodyValidationDiagnostic>,
|
diagnostics: Vec<BodyValidationDiagnostic>,
|
||||||
validate_lints: bool,
|
validate_lints: bool,
|
||||||
}
|
}
|
||||||
@ -190,7 +192,7 @@ impl ExprValidator {
|
|||||||
return;
|
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 pattern_arena = Arena::new();
|
||||||
let mut m_arms = Vec::with_capacity(arms.len());
|
let mut m_arms = Vec::with_capacity(arms.len());
|
||||||
@ -317,7 +319,7 @@ impl ExprValidator {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let pattern_arena = Arena::new();
|
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 {
|
for stmt in &**statements {
|
||||||
let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else {
|
let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else {
|
||||||
continue;
|
continue;
|
||||||
|
@ -12,9 +12,10 @@ use rustc_pattern_analysis::{
|
|||||||
};
|
};
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AdtId, Interner, Scalar, Ty, TyExt, TyKind,
|
AdtId, Interner, Scalar, TraitEnvironment, Ty, TyExt, TyKind,
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
infer::normalize,
|
infer::normalize,
|
||||||
inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from},
|
inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from},
|
||||||
@ -69,13 +70,19 @@ pub(crate) struct MatchCheckCtx<'db> {
|
|||||||
body: DefWithBodyId,
|
body: DefWithBodyId,
|
||||||
pub(crate) db: &'db dyn HirDatabase,
|
pub(crate) db: &'db dyn HirDatabase,
|
||||||
exhaustive_patterns: bool,
|
exhaustive_patterns: bool,
|
||||||
|
env: Arc<TraitEnvironment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> MatchCheckCtx<'db> {
|
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 def_map = module.crate_def_map(db);
|
||||||
let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
|
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(
|
pub(crate) fn compute_match_usefulness(
|
||||||
@ -100,7 +107,7 @@ impl<'db> MatchCheckCtx<'db> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_uninhabited(&self, ty: &Ty) -> bool {
|
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]`.
|
/// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`.
|
||||||
@ -459,8 +466,13 @@ impl PatCx for MatchCheckCtx<'_> {
|
|||||||
} else {
|
} else {
|
||||||
let mut variants = IndexVec::with_capacity(enum_data.variants.len());
|
let mut variants = IndexVec::with_capacity(enum_data.variants.len());
|
||||||
for &(variant, _) in enum_data.variants.iter() {
|
for &(variant, _) in enum_data.variants.iter() {
|
||||||
let is_uninhabited =
|
let is_uninhabited = is_enum_variant_uninhabited_from(
|
||||||
is_enum_variant_uninhabited_from(cx.db, variant, subst, cx.module);
|
cx.db,
|
||||||
|
variant,
|
||||||
|
subst,
|
||||||
|
cx.module,
|
||||||
|
self.env.clone(),
|
||||||
|
);
|
||||||
let visibility = if is_uninhabited {
|
let visibility = if is_uninhabited {
|
||||||
VariantVisibility::Empty
|
VariantVisibility::Empty
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,17 +7,24 @@ use chalk_ir::{
|
|||||||
};
|
};
|
||||||
use hir_def::{AdtId, EnumVariantId, ModuleId, VariantId, visibility::Visibility};
|
use hir_def::{AdtId, EnumVariantId, ModuleId, VariantId, visibility::Visibility};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
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
|
// FIXME: Turn this into a query, it can be quite slow
|
||||||
/// Checks whether a type is visibly uninhabited from a particular module.
|
/// 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 _p = tracing::info_span!("is_ty_uninhabited_from", ?ty).entered();
|
||||||
let mut uninhabited_from =
|
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);
|
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
|
||||||
inhabitedness == BREAK_VISIBLY_UNINHABITED
|
inhabitedness == BREAK_VISIBLY_UNINHABITED
|
||||||
}
|
}
|
||||||
@ -29,11 +36,12 @@ pub(crate) fn is_enum_variant_uninhabited_from(
|
|||||||
variant: EnumVariantId,
|
variant: EnumVariantId,
|
||||||
subst: &Substitution,
|
subst: &Substitution,
|
||||||
target_mod: ModuleId,
|
target_mod: ModuleId,
|
||||||
|
env: Arc<TraitEnvironment>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let _p = tracing::info_span!("is_enum_variant_uninhabited_from").entered();
|
let _p = tracing::info_span!("is_enum_variant_uninhabited_from").entered();
|
||||||
|
|
||||||
let mut uninhabited_from =
|
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);
|
let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst);
|
||||||
inhabitedness == BREAK_VISIBLY_UNINHABITED
|
inhabitedness == BREAK_VISIBLY_UNINHABITED
|
||||||
}
|
}
|
||||||
@ -44,6 +52,7 @@ struct UninhabitedFrom<'a> {
|
|||||||
// guard for preventing stack overflow in non trivial non terminating types
|
// guard for preventing stack overflow in non trivial non terminating types
|
||||||
max_depth: usize,
|
max_depth: usize,
|
||||||
db: &'a dyn HirDatabase,
|
db: &'a dyn HirDatabase,
|
||||||
|
env: Arc<TraitEnvironment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONTINUE_OPAQUELY_INHABITED: ControlFlow<VisiblyUninhabited> = Continue(());
|
const CONTINUE_OPAQUELY_INHABITED: ControlFlow<VisiblyUninhabited> = Continue(());
|
||||||
@ -78,6 +87,12 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
|
|||||||
Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
|
Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
|
||||||
Some(1..) => item_ty.super_visit_with(self, outer_binder),
|
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,
|
_ => CONTINUE_OPAQUELY_INHABITED,
|
||||||
};
|
};
|
||||||
self.recursive_ty.remove(ty);
|
self.recursive_ty.remove(ty);
|
||||||
|
@ -25,7 +25,7 @@ use syntax::TextRange;
|
|||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
|
Adjust, Adjustment, AutoBorrow, CallableDefId, TraitEnvironment, TyBuilder, TyExt,
|
||||||
consteval::ConstEvalError,
|
consteval::ConstEvalError,
|
||||||
db::{HirDatabase, InternedClosure, InternedClosureId},
|
db::{HirDatabase, InternedClosure, InternedClosureId},
|
||||||
display::{DisplayTarget, HirDisplay, hir_display_with_store},
|
display::{DisplayTarget, HirDisplay, hir_display_with_store},
|
||||||
@ -79,6 +79,7 @@ struct MirLowerCtx<'db> {
|
|||||||
infer: &'db InferenceResult,
|
infer: &'db InferenceResult,
|
||||||
resolver: Resolver<'db>,
|
resolver: Resolver<'db>,
|
||||||
drop_scopes: Vec<DropScope>,
|
drop_scopes: Vec<DropScope>,
|
||||||
|
env: Arc<TraitEnvironment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Make this smaller, its stored in database queries
|
// FIXME: Make this smaller, its stored in database queries
|
||||||
@ -288,6 +289,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
closures: vec![],
|
closures: vec![],
|
||||||
};
|
};
|
||||||
let resolver = owner.resolver(db);
|
let resolver = owner.resolver(db);
|
||||||
|
let env = db.trait_environment_for_body(owner);
|
||||||
|
|
||||||
MirLowerCtx {
|
MirLowerCtx {
|
||||||
result: mir,
|
result: mir,
|
||||||
@ -300,6 +302,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
labeled_loop_blocks: Default::default(),
|
labeled_loop_blocks: Default::default(),
|
||||||
discr_temp: None,
|
discr_temp: None,
|
||||||
drop_scopes: vec![DropScope::default()],
|
drop_scopes: vec![DropScope::default()],
|
||||||
|
env,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -944,10 +947,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
let cast_kind = if source_ty.as_reference().is_some() {
|
let cast_kind = if source_ty.as_reference().is_some() {
|
||||||
CastKind::PointerCoercion(PointerCast::ArrayToPointer)
|
CastKind::PointerCoercion(PointerCast::ArrayToPointer)
|
||||||
} else {
|
} else {
|
||||||
let mut table = InferenceTable::new(
|
let mut table = InferenceTable::new(self.db, self.env.clone());
|
||||||
self.db,
|
|
||||||
self.db.trait_environment_for_body(self.owner),
|
|
||||||
);
|
|
||||||
cast_kind(&mut table, &source_ty, &target_ty)?
|
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> {
|
fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> {
|
||||||
let size = || {
|
let size =
|
||||||
self.db
|
|| self.db.layout_of_ty(ty.clone(), self.env.clone()).map(|it| it.size.bytes_usize());
|
||||||
.layout_of_ty(ty.clone(), self.db.trait_environment_for_body(self.owner))
|
|
||||||
.map(|it| it.size.bytes_usize())
|
|
||||||
};
|
|
||||||
const USIZE_SIZE: usize = size_of::<usize>();
|
const USIZE_SIZE: usize = size_of::<usize>();
|
||||||
let bytes: Box<[_]> = match l {
|
let bytes: Box<[_]> = match l {
|
||||||
hir_def::hir::Literal::String(b) => {
|
hir_def::hir::Literal::String(b) => {
|
||||||
@ -1723,7 +1720,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_uninhabited(&self, expr_id: ExprId) -> bool {
|
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
|
/// 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