mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 12:04:43 +00:00 
			
		
		
		
	Normalize when checking for uninhabited types for pattern exhaustiveness checking
This commit is contained in:
		
							parent
							
								
									1511c5b7fd
								
							
						
					
					
						commit
						f17bbfc48b
					
				
					 5 changed files with 81 additions and 25 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Chayim Refael Friedman
						Chayim Refael Friedman