mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 03:54:42 +00:00 
			
		
		
		
	Merge pull request #19851 from ChayimFriedman2/normalize-exhaustiveness
fix: Normalize when checking for uninhabited types for pattern exhaustiveness checking
This commit is contained in:
		
						commit
						d146d3bc90
					
				
					 5 changed files with 81 additions and 25 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lukas Wirth
						Lukas Wirth