Merge #1286
1286: Add infer for generic default type r=flodiebold a=edwin0cheng This PR add infer support for generic default type: ``` struct Gen<T=u32> { val: T } ``` * add the (unresolved) defaults from the definition to GenericParams * add a query generic_defaults that resolves those defaults to types and returns a Substs * add the missing type in `substs_from_path_segment` * add tests based on the idea in this [comment](https://github.com/rust-analyzer/rust-analyzer/issues/1099#issuecomment-484206279) Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
commit
3894eb77d8
5 changed files with 86 additions and 17 deletions
|
@ -11,7 +11,7 @@ use crate::{
|
||||||
DefWithBody, Trait,
|
DefWithBody, Trait,
|
||||||
ids,
|
ids,
|
||||||
nameres::{Namespace, ImportSourceMap, RawItems, CrateDefMap},
|
nameres::{Namespace, ImportSourceMap, RawItems, CrateDefMap},
|
||||||
ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig, TypeCtor, GenericPredicate},
|
ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig, TypeCtor, GenericPredicate, Substs},
|
||||||
adt::{StructData, EnumData},
|
adt::{StructData, EnumData},
|
||||||
impl_block::{ModuleImplBlocks, ImplSourceMap, ImplBlock},
|
impl_block::{ModuleImplBlocks, ImplSourceMap, ImplBlock},
|
||||||
generics::{GenericParams, GenericDef},
|
generics::{GenericParams, GenericDef},
|
||||||
|
@ -141,6 +141,9 @@ pub trait HirDatabase: DefDatabase {
|
||||||
#[salsa::invoke(crate::ty::generic_predicates)]
|
#[salsa::invoke(crate::ty::generic_predicates)]
|
||||||
fn generic_predicates(&self, def: GenericDef) -> Arc<[GenericPredicate]>;
|
fn generic_predicates(&self, def: GenericDef) -> Arc<[GenericPredicate]>;
|
||||||
|
|
||||||
|
#[salsa::invoke(crate::ty::generic_defaults)]
|
||||||
|
fn generic_defaults(&self, def: GenericDef) -> Substs;
|
||||||
|
|
||||||
#[salsa::invoke(crate::expr::body_with_source_map_query)]
|
#[salsa::invoke(crate::expr::body_with_source_map_query)]
|
||||||
fn body_with_source_map(
|
fn body_with_source_map(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ra_syntax::ast::{self, NameOwner, TypeParamsOwner, TypeBoundsOwner};
|
use ra_syntax::ast::{self, NameOwner, TypeParamsOwner, TypeBoundsOwner, DefaultTypeParamOwner};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{ HirDatabase, DefDatabase},
|
db::{ HirDatabase, DefDatabase},
|
||||||
|
@ -18,6 +18,7 @@ pub struct GenericParam {
|
||||||
// FIXME: give generic params proper IDs
|
// FIXME: give generic params proper IDs
|
||||||
pub(crate) idx: u32,
|
pub(crate) idx: u32,
|
||||||
pub(crate) name: Name,
|
pub(crate) name: Name,
|
||||||
|
pub(crate) default: Option<Path>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data about the generic parameters of a function, struct, impl, etc.
|
/// Data about the generic parameters of a function, struct, impl, etc.
|
||||||
|
@ -68,7 +69,11 @@ impl GenericParams {
|
||||||
GenericDef::Enum(it) => generics.fill(&*it.source(db).1, start),
|
GenericDef::Enum(it) => generics.fill(&*it.source(db).1, start),
|
||||||
GenericDef::Trait(it) => {
|
GenericDef::Trait(it) => {
|
||||||
// traits get the Self type as an implicit first type parameter
|
// traits get the Self type as an implicit first type parameter
|
||||||
generics.params.push(GenericParam { idx: start, name: Name::self_type() });
|
generics.params.push(GenericParam {
|
||||||
|
idx: start,
|
||||||
|
name: Name::self_type(),
|
||||||
|
default: None,
|
||||||
|
});
|
||||||
generics.fill(&*it.source(db).1, start + 1);
|
generics.fill(&*it.source(db).1, start + 1);
|
||||||
}
|
}
|
||||||
GenericDef::TypeAlias(it) => generics.fill(&*it.source(db).1, start),
|
GenericDef::TypeAlias(it) => generics.fill(&*it.source(db).1, start),
|
||||||
|
@ -90,7 +95,9 @@ impl GenericParams {
|
||||||
fn fill_params(&mut self, params: &ast::TypeParamList, start: u32) {
|
fn fill_params(&mut self, params: &ast::TypeParamList, start: u32) {
|
||||||
for (idx, type_param) in params.type_params().enumerate() {
|
for (idx, type_param) in params.type_params().enumerate() {
|
||||||
let name = type_param.name().map(AsName::as_name).unwrap_or_else(Name::missing);
|
let name = type_param.name().map(AsName::as_name).unwrap_or_else(Name::missing);
|
||||||
let param = GenericParam { idx: idx as u32 + start, name: name.clone() };
|
let default = type_param.default_type().and_then(|t| t.path()).and_then(Path::from_ast);
|
||||||
|
|
||||||
|
let param = GenericParam { idx: idx as u32 + start, name: name.clone(), default };
|
||||||
self.params.push(param);
|
self.params.push(param);
|
||||||
|
|
||||||
let type_ref = TypeRef::Path(name.into());
|
let type_ref = TypeRef::Path(name.into());
|
||||||
|
|
|
@ -19,7 +19,7 @@ use std::{fmt, mem};
|
||||||
use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait, GenericParams};
|
use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait, GenericParams};
|
||||||
use display::{HirDisplay, HirFormatter};
|
use display::{HirDisplay, HirFormatter};
|
||||||
|
|
||||||
pub(crate) use lower::{TypableDef, type_for_def, type_for_field, callable_item_sig, generic_predicates};
|
pub(crate) use lower::{TypableDef, type_for_def, type_for_field, callable_item_sig, generic_predicates, generic_defaults};
|
||||||
pub(crate) use infer::{infer, InferenceResult, InferTy};
|
pub(crate) use infer::{infer, InferenceResult, InferTy};
|
||||||
pub use lower::CallableDef;
|
pub use lower::CallableDef;
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ use crate::{
|
||||||
nameres::Namespace,
|
nameres::Namespace,
|
||||||
resolve::{Resolver, Resolution},
|
resolve::{Resolver, Resolution},
|
||||||
path::{PathSegment, GenericArg},
|
path::{PathSegment, GenericArg},
|
||||||
generics::{GenericParams, HasGenericParams},
|
generics::{HasGenericParams},
|
||||||
adt::VariantDef, Trait, generics::{ WherePredicate, GenericDef}
|
adt::VariantDef, Trait, generics::{ WherePredicate, GenericDef}
|
||||||
};
|
};
|
||||||
use super::{Ty, primitive, FnSig, Substs, TypeCtor, TraitRef, GenericPredicate};
|
use super::{Ty, primitive, FnSig, Substs, TypeCtor, TraitRef, GenericPredicate};
|
||||||
|
@ -120,15 +120,15 @@ impl Ty {
|
||||||
segment: &PathSegment,
|
segment: &PathSegment,
|
||||||
resolved: TypableDef,
|
resolved: TypableDef,
|
||||||
) -> Substs {
|
) -> Substs {
|
||||||
let def_generics = match resolved {
|
let def_generic: Option<GenericDef> = match resolved {
|
||||||
TypableDef::Function(func) => func.generic_params(db),
|
TypableDef::Function(func) => Some(func.into()),
|
||||||
TypableDef::Struct(s) => s.generic_params(db),
|
TypableDef::Struct(s) => Some(s.into()),
|
||||||
TypableDef::Enum(e) => e.generic_params(db),
|
TypableDef::Enum(e) => Some(e.into()),
|
||||||
TypableDef::EnumVariant(var) => var.parent_enum(db).generic_params(db),
|
TypableDef::EnumVariant(var) => Some(var.parent_enum(db).into()),
|
||||||
TypableDef::TypeAlias(t) => t.generic_params(db),
|
TypableDef::TypeAlias(t) => Some(t.into()),
|
||||||
TypableDef::Const(_) | TypableDef::Static(_) => GenericParams::default().into(),
|
TypableDef::Const(_) | TypableDef::Static(_) => None,
|
||||||
};
|
};
|
||||||
substs_from_path_segment(db, resolver, segment, &def_generics, false)
|
substs_from_path_segment(db, resolver, segment, def_generic, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collect generic arguments from a path into a `Substs`. See also
|
/// Collect generic arguments from a path into a `Substs`. See also
|
||||||
|
@ -172,10 +172,12 @@ pub(super) fn substs_from_path_segment(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
segment: &PathSegment,
|
segment: &PathSegment,
|
||||||
def_generics: &GenericParams,
|
def_generic: Option<GenericDef>,
|
||||||
add_self_param: bool,
|
add_self_param: bool,
|
||||||
) -> Substs {
|
) -> Substs {
|
||||||
let mut substs = Vec::new();
|
let mut substs = Vec::new();
|
||||||
|
let def_generics = def_generic.map(|def| def.generic_params(db)).unwrap_or_default();
|
||||||
|
|
||||||
let parent_param_count = def_generics.count_parent_params();
|
let parent_param_count = def_generics.count_parent_params();
|
||||||
substs.extend(iter::repeat(Ty::Unknown).take(parent_param_count));
|
substs.extend(iter::repeat(Ty::Unknown).take(parent_param_count));
|
||||||
if add_self_param {
|
if add_self_param {
|
||||||
|
@ -199,12 +201,24 @@ pub(super) fn substs_from_path_segment(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add placeholders for args that were not provided
|
// add placeholders for args that were not provided
|
||||||
// FIXME: handle defaults
|
|
||||||
let supplied_params = substs.len();
|
let supplied_params = substs.len();
|
||||||
for _ in supplied_params..def_generics.count_params_including_parent() {
|
for _ in supplied_params..def_generics.count_params_including_parent() {
|
||||||
substs.push(Ty::Unknown);
|
substs.push(Ty::Unknown);
|
||||||
}
|
}
|
||||||
assert_eq!(substs.len(), def_generics.count_params_including_parent());
|
assert_eq!(substs.len(), def_generics.count_params_including_parent());
|
||||||
|
|
||||||
|
// handle defaults
|
||||||
|
if let Some(def_generic) = def_generic {
|
||||||
|
let default_substs = db.generic_defaults(def_generic);
|
||||||
|
assert_eq!(substs.len(), default_substs.len());
|
||||||
|
|
||||||
|
for (i, default_ty) in default_substs.iter().enumerate() {
|
||||||
|
if substs[i] == Ty::Unknown {
|
||||||
|
substs[i] = default_ty.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Substs(substs.into())
|
Substs(substs.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,7 +263,7 @@ impl TraitRef {
|
||||||
resolved: Trait,
|
resolved: Trait,
|
||||||
) -> Substs {
|
) -> Substs {
|
||||||
let segment = path.segments.last().expect("path should have at least one segment");
|
let segment = path.segments.last().expect("path should have at least one segment");
|
||||||
substs_from_path_segment(db, resolver, segment, &resolved.generic_params(db), true)
|
substs_from_path_segment(db, resolver, segment, Some(resolved.into()), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn for_trait(db: &impl HirDatabase, trait_: Trait) -> TraitRef {
|
pub(crate) fn for_trait(db: &impl HirDatabase, trait_: Trait) -> TraitRef {
|
||||||
|
@ -331,6 +345,22 @@ pub(crate) fn generic_predicates(
|
||||||
predicates.into()
|
predicates.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve the default type params from generics
|
||||||
|
pub(crate) fn generic_defaults(db: &impl HirDatabase, def: GenericDef) -> Substs {
|
||||||
|
let resolver = def.resolver(db);
|
||||||
|
let generic_params = def.generic_params(db);
|
||||||
|
|
||||||
|
let defaults = generic_params
|
||||||
|
.params_including_parent()
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| {
|
||||||
|
p.default.as_ref().map_or(Ty::Unknown, |path| Ty::from_hir_path(db, &resolver, path))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Substs(defaults.into())
|
||||||
|
}
|
||||||
|
|
||||||
fn fn_sig_for_fn(db: &impl HirDatabase, def: Function) -> FnSig {
|
fn fn_sig_for_fn(db: &impl HirDatabase, def: Function) -> FnSig {
|
||||||
let signature = def.signature(db);
|
let signature = def.signature(db);
|
||||||
let resolver = def.resolver(db);
|
let resolver = def.resolver(db);
|
||||||
|
|
|
@ -1448,6 +1448,35 @@ fn test() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_associated_method_generics_with_default_param() {
|
||||||
|
assert_snapshot_matches!(
|
||||||
|
infer(r#"
|
||||||
|
struct Gen<T=u32> {
|
||||||
|
val: T
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Gen<T> {
|
||||||
|
pub fn make() -> Gen<T> {
|
||||||
|
loop { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
let a = Gen::make();
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[80; 104) '{ ... }': !
|
||||||
|
[90; 98) 'loop { }': !
|
||||||
|
[95; 98) '{ }': ()
|
||||||
|
[118; 146) '{ ...e(); }': ()
|
||||||
|
[128; 129) 'a': Gen<u32>
|
||||||
|
[132; 141) 'Gen::make': fn make<u32>() -> Gen<T>
|
||||||
|
[132; 143) 'Gen::make()': Gen<u32>"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_associated_method_generics_without_args() {
|
fn infer_associated_method_generics_without_args() {
|
||||||
assert_snapshot_matches!(
|
assert_snapshot_matches!(
|
||||||
|
|
Loading…
Reference in a new issue