Unnamed repository; edit this file 'description' to name the repository.
| -rw-r--r-- | crates/hir-def/src/hir/type_ref.rs | 14 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 40 | ||||
| -rw-r--r-- | crates/hir-ty/src/lib.rs | 29 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests.rs | 33 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/display_source_code.rs | 19 |
5 files changed, 128 insertions, 7 deletions
diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index da0f058a9c..ad8535413d 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs @@ -195,12 +195,16 @@ impl TypeRef { TypeRef::Tuple(ThinVec::new()) } - pub fn walk(this: TypeRefId, map: &ExpressionStore, f: &mut impl FnMut(&TypeRef)) { + pub fn walk(this: TypeRefId, map: &ExpressionStore, f: &mut impl FnMut(TypeRefId, &TypeRef)) { go(this, f, map); - fn go(type_ref: TypeRefId, f: &mut impl FnMut(&TypeRef), map: &ExpressionStore) { - let type_ref = &map[type_ref]; - f(type_ref); + fn go( + type_ref_id: TypeRefId, + f: &mut impl FnMut(TypeRefId, &TypeRef), + map: &ExpressionStore, + ) { + let type_ref = &map[type_ref_id]; + f(type_ref_id, type_ref); match type_ref { TypeRef::Fn(fn_) => { fn_.params.iter().for_each(|&(_, param_type)| go(param_type, f, map)) @@ -224,7 +228,7 @@ impl TypeRef { }; } - fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef), map: &ExpressionStore) { + fn go_path(path: &Path, f: &mut impl FnMut(TypeRefId, &TypeRef), map: &ExpressionStore) { if let Some(type_ref) = path.type_anchor() { go(type_ref, f, map); } diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 02b8ab8cdd..d507afecd3 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -41,7 +41,7 @@ use hir_def::{ layout::Integer, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, signatures::{ConstSignature, StaticSignature}, - type_ref::{ConstRef, LifetimeRefId, TypeRefId}, + type_ref::{ConstRef, LifetimeRefId, TypeRef, TypeRefId}, }; use hir_expand::{mod_path::ModPath, name::Name}; use indexmap::IndexSet; @@ -60,6 +60,7 @@ use triomphe::Arc; use crate::{ ImplTraitId, IncorrectGenericsLenKind, PathLoweringDiagnostic, TargetFeatures, + collect_type_inference_vars, db::{HirDatabase, InternedClosureId, InternedOpaqueTyId}, infer::{ coerce::{CoerceMany, DynamicCoerceMany}, @@ -497,6 +498,7 @@ pub struct InferenceResult<'db> { /// unresolved or missing subpatterns or subpatterns of mismatched types. pub(crate) type_of_pat: ArenaMap<PatId, Ty<'db>>, pub(crate) type_of_binding: ArenaMap<BindingId, Ty<'db>>, + pub(crate) type_of_type_placeholder: ArenaMap<TypeRefId, Ty<'db>>, pub(crate) type_of_opaque: FxHashMap<InternedOpaqueTyId, Ty<'db>>, pub(crate) type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch<'db>>, /// Whether there are any type-mismatching errors in the result. @@ -542,6 +544,7 @@ impl<'db> InferenceResult<'db> { type_of_expr: Default::default(), type_of_pat: Default::default(), type_of_binding: Default::default(), + type_of_type_placeholder: Default::default(), type_of_opaque: Default::default(), type_mismatches: Default::default(), has_errors: Default::default(), @@ -606,6 +609,9 @@ impl<'db> InferenceResult<'db> { _ => None, }) } + pub fn placeholder_types(&self) -> impl Iterator<Item = (TypeRefId, &Ty<'db>)> { + self.type_of_type_placeholder.iter() + } pub fn closure_info(&self, closure: InternedClosureId) -> &(Vec<CapturedItem<'db>>, FnTrait) { self.closure_info.get(&closure).unwrap() } @@ -1014,6 +1020,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { type_of_expr, type_of_pat, type_of_binding, + type_of_type_placeholder, type_of_opaque, type_mismatches, has_errors, @@ -1046,6 +1053,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { *has_errors = *has_errors || ty.references_non_lt_error(); } type_of_binding.shrink_to_fit(); + for ty in type_of_type_placeholder.values_mut() { + *ty = table.resolve_completely(*ty); + *has_errors = *has_errors || ty.references_non_lt_error(); + } + type_of_type_placeholder.shrink_to_fit(); type_of_opaque.shrink_to_fit(); *has_errors |= !type_mismatches.is_empty(); @@ -1285,6 +1297,10 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.result.type_of_pat.insert(pat, ty); } + fn write_type_placeholder_ty(&mut self, type_ref: TypeRefId, ty: Ty<'db>) { + self.result.type_of_type_placeholder.insert(type_ref, ty); + } + fn write_binding_ty(&mut self, id: BindingId, ty: Ty<'db>) { self.result.type_of_binding.insert(id, ty); } @@ -1333,7 +1349,27 @@ impl<'body, 'db> InferenceContext<'body, 'db> { ) -> Ty<'db> { let ty = self .with_ty_lowering(store, type_source, lifetime_elision, |ctx| ctx.lower_ty(type_ref)); - self.process_user_written_ty(ty) + let ty = self.process_user_written_ty(ty); + + // Record the association from placeholders' TypeRefId to type variables. + // We only record them if their number matches. This assumes TypeRef::walk and TypeVisitable process the items in the same order. + let type_variables = collect_type_inference_vars(&ty); + let mut placeholder_ids = vec![]; + TypeRef::walk(type_ref, store, &mut |type_ref_id, type_ref| { + if matches!(type_ref, TypeRef::Placeholder) { + placeholder_ids.push(type_ref_id); + } + }); + + if placeholder_ids.len() == type_variables.len() { + for (placeholder_id, type_variable) in + placeholder_ids.into_iter().zip(type_variables.into_iter()) + { + self.write_type_placeholder_ty(placeholder_id, type_variable); + } + } + + ty } pub(crate) fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> { diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index b29c7d252b..8819307c53 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -569,6 +569,35 @@ where Vec::from_iter(collector.params) } +struct TypeInferenceVarCollector<'db> { + type_inference_vars: Vec<Ty<'db>>, +} + +impl<'db> rustc_type_ir::TypeVisitor<DbInterner<'db>> for TypeInferenceVarCollector<'db> { + type Result = (); + + fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result { + use crate::rustc_type_ir::Flags; + if ty.is_ty_var() { + self.type_inference_vars.push(ty); + } else if ty.flags().intersects(rustc_type_ir::TypeFlags::HAS_TY_INFER) { + ty.super_visit_with(self); + } else { + // Fast path: don't visit inner types (e.g. generic arguments) when `flags` indicate + // that there are no placeholders. + } + } +} + +pub fn collect_type_inference_vars<'db, T>(value: &T) -> Vec<Ty<'db>> +where + T: ?Sized + rustc_type_ir::TypeVisitable<DbInterner<'db>>, +{ + let mut collector = TypeInferenceVarCollector { type_inference_vars: vec![] }; + value.visit_with(&mut collector); + collector.type_inference_vars +} + pub fn known_const_to_ast<'db>( konst: Const<'db>, db: &'db dyn HirDatabase, diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index 95a02d534b..002d58961d 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -23,6 +23,7 @@ use hir_def::{ item_scope::ItemScope, nameres::DefMap, src::HasSource, + type_ref::TypeRefId, }; use hir_expand::{FileRange, InFile, db::ExpandDatabase}; use itertools::Itertools; @@ -219,6 +220,24 @@ fn check_impl( } } } + + for (type_ref, ty) in inference_result.placeholder_types() { + let node = match type_node(&body_source_map, type_ref, &db) { + Some(value) => value, + None => continue, + }; + let range = node.as_ref().original_file_range_rooted(&db); + if let Some(expected) = types.remove(&range) { + let actual = salsa::attach(&db, || { + if display_source { + ty.display_source_code(&db, def.module(&db), true).unwrap() + } else { + ty.display_test(&db, display_target).to_string() + } + }); + assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); + } + } } let mut buf = String::new(); @@ -275,6 +294,20 @@ fn pat_node( }) } +fn type_node( + body_source_map: &BodySourceMap, + type_ref: TypeRefId, + db: &TestDB, +) -> Option<InFile<SyntaxNode>> { + Some(match body_source_map.type_syntax(type_ref) { + Ok(sp) => { + let root = db.parse_or_expand(sp.file_id); + sp.map(|ptr| ptr.to_node(&root).syntax().clone()) + } + Err(SyntheticSyntax) => return None, + }) +} + fn infer(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String { infer_with_mismatches(ra_fixture, false) } diff --git a/crates/hir-ty/src/tests/display_source_code.rs b/crates/hir-ty/src/tests/display_source_code.rs index a986b54a7b..dc3869930d 100644 --- a/crates/hir-ty/src/tests/display_source_code.rs +++ b/crates/hir-ty/src/tests/display_source_code.rs @@ -246,3 +246,22 @@ fn test() { "#, ); } + +#[test] +fn type_placeholder_type() { + check_types_source_code( + r#" +struct S<T>(T); +fn test() { + let f: S<_> = S(3); + //^ i32 + let f: [_; _] = [4_u32, 5, 6]; + //^ u32 + let f: (_, _, _) = (1_u32, 1_i32, false); + //^ u32 + //^ i32 + //^ bool +} +"#, + ); +} |