Merge #864
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:
commit
bb665a7062
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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(
|
||||
|
@ -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; }");
|
||||
|
Loading…
x
Reference in New Issue
Block a user