diff options
-rw-r--r-- | crates/hir_ty/src/infer.rs | 20 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/expr.rs | 6 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/path.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/unify.rs | 8 | ||||
-rw-r--r-- | crates/hir_ty/src/lower.rs | 69 | ||||
-rw-r--r-- | docs/dev/style.md | 45 |
6 files changed, 103 insertions, 47 deletions
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index e4407ff50..674e9e6f9 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -210,6 +210,7 @@ struct InferenceContext<'a> { | |||
210 | table: unify::InferenceTable, | 210 | table: unify::InferenceTable, |
211 | trait_env: Arc<TraitEnvironment>, | 211 | trait_env: Arc<TraitEnvironment>, |
212 | obligations: Vec<DomainGoal>, | 212 | obligations: Vec<DomainGoal>, |
213 | last_obligations_check: Option<u32>, | ||
213 | result: InferenceResult, | 214 | result: InferenceResult, |
214 | /// The return type of the function being inferred, or the closure if we're | 215 | /// The return type of the function being inferred, or the closure if we're |
215 | /// currently within one. | 216 | /// currently within one. |
@@ -245,6 +246,7 @@ impl<'a> InferenceContext<'a> { | |||
245 | result: InferenceResult::default(), | 246 | result: InferenceResult::default(), |
246 | table: unify::InferenceTable::new(), | 247 | table: unify::InferenceTable::new(), |
247 | obligations: Vec::default(), | 248 | obligations: Vec::default(), |
249 | last_obligations_check: None, | ||
248 | return_ty: TyKind::Unknown.intern(&Interner), // set in collect_fn_signature | 250 | return_ty: TyKind::Unknown.intern(&Interner), // set in collect_fn_signature |
249 | trait_env: owner | 251 | trait_env: owner |
250 | .as_generic_def_id() | 252 | .as_generic_def_id() |
@@ -334,6 +336,13 @@ impl<'a> InferenceContext<'a> { | |||
334 | } | 336 | } |
335 | 337 | ||
336 | fn resolve_obligations_as_possible(&mut self) { | 338 | fn resolve_obligations_as_possible(&mut self) { |
339 | if self.last_obligations_check == Some(self.table.revision) { | ||
340 | // no change | ||
341 | return; | ||
342 | } | ||
343 | let _span = profile::span("resolve_obligations_as_possible"); | ||
344 | |||
345 | self.last_obligations_check = Some(self.table.revision); | ||
337 | let obligations = mem::replace(&mut self.obligations, Vec::new()); | 346 | let obligations = mem::replace(&mut self.obligations, Vec::new()); |
338 | for obligation in obligations { | 347 | for obligation in obligations { |
339 | let in_env = InEnvironment::new(self.trait_env.env.clone(), obligation.clone()); | 348 | let in_env = InEnvironment::new(self.trait_env.env.clone(), obligation.clone()); |
@@ -360,6 +369,11 @@ impl<'a> InferenceContext<'a> { | |||
360 | } | 369 | } |
361 | } | 370 | } |
362 | 371 | ||
372 | fn push_obligation(&mut self, o: DomainGoal) { | ||
373 | self.obligations.push(o); | ||
374 | self.last_obligations_check = None; | ||
375 | } | ||
376 | |||
363 | fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { | 377 | fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { |
364 | self.table.unify(ty1, ty2) | 378 | self.table.unify(ty1, ty2) |
365 | } | 379 | } |
@@ -408,8 +422,8 @@ impl<'a> InferenceContext<'a> { | |||
408 | }), | 422 | }), |
409 | ty: ty.clone(), | 423 | ty: ty.clone(), |
410 | }; | 424 | }; |
411 | self.obligations.push(trait_ref.cast(&Interner)); | 425 | self.push_obligation(trait_ref.cast(&Interner)); |
412 | self.obligations.push(alias_eq.cast(&Interner)); | 426 | self.push_obligation(alias_eq.cast(&Interner)); |
413 | self.resolve_ty_as_possible(ty) | 427 | self.resolve_ty_as_possible(ty) |
414 | } | 428 | } |
415 | None => self.err_ty(), | 429 | None => self.err_ty(), |
@@ -436,7 +450,7 @@ impl<'a> InferenceContext<'a> { | |||
436 | let var = self.table.new_type_var(); | 450 | let var = self.table.new_type_var(); |
437 | let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; | 451 | let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; |
438 | let obligation = alias_eq.cast(&Interner); | 452 | let obligation = alias_eq.cast(&Interner); |
439 | self.obligations.push(obligation); | 453 | self.push_obligation(obligation); |
440 | var | 454 | var |
441 | } | 455 | } |
442 | 456 | ||
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 6279aa572..25ab3ea4c 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -99,7 +99,7 @@ impl<'a> InferenceContext<'a> { | |||
99 | environment: trait_env, | 99 | environment: trait_env, |
100 | }); | 100 | }); |
101 | if self.db.trait_solve(krate, goal.value).is_some() { | 101 | if self.db.trait_solve(krate, goal.value).is_some() { |
102 | self.obligations.push(implements_fn_trait); | 102 | self.push_obligation(implements_fn_trait); |
103 | let output_proj_ty = crate::ProjectionTy { | 103 | let output_proj_ty = crate::ProjectionTy { |
104 | associated_ty_id: to_assoc_type_id(output_assoc_type), | 104 | associated_ty_id: to_assoc_type_id(output_assoc_type), |
105 | substitution: substs, | 105 | substitution: substs, |
@@ -964,7 +964,7 @@ impl<'a> InferenceContext<'a> { | |||
964 | let (predicate, binders) = | 964 | let (predicate, binders) = |
965 | predicate.clone().subst(parameters).into_value_and_skipped_binders(); | 965 | predicate.clone().subst(parameters).into_value_and_skipped_binders(); |
966 | always!(binders == 0); // quantified where clauses not yet handled | 966 | always!(binders == 0); // quantified where clauses not yet handled |
967 | self.obligations.push(predicate.cast(&Interner)); | 967 | self.push_obligation(predicate.cast(&Interner)); |
968 | } | 968 | } |
969 | // add obligation for trait implementation, if this is a trait method | 969 | // add obligation for trait implementation, if this is a trait method |
970 | match def { | 970 | match def { |
@@ -974,7 +974,7 @@ impl<'a> InferenceContext<'a> { | |||
974 | // construct a TraitRef | 974 | // construct a TraitRef |
975 | let substs = | 975 | let substs = |
976 | parameters.prefix(generics(self.db.upcast(), trait_.into()).len()); | 976 | parameters.prefix(generics(self.db.upcast(), trait_.into()).len()); |
977 | self.obligations.push( | 977 | self.push_obligation( |
978 | TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs } | 978 | TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs } |
979 | .cast(&Interner), | 979 | .cast(&Interner), |
980 | ); | 980 | ); |
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index cefa38509..717738789 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs | |||
@@ -258,7 +258,7 @@ impl<'a> InferenceContext<'a> { | |||
258 | .push(ty.clone()) | 258 | .push(ty.clone()) |
259 | .fill(std::iter::repeat_with(|| self.table.new_type_var())) | 259 | .fill(std::iter::repeat_with(|| self.table.new_type_var())) |
260 | .build(); | 260 | .build(); |
261 | self.obligations.push( | 261 | self.push_obligation( |
262 | TraitRef { | 262 | TraitRef { |
263 | trait_id: to_chalk_trait_id(trait_), | 263 | trait_id: to_chalk_trait_id(trait_), |
264 | substitution: trait_substs.clone(), | 264 | substitution: trait_substs.clone(), |
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 6e7b0f5a6..5ea4b7481 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs | |||
@@ -231,6 +231,7 @@ pub(crate) struct TypeVariableData { | |||
231 | pub(crate) struct InferenceTable { | 231 | pub(crate) struct InferenceTable { |
232 | pub(super) var_unification_table: InPlaceUnificationTable<TypeVarId>, | 232 | pub(super) var_unification_table: InPlaceUnificationTable<TypeVarId>, |
233 | pub(super) type_variable_table: TypeVariableTable, | 233 | pub(super) type_variable_table: TypeVariableTable, |
234 | pub(super) revision: u32, | ||
234 | } | 235 | } |
235 | 236 | ||
236 | impl InferenceTable { | 237 | impl InferenceTable { |
@@ -238,6 +239,7 @@ impl InferenceTable { | |||
238 | InferenceTable { | 239 | InferenceTable { |
239 | var_unification_table: InPlaceUnificationTable::new(), | 240 | var_unification_table: InPlaceUnificationTable::new(), |
240 | type_variable_table: TypeVariableTable { inner: Vec::new() }, | 241 | type_variable_table: TypeVariableTable { inner: Vec::new() }, |
242 | revision: 0, | ||
241 | } | 243 | } |
242 | } | 244 | } |
243 | 245 | ||
@@ -360,7 +362,10 @@ impl InferenceTable { | |||
360 | == self.type_variable_table.is_diverging(*tv2) => | 362 | == self.type_variable_table.is_diverging(*tv2) => |
361 | { | 363 | { |
362 | // both type vars are unknown since we tried to resolve them | 364 | // both type vars are unknown since we tried to resolve them |
363 | self.var_unification_table.union(tv1.to_inner(), tv2.to_inner()); | 365 | if !self.var_unification_table.unioned(tv1.to_inner(), tv2.to_inner()) { |
366 | self.var_unification_table.union(tv1.to_inner(), tv2.to_inner()); | ||
367 | self.revision += 1; | ||
368 | } | ||
364 | true | 369 | true |
365 | } | 370 | } |
366 | 371 | ||
@@ -398,6 +403,7 @@ impl InferenceTable { | |||
398 | tv.to_inner(), | 403 | tv.to_inner(), |
399 | TypeVarValue::Known(other.clone().intern(&Interner)), | 404 | TypeVarValue::Known(other.clone().intern(&Interner)), |
400 | ); | 405 | ); |
406 | self.revision += 1; | ||
401 | true | 407 | true |
402 | } | 408 | } |
403 | 409 | ||
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index 14f34d73c..018621131 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -821,24 +821,38 @@ pub fn associated_type_shorthand_candidates<R>( | |||
821 | res: TypeNs, | 821 | res: TypeNs, |
822 | mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>, | 822 | mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>, |
823 | ) -> Option<R> { | 823 | ) -> Option<R> { |
824 | let traits_from_env: Vec<_> = match res { | 824 | let mut search = |t| { |
825 | TypeNs::SelfType(impl_id) => match db.impl_trait(impl_id) { | 825 | for t in all_super_trait_refs(db, t) { |
826 | None => vec![], | 826 | let data = db.trait_data(t.hir_trait_id()); |
827 | // FIXME: how to correctly handle higher-ranked bounds here? | 827 | |
828 | Some(trait_ref) => vec![trait_ref.value.shift_bound_vars_out(DebruijnIndex::ONE)], | 828 | for (name, assoc_id) in &data.items { |
829 | }, | 829 | if let AssocItemId::TypeAliasId(alias) = assoc_id { |
830 | if let Some(result) = cb(name, &t, *alias) { | ||
831 | return Some(result); | ||
832 | } | ||
833 | } | ||
834 | } | ||
835 | } | ||
836 | None | ||
837 | }; | ||
838 | |||
839 | match res { | ||
840 | // FIXME: how to correctly handle higher-ranked bounds here? | ||
841 | TypeNs::SelfType(impl_id) => { | ||
842 | search(db.impl_trait(impl_id)?.value.shift_bound_vars_out(DebruijnIndex::ONE)) | ||
843 | } | ||
830 | TypeNs::GenericParam(param_id) => { | 844 | TypeNs::GenericParam(param_id) => { |
831 | let predicates = db.generic_predicates_for_param(param_id); | 845 | let predicates = db.generic_predicates_for_param(param_id); |
832 | let mut traits_: Vec<_> = predicates | 846 | let res = predicates.iter().find_map(|pred| match &pred.value.value { |
833 | .iter() | 847 | // FIXME: how to correctly handle higher-ranked bounds here? |
834 | .filter_map(|pred| match &pred.value.value { | 848 | WhereClause::Implemented(tr) => { |
835 | // FIXME: how to correctly handle higher-ranked bounds here? | 849 | search(tr.clone().shift_bound_vars_out(DebruijnIndex::ONE)) |
836 | WhereClause::Implemented(tr) => { | 850 | } |
837 | Some(tr.clone().shift_bound_vars_out(DebruijnIndex::ONE)) | 851 | _ => None, |
838 | } | 852 | }); |
839 | _ => None, | 853 | if let res @ Some(_) = res { |
840 | }) | 854 | return res; |
841 | .collect(); | 855 | } |
842 | // Handle `Self::Type` referring to own associated type in trait definitions | 856 | // Handle `Self::Type` referring to own associated type in trait definitions |
843 | if let GenericDefId::TraitId(trait_id) = param_id.parent { | 857 | if let GenericDefId::TraitId(trait_id) = param_id.parent { |
844 | let generics = generics(db.upcast(), trait_id.into()); | 858 | let generics = generics(db.upcast(), trait_id.into()); |
@@ -849,30 +863,13 @@ pub fn associated_type_shorthand_candidates<R>( | |||
849 | trait_id: to_chalk_trait_id(trait_id), | 863 | trait_id: to_chalk_trait_id(trait_id), |
850 | substitution: Substitution::bound_vars(&generics, DebruijnIndex::INNERMOST), | 864 | substitution: Substitution::bound_vars(&generics, DebruijnIndex::INNERMOST), |
851 | }; | 865 | }; |
852 | traits_.push(trait_ref); | 866 | return search(trait_ref); |
853 | } | 867 | } |
854 | } | 868 | } |
855 | traits_ | 869 | None |
856 | } | ||
857 | _ => vec![], | ||
858 | }; | ||
859 | |||
860 | for t in traits_from_env.into_iter().flat_map(move |t| all_super_trait_refs(db, t)) { | ||
861 | let data = db.trait_data(t.hir_trait_id()); | ||
862 | |||
863 | for (name, assoc_id) in &data.items { | ||
864 | match assoc_id { | ||
865 | AssocItemId::TypeAliasId(alias) => { | ||
866 | if let Some(result) = cb(name, &t, *alias) { | ||
867 | return Some(result); | ||
868 | } | ||
869 | } | ||
870 | AssocItemId::FunctionId(_) | AssocItemId::ConstId(_) => {} | ||
871 | } | ||
872 | } | 870 | } |
871 | _ => None, | ||
873 | } | 872 | } |
874 | |||
875 | None | ||
876 | } | 873 | } |
877 | 874 | ||
878 | /// Build the type of all specific fields of a struct or enum variant. | 875 | /// Build the type of all specific fields of a struct or enum variant. |
diff --git a/docs/dev/style.md b/docs/dev/style.md index e4a1672ca..c594946be 100644 --- a/docs/dev/style.md +++ b/docs/dev/style.md | |||
@@ -55,9 +55,9 @@ https://www.tedinski.com/2018/02/06/system-boundaries.html | |||
55 | We try to be very conservative with usage of crates.io dependencies. | 55 | We try to be very conservative with usage of crates.io dependencies. |
56 | Don't use small "helper" crates (exception: `itertools` is allowed). | 56 | Don't use small "helper" crates (exception: `itertools` is allowed). |
57 | If there's some general reusable bit of code you need, consider adding it to the `stdx` crate. | 57 | If there's some general reusable bit of code you need, consider adding it to the `stdx` crate. |
58 | A useful exercise is to read Cargo.lock and see if some of the *transitive* dependencies do not make sense for rust-analyzer. | ||
58 | 59 | ||
59 | **Rationale:** keep compile times low, create ecosystem pressure for faster | 60 | **Rationale:** keep compile times low, create ecosystem pressure for faster compiles, reduce the number of things which might break. |
60 | compiles, reduce the number of things which might break. | ||
61 | 61 | ||
62 | ## Commit Style | 62 | ## Commit Style |
63 | 63 | ||
@@ -806,9 +806,48 @@ if let Some(expected_type) = ctx.expected_type.as_ref() { | |||
806 | } | 806 | } |
807 | ``` | 807 | ``` |
808 | 808 | ||
809 | **Rational:** `match` is almost always more compact. | 809 | **Rationale:** `match` is almost always more compact. |
810 | The `else` branch can get a more precise pattern: `None` or `Err(_)` instead of `_`. | 810 | The `else` branch can get a more precise pattern: `None` or `Err(_)` instead of `_`. |
811 | 811 | ||
812 | ## Helper Functions | ||
813 | |||
814 | Avoid creating singe-use helper functions: | ||
815 | |||
816 | ```rust | ||
817 | // GOOD | ||
818 | let buf = { | ||
819 | let mut buf = get_empty_buf(&mut arena); | ||
820 | buf.add_item(item); | ||
821 | buf | ||
822 | }; | ||
823 | |||
824 | // BAD | ||
825 | |||
826 | let buf = prepare_buf(&mut arena, item); | ||
827 | |||
828 | ... | ||
829 | |||
830 | fn prepare_buf(arena: &mut Arena, item: Item) -> ItemBuf { | ||
831 | let mut res = get_empty_buf(&mut arena); | ||
832 | res.add_item(item); | ||
833 | res | ||
834 | } | ||
835 | ``` | ||
836 | |||
837 | Exception: if you want to make use of `return` or `?`. | ||
838 | |||
839 | **Rationale:** single-use functions change frequently, adding or removing parameters adds churn. | ||
840 | A block serves just as well to delineate a bit of logic, but has access to all the context. | ||
841 | Re-using originally single-purpose function often leads to bad coupling. | ||
842 | |||
843 | ## Helper Variables | ||
844 | |||
845 | Introduce helper variables freely, especially for multiline conditions. | ||
846 | |||
847 | **Rationale:** like blocks, single-use variables are a cognitively cheap abstraction, as they have access to all the context. | ||
848 | Extra variables help during debugging, they make it easy to print/view important intermediate results. | ||
849 | Giving a name to a condition in `if` expression often improves clarity and leads to a nicer formatted code. | ||
850 | |||
812 | ## Token names | 851 | ## Token names |
813 | 852 | ||
814 | Use `T![foo]` instead of `SyntaxKind::FOO_KW`. | 853 | Use `T![foo]` instead of `SyntaxKind::FOO_KW`. |