Make type walking infrastructure a bit nicer

If/when we switch to using Chalk's Ty, we'll need to replace this by its `Fold`
trait, but I didn't want to import the whole thing just yet.
This commit is contained in:
Florian Diebold 2019-09-03 13:10:00 +02:00
parent c4fcfa2b0d
commit b8c1e402fa
10 changed files with 146 additions and 147 deletions

View file

@ -1,7 +1,7 @@
use std::{collections::HashSet, fmt::Write, path::Path, time::Instant}; use std::{collections::HashSet, fmt::Write, path::Path, time::Instant};
use ra_db::SourceDatabase; use ra_db::SourceDatabase;
use ra_hir::{Crate, HasBodySource, HasSource, HirDisplay, ImplItem, ModuleDef, Ty}; use ra_hir::{Crate, HasBodySource, HasSource, HirDisplay, ImplItem, ModuleDef, Ty, TypeWalk};
use ra_syntax::AstNode; use ra_syntax::AstNode;
use crate::Result; use crate::Result;

View file

@ -69,7 +69,9 @@ pub use self::{
resolve::Resolution, resolve::Resolution,
source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer},
source_id::{AstIdMap, ErasedFileAstId}, source_id::{AstIdMap, ErasedFileAstId},
ty::{display::HirDisplay, ApplicationTy, CallableDef, Substs, TraitRef, Ty, TypeCtor}, ty::{
display::HirDisplay, ApplicationTy, CallableDef, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
},
type_ref::Mutability, type_ref::Mutability,
}; };

View file

@ -130,12 +130,14 @@ impl ProjectionTy {
substs: self.parameters.clone(), substs: self.parameters.clone(),
} }
} }
}
pub fn walk(&self, f: &mut impl FnMut(&Ty)) { impl TypeWalk for ProjectionTy {
fn walk(&self, f: &mut impl FnMut(&Ty)) {
self.parameters.walk(f); self.parameters.walk(f);
} }
pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
self.parameters.walk_mut(f); self.parameters.walk_mut(f);
} }
} }
@ -146,12 +148,12 @@ pub struct UnselectedProjectionTy {
pub parameters: Substs, pub parameters: Substs,
} }
impl UnselectedProjectionTy { impl TypeWalk for UnselectedProjectionTy {
pub fn walk(&self, f: &mut impl FnMut(&Ty)) { fn walk(&self, f: &mut impl FnMut(&Ty)) {
self.parameters.walk(f); self.parameters.walk(f);
} }
pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
self.parameters.walk_mut(f); self.parameters.walk_mut(f);
} }
} }
@ -312,20 +314,14 @@ impl TraitRef {
pub fn self_ty(&self) -> &Ty { pub fn self_ty(&self) -> &Ty {
&self.substs[0] &self.substs[0]
} }
}
pub fn subst(mut self, substs: &Substs) -> TraitRef { impl TypeWalk for TraitRef {
self.substs.walk_mut(&mut |ty_mut| { fn walk(&self, f: &mut impl FnMut(&Ty)) {
let ty = mem::replace(ty_mut, Ty::Unknown);
*ty_mut = ty.subst(substs);
});
self
}
pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
self.substs.walk(f); self.substs.walk(f);
} }
pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
self.substs.walk_mut(f); self.substs.walk_mut(f);
} }
} }
@ -365,20 +361,10 @@ impl GenericPredicate {
GenericPredicate::Error => None, GenericPredicate::Error => None,
} }
} }
}
pub fn subst(self, substs: &Substs) -> GenericPredicate { impl TypeWalk for GenericPredicate {
match self { fn walk(&self, f: &mut impl FnMut(&Ty)) {
GenericPredicate::Implemented(trait_ref) => {
GenericPredicate::Implemented(trait_ref.subst(substs))
}
GenericPredicate::Projection(projection_predicate) => {
GenericPredicate::Projection(projection_predicate.subst(substs))
}
GenericPredicate::Error => self,
}
}
pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
match self { match self {
GenericPredicate::Implemented(trait_ref) => trait_ref.walk(f), GenericPredicate::Implemented(trait_ref) => trait_ref.walk(f),
GenericPredicate::Projection(projection_pred) => projection_pred.walk(f), GenericPredicate::Projection(projection_pred) => projection_pred.walk(f),
@ -386,7 +372,7 @@ impl GenericPredicate {
} }
} }
pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
match self { match self {
GenericPredicate::Implemented(trait_ref) => trait_ref.walk_mut(f), GenericPredicate::Implemented(trait_ref) => trait_ref.walk_mut(f),
GenericPredicate::Projection(projection_pred) => projection_pred.walk_mut(f), GenericPredicate::Projection(projection_pred) => projection_pred.walk_mut(f),
@ -430,16 +416,16 @@ impl FnSig {
pub fn ret(&self) -> &Ty { pub fn ret(&self) -> &Ty {
&self.params_and_return[self.params_and_return.len() - 1] &self.params_and_return[self.params_and_return.len() - 1]
} }
}
/// Applies the given substitutions to all types in this signature and impl TypeWalk for FnSig {
/// returns the result. fn walk(&self, f: &mut impl FnMut(&Ty)) {
pub fn subst(&self, substs: &Substs) -> FnSig { for t in self.params_and_return.iter() {
let result: Vec<_> = t.walk(f);
self.params_and_return.iter().map(|ty| ty.clone().subst(substs)).collect(); }
FnSig { params_and_return: result.into() }
} }
pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
// Without an Arc::make_mut_slice, we can't avoid the clone here: // Without an Arc::make_mut_slice, we can't avoid the clone here:
let mut v: Vec<_> = self.params_and_return.iter().cloned().collect(); let mut v: Vec<_> = self.params_and_return.iter().cloned().collect();
for t in &mut v { for t in &mut v {
@ -463,64 +449,6 @@ impl Ty {
Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty()) Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty())
} }
pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
match self {
Ty::Apply(a_ty) => {
for t in a_ty.parameters.iter() {
t.walk(f);
}
}
Ty::Projection(p_ty) => {
for t in p_ty.parameters.iter() {
t.walk(f);
}
}
Ty::UnselectedProjection(p_ty) => {
for t in p_ty.parameters.iter() {
t.walk(f);
}
}
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
for p in predicates.iter() {
p.walk(f);
}
}
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
}
f(self);
}
fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
match self {
Ty::Apply(a_ty) => {
a_ty.parameters.walk_mut(f);
}
Ty::Projection(p_ty) => {
p_ty.parameters.walk_mut(f);
}
Ty::UnselectedProjection(p_ty) => {
p_ty.parameters.walk_mut(f);
}
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
let mut v: Vec<_> = predicates.iter().cloned().collect();
for p in &mut v {
p.walk_mut(f);
}
*predicates = v.into();
}
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
}
f(self);
}
fn fold(mut self, f: &mut impl FnMut(Ty) -> Ty) -> Ty {
self.walk_mut(&mut |ty_mut| {
let ty = mem::replace(ty_mut, Ty::Unknown);
*ty_mut = f(ty);
});
self
}
pub fn as_reference(&self) -> Option<(&Ty, Mutability)> { pub fn as_reference(&self) -> Option<(&Ty, Mutability)> {
match self { match self {
Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(mutability), parameters }) => { Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(mutability), parameters }) => {
@ -596,26 +524,6 @@ impl Ty {
} }
} }
/// Replaces type parameters in this type using the given `Substs`. (So e.g.
/// if `self` is `&[T]`, where type parameter T has index 0, and the
/// `Substs` contain `u32` at index 0, we'll have `&[u32]` afterwards.)
pub fn subst(self, substs: &Substs) -> Ty {
self.fold(&mut |ty| match ty {
Ty::Param { idx, name } => {
substs.get(idx as usize).cloned().unwrap_or(Ty::Param { idx, name })
}
ty => ty,
})
}
/// Substitutes `Ty::Bound` vars (as opposed to type parameters).
pub fn subst_bound_vars(self, substs: &Substs) -> Ty {
self.fold(&mut |ty| match ty {
Ty::Bound(idx) => substs.get(idx as usize).cloned().unwrap_or_else(|| Ty::Bound(idx)),
ty => ty,
})
}
/// Returns the type parameters of this type if it has some (i.e. is an ADT /// Returns the type parameters of this type if it has some (i.e. is an ADT
/// or function); so if `self` is `Option<u32>`, this returns the `u32`. /// or function); so if `self` is `Option<u32>`, this returns the `u32`.
pub fn substs(&self) -> Option<Substs> { pub fn substs(&self) -> Option<Substs> {
@ -625,17 +533,6 @@ impl Ty {
} }
} }
/// Shifts up `Ty::Bound` vars by `n`.
pub fn shift_bound_vars(self, n: i32) -> Ty {
self.fold(&mut |ty| match ty {
Ty::Bound(idx) => {
assert!(idx as i32 >= -n);
Ty::Bound((idx as i32 + n) as u32)
}
ty => ty,
})
}
/// If this is an `impl Trait` or `dyn Trait`, returns that trait. /// If this is an `impl Trait` or `dyn Trait`, returns that trait.
pub fn inherent_trait(&self) -> Option<Trait> { pub fn inherent_trait(&self) -> Option<Trait> {
match self { match self {
@ -650,6 +547,116 @@ impl Ty {
} }
} }
/// This allows walking structures that contain types to do something with those
/// types, similar to Chalk's `Fold` trait.
pub trait TypeWalk {
fn walk(&self, f: &mut impl FnMut(&Ty));
fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty));
fn fold(mut self, f: &mut impl FnMut(Ty) -> Ty) -> Self
where
Self: Sized,
{
self.walk_mut(&mut |ty_mut| {
let ty = mem::replace(ty_mut, Ty::Unknown);
*ty_mut = f(ty);
});
self
}
/// Replaces type parameters in this type using the given `Substs`. (So e.g.
/// if `self` is `&[T]`, where type parameter T has index 0, and the
/// `Substs` contain `u32` at index 0, we'll have `&[u32]` afterwards.)
fn subst(self, substs: &Substs) -> Self
where
Self: Sized,
{
self.fold(&mut |ty| match ty {
Ty::Param { idx, name } => {
substs.get(idx as usize).cloned().unwrap_or(Ty::Param { idx, name })
}
ty => ty,
})
}
/// Substitutes `Ty::Bound` vars (as opposed to type parameters).
fn subst_bound_vars(self, substs: &Substs) -> Self
where
Self: Sized,
{
self.fold(&mut |ty| match ty {
Ty::Bound(idx) => substs.get(idx as usize).cloned().unwrap_or_else(|| Ty::Bound(idx)),
ty => ty,
})
}
/// Shifts up `Ty::Bound` vars by `n`.
fn shift_bound_vars(self, n: i32) -> Self
where
Self: Sized,
{
self.fold(&mut |ty| match ty {
Ty::Bound(idx) => {
assert!(idx as i32 >= -n);
Ty::Bound((idx as i32 + n) as u32)
}
ty => ty,
})
}
}
impl TypeWalk for Ty {
fn walk(&self, f: &mut impl FnMut(&Ty)) {
match self {
Ty::Apply(a_ty) => {
for t in a_ty.parameters.iter() {
t.walk(f);
}
}
Ty::Projection(p_ty) => {
for t in p_ty.parameters.iter() {
t.walk(f);
}
}
Ty::UnselectedProjection(p_ty) => {
for t in p_ty.parameters.iter() {
t.walk(f);
}
}
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
for p in predicates.iter() {
p.walk(f);
}
}
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
}
f(self);
}
fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
match self {
Ty::Apply(a_ty) => {
a_ty.parameters.walk_mut(f);
}
Ty::Projection(p_ty) => {
p_ty.parameters.walk_mut(f);
}
Ty::UnselectedProjection(p_ty) => {
p_ty.parameters.walk_mut(f);
}
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
let mut v: Vec<_> = predicates.iter().cloned().collect();
for p in &mut v {
p.walk_mut(f);
}
*predicates = v.into();
}
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
}
f(self);
}
}
impl HirDisplay for &Ty { impl HirDisplay for &Ty {
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
HirDisplay::hir_fmt(*self, f) HirDisplay::hir_fmt(*self, f)

View file

@ -7,7 +7,7 @@ use std::iter::successors;
use log::{info, warn}; use log::{info, warn};
use super::{traits::Solution, Canonical, Ty}; use super::{traits::Solution, Canonical, Ty, TypeWalk};
use crate::{HasGenericParams, HirDatabase, Name, Resolver}; use crate::{HasGenericParams, HirDatabase, Name, Resolver};
const AUTODEREF_RECURSION_LIMIT: usize = 10; const AUTODEREF_RECURSION_LIMIT: usize = 10;

View file

@ -30,7 +30,7 @@ use super::{
autoderef, lower, method_resolution, op, primitive, autoderef, lower, method_resolution, op, primitive,
traits::{Guidance, Obligation, ProjectionPredicate, Solution}, traits::{Guidance, Obligation, ProjectionPredicate, Solution},
ApplicationTy, CallableDef, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, ApplicationTy, CallableDef, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef,
Ty, TypableDef, TypeCtor, Ty, TypableDef, TypeCtor, TypeWalk,
}; };
use crate::{ use crate::{
adt::VariantDef, adt::VariantDef,

View file

@ -3,7 +3,7 @@
use super::{InferenceContext, Obligation}; use super::{InferenceContext, Obligation};
use crate::db::HirDatabase; use crate::db::HirDatabase;
use crate::ty::{ use crate::ty::{
Canonical, InEnvironment, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty, Canonical, InEnvironment, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty, TypeWalk,
}; };
impl<'a, D: HirDatabase> InferenceContext<'a, D> { impl<'a, D: HirDatabase> InferenceContext<'a, D> {

View file

@ -10,6 +10,7 @@ use std::sync::Arc;
use super::{ use super::{
FnSig, GenericPredicate, ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, FnSig, GenericPredicate, ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
TypeWalk,
}; };
use crate::{ use crate::{
adt::VariantDef, adt::VariantDef,

View file

@ -8,7 +8,7 @@ use ra_db::salsa;
use ra_prof::profile; use ra_prof::profile;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty}; use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};
use crate::{db::HirDatabase, Crate, ImplBlock, Trait}; use crate::{db::HirDatabase, Crate, ImplBlock, Trait};
use self::chalk::{from_chalk, ToChalk}; use self::chalk::{from_chalk, ToChalk};
@ -138,25 +138,13 @@ pub struct ProjectionPredicate {
pub ty: Ty, pub ty: Ty,
} }
impl ProjectionPredicate { impl TypeWalk for ProjectionPredicate {
pub fn subst(mut self, substs: &super::Substs) -> ProjectionPredicate { fn walk(&self, f: &mut impl FnMut(&Ty)) {
self.walk_mut(&mut |ty| match ty {
Ty::Param { idx, .. } => {
if let Some(t) = substs.get(*idx as usize).cloned() {
*ty = t;
}
}
_ => {}
});
self
}
pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
self.projection_ty.walk(f); self.projection_ty.walk(f);
self.ty.walk(f); self.ty.walk(f);
} }
pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
self.projection_ty.walk_mut(f); self.projection_ty.walk_mut(f);
self.ty.walk_mut(f); self.ty.walk_mut(f);
} }

View file

@ -19,6 +19,7 @@ use crate::{
ty::display::HirDisplay, ty::display::HirDisplay,
ty::{ ty::{
ApplicationTy, CallableDef, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, ApplicationTy, CallableDef, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
TypeWalk,
}, },
Crate, HasGenericParams, ImplBlock, ImplItem, Trait, TypeAlias, Crate, HasGenericParams, ImplBlock, ImplItem, Trait, TypeAlias,
}; };

View file

@ -1,5 +1,5 @@
//! This modules takes care of rendering various defenitions as completion items. //! This modules takes care of rendering various defenitions as completion items.
use hir::{Docs, HasSource, HirDisplay, PerNs, Resolution, Ty}; use hir::{Docs, HasSource, HirDisplay, PerNs, Resolution, Ty, TypeWalk};
use join_to_string::join; use join_to_string::join;
use ra_syntax::ast::NameOwner; use ra_syntax::ast::NameOwner;
use test_utils::tested_by; use test_utils::tested_by;