864: Fix handling of generics in tuple variants and refactor a bit r=matklad a=flodiebold

(The problem was that we created separate substitutions for the return value, so we lost the connection between the type arguments in the constructor call and the type arguments of the result.)

Also make them display a tiny bit nicer.

Fixes #860.

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
Co-authored-by: Florian Diebold <florian.diebold@freiheit.com>
This commit is contained in:
bors[bot] 2019-02-21 20:47:50 +00:00
commit bb665a7062
6 changed files with 124 additions and 42 deletions

View file

@ -87,4 +87,17 @@ impl GenericParams {
let parent_count = self.count_parent_params();
parent_count + self.params.len()
}
fn for_each_param<'a>(&'a self, f: &mut impl FnMut(&'a GenericParam)) {
if let Some(parent) = &self.parent_params {
parent.for_each_param(f);
}
self.params.iter().for_each(f);
}
pub fn params_including_parent(&self) -> Vec<&GenericParam> {
let mut vec = Vec::with_capacity(self.count_params_including_parent());
self.for_each_param(&mut |p| vec.push(p));
vec
}
}

View file

@ -40,7 +40,7 @@ use crate::{
name::KnownName,
expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self},
generics::GenericParams,
path::GenericArg,
path::{ GenericArgs, GenericArg},
adt::VariantDef,
resolve::{Resolver, Resolution}, nameres::Namespace
};
@ -165,17 +165,6 @@ impl Substs {
pub fn empty() -> Substs {
Substs(Arc::new([]))
}
/// Replaces the end of the substitutions by other ones.
pub(crate) fn replace_tail(self, replace_by: Vec<Ty>) -> Substs {
// again missing Arc::make_mut_slice...
let len = replace_by.len().min(self.0.len());
let parent_len = self.0.len() - len;
let mut result = Vec::with_capacity(parent_len + len);
result.extend(self.0.iter().take(parent_len).cloned());
result.extend(replace_by);
Substs(result.into())
}
}
/// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs).
@ -454,7 +443,7 @@ impl Ty {
for _ in supplied_params..def_generics.count_params_including_parent() {
substs.push(Ty::Unknown);
}
assert_eq!(substs.len(), def_generics.params.len());
assert_eq!(substs.len(), def_generics.count_params_including_parent());
Substs(substs.into())
}
@ -639,8 +628,11 @@ impl fmt::Display for Ty {
join(sig.input.iter()).surround_with("fn(", ")").separator(", ").to_fmt(f)?;
write!(f, " -> {}", sig.output)
}
Ty::FnDef { name, substs, sig, .. } => {
write!(f, "fn {}", name)?;
Ty::FnDef { def, name, substs, sig, .. } => {
match def {
CallableDef::Function(_) => write!(f, "fn {}", name)?,
CallableDef::Struct(_) | CallableDef::EnumVariant(_) => write!(f, "{}", name)?,
}
if substs.0.len() > 0 {
join(substs.0.iter()).surround_with("<", ">").separator(", ").to_fmt(f)?;
}
@ -712,16 +704,18 @@ fn type_for_enum_variant_constructor(db: &impl HirDatabase, def: EnumVariant) ->
.iter()
.map(|(_, field)| Ty::from_hir(db, &resolver, &field.type_ref))
.collect::<Vec<_>>();
let output = type_for_enum(db, def.parent_enum(db));
let sig = Arc::new(FnSig { input, output });
let substs = make_substs(&generics);
let output = type_for_enum(db, def.parent_enum(db)).apply_substs(substs.clone());
let sig = Arc::new(FnSig { input, output });
Ty::FnDef { def: def.into(), sig, name, substs }
}
fn make_substs(generics: &GenericParams) -> Substs {
Substs(
(0..generics.count_params_including_parent())
.map(|_p| Ty::Unknown)
generics
.params_including_parent()
.into_iter()
.map(|p| Ty::Param { idx: p.idx, name: p.name.clone() })
.collect::<Vec<_>>()
.into(),
)
@ -736,7 +730,7 @@ fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty {
}
}
pub(crate) fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Ty {
fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Ty {
let generics = s.generic_params(db);
Ty::Adt {
def_id: s.into(),
@ -1353,6 +1347,37 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
ty
}
fn substs_for_method_call(
&mut self,
def_generics: Option<Arc<GenericParams>>,
generic_args: &Option<GenericArgs>,
) -> Substs {
let (parent_param_count, param_count) =
def_generics.map_or((0, 0), |g| (g.count_parent_params(), g.params.len()));
let mut substs = Vec::with_capacity(parent_param_count + param_count);
for _ in 0..parent_param_count {
substs.push(Ty::Unknown);
}
// handle provided type arguments
if let Some(generic_args) = generic_args {
// if args are provided, it should be all of them, but we can't rely on that
for arg in generic_args.args.iter().take(param_count) {
match arg {
GenericArg::Type(type_ref) => {
let ty = self.make_ty(type_ref);
substs.push(ty);
}
}
}
};
let supplied_params = substs.len();
for _ in supplied_params..parent_param_count + param_count {
substs.push(Ty::Unknown);
}
assert_eq!(substs.len(), parent_param_count + param_count);
Substs(substs.into())
}
fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
let body = Arc::clone(&self.body); // avoid borrow checker problem
let ty = match &body[tgt_expr] {
@ -1443,25 +1468,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
None => (Ty::Unknown, receiver_ty, None),
};
// handle provided type arguments
let method_ty = if let Some(generic_args) = generic_args {
// if args are provided, it should be all of them, but we can't rely on that
let param_count = def_generics.map(|g| g.params.len()).unwrap_or(0);
let mut new_substs = Vec::with_capacity(generic_args.args.len());
for arg in generic_args.args.iter().take(param_count) {
match arg {
GenericArg::Type(type_ref) => {
let ty = self.make_ty(type_ref);
new_substs.push(ty);
}
}
}
let substs = method_ty.substs().unwrap_or_else(Substs::empty);
let substs = substs.replace_tail(new_substs);
method_ty.apply_substs(substs)
} else {
method_ty
};
let substs = self.substs_for_method_call(def_generics, generic_args);
let method_ty = method_ty.apply_substs(substs);
let method_ty = self.insert_type_vars(method_ty);
let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty {
Ty::FnPtr(sig) => {

View file

@ -1,19 +1,19 @@
---
created: "2019-02-17T16:16:58.863630956Z"
created: "2019-02-20T21:31:12.910924715Z"
creator: insta@0.6.2
source: crates/ra_hir/src/ty/tests.rs
expression: "&result"
---
[72; 154) '{ ...a.c; }': ()
[82; 83) 'c': C
[86; 87) 'C': fn C(usize) -> C
[86; 87) 'C': C(usize) -> C
[86; 90) 'C(1)': C
[88; 89) '1': usize
[96; 97) 'B': B
[107; 108) 'a': A
[114; 133) 'A { b:...C(1) }': A
[121; 122) 'B': B
[127; 128) 'C': fn C(usize) -> C
[127; 128) 'C': C(usize) -> C
[127; 131) 'C(1)': C
[129; 130) '1': usize
[139; 140) 'a': A

View file

@ -0,0 +1,23 @@
---
created: "2019-02-20T21:31:12.911275141Z"
creator: insta@0.6.2
source: crates/ra_hir/src/ty/tests.rs
expression: "&result"
---
[77; 185) '{ ...one; }': ()
[83; 84) 'A': A<i32>(T) -> A<T>
[83; 88) 'A(42)': A<i32>
[85; 87) '42': i32
[94; 95) 'A': A<u128>(T) -> A<T>
[94; 103) 'A(42u128)': A<u128>
[96; 102) '42u128': u128
[109; 113) 'Some': Some<&str>(T) -> Option<T>
[109; 118) 'Some("x")': Option<&str>
[114; 117) '"x"': &str
[124; 136) 'Option::Some': Some<&str>(T) -> Option<T>
[124; 141) 'Option...e("x")': Option<&str>
[137; 140) '"x"': &str
[147; 151) 'None': Option<[unknown]>
[161; 162) 'x': Option<i64>
[178; 182) 'None': Option<i64>

View file

@ -465,6 +465,27 @@ fn test(a1: A<u32>, i: i32) {
);
}
#[test]
fn infer_tuple_struct_generics() {
check_inference(
"infer_tuple_struct_generics",
r#"
struct A<T>(T);
enum Option<T> { Some(T), None };
use Option::*;
fn test() {
A(42);
A(42u128);
Some("x");
Option::Some("x");
None;
let x: Option<i64> = None;
}
"#,
);
}
#[test]
fn infer_generics_in_patterns() {
check_inference(

View file

@ -163,6 +163,23 @@ mod tests {
assert_eq!(hover.info, "u32");
}
#[test]
fn hover_some() {
let (analysis, position) = single_file_with_position(
"
enum Option<T> { Some(T) }
use Option::Some;
fn main() {
So<|>me(12);
}
",
);
let hover = analysis.hover(position).unwrap().unwrap();
// not the nicest way to show it currently
assert_eq!(hover.info, "Some<i32>(T) -> Option<T>");
}
#[test]
fn hover_for_local_variable() {
let (analysis, position) = single_file_with_position("fn func(foo: i32) { fo<|>o; }");