Add infer for generic default type

This commit is contained in:
Edwin Cheng 2019-05-19 21:08:16 +08:00
parent e74d8f148b
commit d4dc879415
4 changed files with 80 additions and 15 deletions

View file

@ -11,7 +11,7 @@ use crate::{
DefWithBody, Trait,
ids,
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},
impl_block::{ModuleImplBlocks, ImplSourceMap, ImplBlock},
generics::{GenericParams, GenericDef},
@ -141,6 +141,9 @@ pub trait HirDatabase: DefDatabase {
#[salsa::invoke(crate::ty::generic_predicates)]
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)]
fn body_with_source_map(
&self,

View file

@ -19,7 +19,7 @@ use std::{fmt, mem};
use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait, GenericParams};
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 use lower::CallableDef;

View file

@ -18,7 +18,7 @@ use crate::{
nameres::Namespace,
resolve::{Resolver, Resolution},
path::{PathSegment, GenericArg},
generics::{GenericParams, HasGenericParams},
generics::{HasGenericParams},
adt::VariantDef, Trait, generics::{ WherePredicate, GenericDef}
};
use super::{Ty, primitive, FnSig, Substs, TypeCtor, TraitRef, GenericPredicate};
@ -120,15 +120,15 @@ impl Ty {
segment: &PathSegment,
resolved: TypableDef,
) -> Substs {
let def_generics = match resolved {
TypableDef::Function(func) => func.generic_params(db),
TypableDef::Struct(s) => s.generic_params(db),
TypableDef::Enum(e) => e.generic_params(db),
TypableDef::EnumVariant(var) => var.parent_enum(db).generic_params(db),
TypableDef::TypeAlias(t) => t.generic_params(db),
TypableDef::Const(_) | TypableDef::Static(_) => GenericParams::default().into(),
let def_generic: Option<GenericDef> = match resolved {
TypableDef::Function(func) => Some(func.into()),
TypableDef::Struct(s) => Some(s.into()),
TypableDef::Enum(e) => Some(e.into()),
TypableDef::EnumVariant(var) => Some(var.parent_enum(db).into()),
TypableDef::TypeAlias(t) => Some(t.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
@ -172,10 +172,12 @@ pub(super) fn substs_from_path_segment(
db: &impl HirDatabase,
resolver: &Resolver,
segment: &PathSegment,
def_generics: &GenericParams,
def_generic: Option<GenericDef>,
add_self_param: bool,
) -> Substs {
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();
substs.extend(iter::repeat(Ty::Unknown).take(parent_param_count));
if add_self_param {
@ -199,13 +201,28 @@ pub(super) fn substs_from_path_segment(
}
}
// add placeholders for args that were not provided
// FIXME: handle defaults
let supplied_params = substs.len();
for _ in supplied_params..def_generics.count_params_including_parent() {
substs.push(Ty::Unknown);
}
assert_eq!(substs.len(), def_generics.count_params_including_parent());
Substs(substs.into())
let mut substs = Substs(substs.into());
// handle defaults
if let Some(def_generic) = def_generic {
let default_substs = db.generic_defaults(def_generic);
assert_eq!(substs.len(), default_substs.len());
let mut i = 0;
substs.walk_mut(&mut |ty| {
if *ty == Ty::Unknown {
*ty = default_substs[i].clone();
}
i += 1;
});
}
substs
}
impl TraitRef {
@ -249,7 +266,7 @@ impl TraitRef {
resolved: Trait,
) -> Substs {
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 {
@ -331,6 +348,22 @@ pub(crate) fn generic_predicates(
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 {
let signature = def.signature(db);
let resolver = def.resolver(db);

View file

@ -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]
fn infer_associated_method_generics_without_args() {
assert_snapshot_matches!(