From 415cdc52108807126f0339fbf812856582f01c18 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 12 Jan 2019 18:47:43 +0100 Subject: Add some tests --- crates/ra_hir/src/ty/tests.rs | 63 ++++++++++++++++++++++ .../ra_hir/src/ty/tests/data/function_generics.txt | 14 +++++ crates/ra_hir/src/ty/tests/data/generic_chain.txt | 29 ++++++++++ .../ra_hir/src/ty/tests/data/struct_generics.txt | 15 ++++++ 4 files changed, 121 insertions(+) create mode 100644 crates/ra_hir/src/ty/tests/data/function_generics.txt create mode 100644 crates/ra_hir/src/ty/tests/data/generic_chain.txt create mode 100644 crates/ra_hir/src/ty/tests/data/struct_generics.txt (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index fc4054159..c590a09db 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -418,6 +418,69 @@ fn test() { ); } +#[test] +fn infer_struct_generics() { + check_inference( + r#" +struct A { + x: T, +} + +fn test(a1: A, i: i32) { + a1.x; + let a2 = A { x: i }; + a2.x; + let a3 = A:: { x: 1 }; + a3.x; +} +"#, + "struct_generics.txt", + ); +} + +#[test] +fn infer_function_generics() { + check_inference( + r#" +fn id(t: T) -> T { t } + +fn test() { + id(1u32); + id::(1); + let x: u64 = id(1); +} +"#, + "function_generics.txt", + ); +} + +#[test] +fn infer_generic_chain() { + check_inference( + r#" +struct A { + x: T, +} +impl A { + fn x(self) -> T2 { + self.x + } +} +fn id(t: T) -> T { t } + +fn test() -> i128 { + let x = 1; + let y = id(x); + let a = A { x: id(y) }; + let z = id(a.x); + let b = A { x: z }; + b.x() +} +"#, + "generic_chain.txt", + ); +} + fn infer(content: &str) -> String { let (db, _, file_id) = MockDatabase::with_single_file(content); let source_file = db.source_file(file_id); diff --git a/crates/ra_hir/src/ty/tests/data/function_generics.txt b/crates/ra_hir/src/ty/tests/data/function_generics.txt new file mode 100644 index 000000000..179e136a0 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/function_generics.txt @@ -0,0 +1,14 @@ +[10; 11) 't': [unknown] +[21; 26) '{ t }': [unknown] +[23; 24) 't': [unknown] +[38; 98) '{ ...(1); }': () +[44; 46) 'id': fn([unknown]) -> [unknown] +[44; 52) 'id(1u32)': [unknown] +[47; 51) '1u32': [unknown] +[58; 68) 'id::': fn([unknown]) -> [unknown] +[58; 71) 'id::(1)': [unknown] +[69; 70) '1': [unknown] +[81; 82) 'x': u64 +[90; 92) 'id': fn([unknown]) -> u64 +[90; 95) 'id(1)': u64 +[93; 94) '1': [unknown] diff --git a/crates/ra_hir/src/ty/tests/data/generic_chain.txt b/crates/ra_hir/src/ty/tests/data/generic_chain.txt new file mode 100644 index 000000000..720609153 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/generic_chain.txt @@ -0,0 +1,29 @@ +[53; 57) 'self': A +[65; 87) '{ ... }': [unknown] +[75; 79) 'self': A +[75; 81) 'self.x': [unknown] +[99; 100) 't': [unknown] +[110; 115) '{ t }': [unknown] +[112; 113) 't': [unknown] +[135; 261) '{ ....x() }': i128 +[146; 147) 'x': [unknown] +[150; 151) '1': [unknown] +[162; 163) 'y': [unknown] +[166; 168) 'id': fn([unknown]) -> [unknown] +[166; 171) 'id(x)': [unknown] +[169; 170) 'x': [unknown] +[182; 183) 'a': A +[186; 200) 'A { x: id(y) }': A +[193; 195) 'id': fn([unknown]) -> [unknown] +[193; 198) 'id(y)': [unknown] +[196; 197) 'y': [unknown] +[211; 212) 'z': [unknown] +[215; 217) 'id': fn([unknown]) -> [unknown] +[215; 222) 'id(a.x)': [unknown] +[218; 219) 'a': A +[218; 221) 'a.x': [unknown] +[233; 234) 'b': A +[237; 247) 'A { x: z }': A +[244; 245) 'z': [unknown] +[254; 255) 'b': A +[254; 259) 'b.x()': i128 diff --git a/crates/ra_hir/src/ty/tests/data/struct_generics.txt b/crates/ra_hir/src/ty/tests/data/struct_generics.txt new file mode 100644 index 000000000..d1026b459 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/struct_generics.txt @@ -0,0 +1,15 @@ +[36; 38) 'a1': A +[48; 49) 'i': i32 +[56; 147) '{ ...3.x; }': () +[62; 64) 'a1': A +[62; 66) 'a1.x': [unknown] +[76; 78) 'a2': A +[81; 91) 'A { x: i }': A +[88; 89) 'i': i32 +[97; 99) 'a2': A +[97; 101) 'a2.x': [unknown] +[111; 113) 'a3': A +[116; 134) 'A:: Date: Sat, 12 Jan 2019 21:27:35 +0100 Subject: Implement beginnings of generics - add HIR for generic params - resolve generic params in type paths - add substitions for ADTs - insert type variables for substitutions --- crates/ra_hir/src/adt.rs | 5 +- crates/ra_hir/src/code_model_api.rs | 21 +++ crates/ra_hir/src/db.rs | 4 + crates/ra_hir/src/generics.rs | 48 ++++++ crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/mock.rs | 1 + crates/ra_hir/src/ty.rs | 180 +++++++++++++++++---- crates/ra_hir/src/ty/method_resolution.rs | 17 +- .../ra_hir/src/ty/tests/data/function_generics.txt | 20 +-- crates/ra_hir/src/ty/tests/data/generic_chain.txt | 44 ++--- .../ra_hir/src/ty/tests/data/struct_generics.txt | 22 +-- 11 files changed, 283 insertions(+), 80 deletions(-) create mode 100644 crates/ra_hir/src/generics.rs (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index d87fe7049..ab1c428db 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs @@ -1,3 +1,6 @@ +//! This module contains the implementation details of the HIR for ADTs, i.e. +//! structs and enums (and unions). + use std::sync::Arc; use ra_syntax::{ @@ -62,7 +65,7 @@ fn get_def_id( }; let loc = DefLoc { kind: expected_kind, - source_item_id: source_item_id, + source_item_id, ..*same_file_loc }; loc.id(db) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 11e919c1a..6c85e5939 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -12,6 +12,7 @@ use crate::{ expr::BodySyntaxMapping, ty::InferenceResult, adt::VariantData, + generics::Generics, code_model_impl::def_id_to_ast, }; @@ -201,6 +202,10 @@ impl Struct { pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc) { def_id_to_ast(db, self.def_id) } + + pub fn generics(&self, db: &impl HirDatabase) -> Arc { + db.generics(self.def_id) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -228,6 +233,10 @@ impl Enum { pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc) { def_id_to_ast(db, self.def_id) } + + pub fn generics(&self, db: &impl HirDatabase) -> Arc { + db.generics(self.def_id) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -339,6 +348,10 @@ impl Function { pub fn infer(&self, db: &impl HirDatabase) -> Arc { db.infer(self.def_id) } + + pub fn generics(&self, db: &impl HirDatabase) -> Arc { + db.generics(self.def_id) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -384,6 +397,10 @@ impl Trait { pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc) { def_id_to_ast(db, self.def_id) } + + pub fn generics(&self, db: &impl HirDatabase) -> Arc { + db.generics(self.def_id) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -399,4 +416,8 @@ impl Type { pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc) { def_id_to_ast(db, self.def_id) } + + pub fn generics(&self, db: &impl HirDatabase) -> Arc { + db.generics(self.def_id) + } } diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 60065cf6a..10e64f880 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -14,6 +14,7 @@ use crate::{ ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks}, adt::{StructData, EnumData, EnumVariantData}, impl_block::ModuleImplBlocks, + generics::Generics, }; #[salsa::query_group] @@ -101,6 +102,9 @@ pub trait HirDatabase: #[salsa::invoke(crate::expr::body_syntax_mapping)] fn body_syntax_mapping(&self, def_id: DefId) -> Arc; + #[salsa::invoke(crate::generics::Generics::generics_query)] + fn generics(&self, def_id: DefId) -> Arc; + #[salsa::invoke(crate::FnSignature::fn_signature_query)] fn fn_signature(&self, def_id: DefId) -> Arc; } diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs new file mode 100644 index 000000000..cdabc0424 --- /dev/null +++ b/crates/ra_hir/src/generics.rs @@ -0,0 +1,48 @@ +//! Many kinds of items or constructs can have generic parameters: functions, +//! structs, impls, traits, etc. This module provides a common HIR for these +//! generic parameters. See also the `Generics` type and the `generics_of` query +//! in rustc. + +use std::sync::Arc; + +use ra_syntax::ast::{TypeParamList, AstNode, NameOwner}; + +use crate::{db::HirDatabase, DefId, Name, AsName}; + +/// Data about a generic parameter (to a function, struct, impl, ...). +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct GenericParam { + pub(crate) idx: u32, + pub(crate) name: Name, +} + +/// Data about the generic parameters of a function, struct, impl, etc. +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct Generics { + pub(crate) params: Vec, +} + +impl Generics { + pub(crate) fn generics_query(db: &impl HirDatabase, def_id: DefId) -> Arc { + let (_file_id, node) = def_id.source(db); + let mut generics = Generics::default(); + if let Some(type_param_list) = node.children().find_map(TypeParamList::cast) { + for (idx, type_param) in type_param_list.type_params().enumerate() { + let name = type_param + .name() + .map(AsName::as_name) + .unwrap_or_else(Name::missing); + let param = GenericParam { + idx: idx as u32, + name, + }; + generics.params.push(param); + } + } + Arc::new(generics) + } + + pub(crate) fn find_by_name(&self, name: &Name) -> Option<&GenericParam> { + self.params.iter().find(|p| &p.name == name) + } +} diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index f1402a286..361d39f03 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -24,6 +24,7 @@ mod type_ref; mod ty; mod impl_block; mod expr; +mod generics; mod code_model_api; mod code_model_impl; diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 018e5e0d4..d674db8e6 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -243,6 +243,7 @@ salsa::database_storage! { fn body_hir() for db::BodyHirQuery; fn body_syntax_mapping() for db::BodySyntaxMappingQuery; fn fn_signature() for db::FnSignatureQuery; + fn generics() for db::GenericsQuery; } } } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 8d6493887..b9a48929d 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -37,6 +37,7 @@ use crate::{ type_ref::{TypeRef, Mutability}, name::KnownName, expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, + generics::Generics, }; /// The ID of a type variable. @@ -151,10 +152,14 @@ impl Expectation { } } +/// A list of substitutions for generic parameters. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Substs(Arc<[Ty]>); + /// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs). /// /// This should be cheap to clone. -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] pub enum Ty { /// The primitive boolean type. Written as `bool`. Bool, @@ -175,7 +180,8 @@ pub enum Ty { def_id: DefId, /// The name, for displaying. name: Name, - // later we'll need generic substitutions here + /// Substitutions for the generic parameters of the type. + substs: Substs, }, /// The pointee of a string slice. Written as `str`. @@ -234,9 +240,14 @@ pub enum Ty { // Opaque (`impl Trait`) type found in a return type. // Opaque(DefId, Substs), + /// A type parameter; for example, `T` in `fn f(x: T) {} + Param { + /// The index of the parameter. + idx: u32, + /// The name of the parameter, for displaying. + name: Name, + }, - // A type parameter; for example, `T` in `fn f(x: T) {} - // Param(ParamTy), /// A type variable used during type checking. Not to be confused with a /// type parameter. Infer(InferTy), @@ -250,7 +261,7 @@ pub enum Ty { } /// A function signature. -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] pub struct FnSig { input: Vec, output: Ty, @@ -261,6 +272,7 @@ impl Ty { db: &impl HirDatabase, module: &Module, impl_block: Option<&ImplBlock>, + generics: &Generics, type_ref: &TypeRef, ) -> Self { match type_ref { @@ -268,32 +280,32 @@ impl Ty { TypeRef::Tuple(inner) => { let inner_tys = inner .iter() - .map(|tr| Ty::from_hir(db, module, impl_block, tr)) + .map(|tr| Ty::from_hir(db, module, impl_block, generics, tr)) .collect::>(); Ty::Tuple(inner_tys.into()) } - TypeRef::Path(path) => Ty::from_hir_path(db, module, impl_block, path), + TypeRef::Path(path) => Ty::from_hir_path(db, module, impl_block, generics, path), TypeRef::RawPtr(inner, mutability) => { - let inner_ty = Ty::from_hir(db, module, impl_block, inner); + let inner_ty = Ty::from_hir(db, module, impl_block, generics, inner); Ty::RawPtr(Arc::new(inner_ty), *mutability) } TypeRef::Array(inner) => { - let inner_ty = Ty::from_hir(db, module, impl_block, inner); + let inner_ty = Ty::from_hir(db, module, impl_block, generics, inner); Ty::Array(Arc::new(inner_ty)) } TypeRef::Slice(inner) => { - let inner_ty = Ty::from_hir(db, module, impl_block, inner); + let inner_ty = Ty::from_hir(db, module, impl_block, generics, inner); Ty::Slice(Arc::new(inner_ty)) } TypeRef::Reference(inner, mutability) => { - let inner_ty = Ty::from_hir(db, module, impl_block, inner); + let inner_ty = Ty::from_hir(db, module, impl_block, generics, inner); Ty::Ref(Arc::new(inner_ty), *mutability) } TypeRef::Placeholder => Ty::Unknown, TypeRef::Fn(params) => { let mut inner_tys = params .iter() - .map(|tr| Ty::from_hir(db, module, impl_block, tr)) + .map(|tr| Ty::from_hir(db, module, impl_block, generics, tr)) .collect::>(); let return_ty = inner_tys .pop() @@ -312,15 +324,19 @@ impl Ty { db: &impl HirDatabase, module: &Module, impl_block: Option<&ImplBlock>, + generics: &Generics, type_ref: Option<&TypeRef>, ) -> Self { - type_ref.map_or(Ty::Unknown, |t| Ty::from_hir(db, module, impl_block, t)) + type_ref.map_or(Ty::Unknown, |t| { + Ty::from_hir(db, module, impl_block, generics, t) + }) } pub(crate) fn from_hir_path( db: &impl HirDatabase, module: &Module, impl_block: Option<&ImplBlock>, + generics: &Generics, path: &Path, ) -> Self { if let Some(name) = path.as_ident() { @@ -329,7 +345,15 @@ impl Ty { } else if let Some(float_ty) = primitive::UncertainFloatTy::from_name(name) { return Ty::Float(float_ty); } else if name.as_known_name() == Some(KnownName::SelfType) { - return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type())); + // TODO pass the impl block's generics? + let generics = &Generics::default(); + return Ty::from_hir_opt( + db, + module, + None, + generics, + impl_block.map(|i| i.target_type()), + ); } else if let Some(known) = name.as_known_name() { match known { KnownName::Bool => return Ty::Bool, @@ -337,6 +361,11 @@ impl Ty { KnownName::Str => return Ty::Str, _ => {} } + } else if let Some(generic_param) = generics.find_by_name(&name) { + return Ty::Param { + idx: generic_param.idx, + name: generic_param.name.clone(), + }; } } @@ -374,7 +403,14 @@ impl Ty { } sig_mut.output.walk_mut(f); } - Ty::Adt { .. } => {} // need to walk type parameters later + Ty::Adt { substs, .. } => { + // Without an Arc::make_mut_slice, we can't avoid the clone here: + let mut v: Vec<_> = substs.0.iter().cloned().collect(); + for t in &mut v { + t.walk_mut(f); + } + substs.0 = v.into(); + } _ => {} } } @@ -394,6 +430,32 @@ impl Ty { _ => None, } } + + /// 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 } => { + if (idx as usize) < substs.0.len() { + substs.0[idx as usize].clone() + } else { + // TODO it's yet unclear to me whether we need to shift the indices here + Ty::Param { idx, name } + } + } + ty => ty, + }) + } + + /// Returns the type parameters of this type if it has some (i.e. is an ADT + /// or function); so if `self` is `Option`, this returns the `u32`. + fn substs(&self) -> Option { + match self { + Ty::Adt { substs, .. } => Some(substs.clone()), + _ => None, + } + } } impl fmt::Display for Ty { @@ -425,7 +487,17 @@ impl fmt::Display for Ty { .to_fmt(f)?; write!(f, " -> {}", sig.output) } - Ty::Adt { name, .. } => write!(f, "{}", name), + Ty::Adt { name, substs, .. } => { + write!(f, "{}", name)?; + if substs.0.len() > 0 { + join(substs.0.iter()) + .surround_with("<", ">") + .separator(", ") + .to_fmt(f)?; + } + Ok(()) + } + Ty::Param { name, .. } => write!(f, "{}", name), Ty::Unknown => write!(f, "[unknown]"), Ty::Infer(..) => write!(f, "_"), } @@ -440,28 +512,49 @@ fn type_for_fn(db: &impl HirDatabase, f: Function) -> Ty { let signature = f.signature(db); let module = f.module(db); let impl_block = f.impl_block(db); - // TODO we ignore type parameters for now + let generics = f.generics(db); let input = signature .params() .iter() - .map(|tr| Ty::from_hir(db, &module, impl_block.as_ref(), tr)) + .map(|tr| Ty::from_hir(db, &module, impl_block.as_ref(), &generics, tr)) .collect::>(); - let output = Ty::from_hir(db, &module, impl_block.as_ref(), signature.ret_type()); + let output = Ty::from_hir( + db, + &module, + impl_block.as_ref(), + &generics, + signature.ret_type(), + ); let sig = FnSig { input, output }; Ty::FnPtr(Arc::new(sig)) } +fn make_substs(generics: &Generics) -> Substs { + Substs( + generics + .params + .iter() + .map(|_p| Ty::Unknown) + .collect::>() + .into(), + ) +} + fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty { + let generics = s.generics(db); Ty::Adt { def_id: s.def_id(), name: s.name(db).unwrap_or_else(Name::missing), + substs: make_substs(&generics), } } pub(crate) fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Ty { + let generics = s.generics(db); Ty::Adt { def_id: s.def_id(), name: s.name(db).unwrap_or_else(Name::missing), + substs: make_substs(&generics), } } @@ -506,8 +599,15 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) }; let module = def_id.module(db); let impl_block = def_id.impl_block(db); + let generics = db.generics(def_id); let type_ref = variant_data.get_field_type_ref(&field)?; - Some(Ty::from_hir(db, &module, impl_block.as_ref(), &type_ref)) + Some(Ty::from_hir( + db, + &module, + impl_block.as_ref(), + &generics, + &type_ref, + )) } /// The result of type inference: A mapping from expressions and patterns to types. @@ -684,8 +784,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.type_of_pat.insert(pat, ty); } - fn make_ty(&self, type_ref: &TypeRef) -> Ty { - Ty::from_hir(self.db, &self.module, self.impl_block.as_ref(), type_ref) + fn make_ty(&mut self, type_ref: &TypeRef) -> Ty { + // TODO provide generics of function + let generics = Generics::default(); + let ty = Ty::from_hir( + self.db, + &self.module, + self.impl_block.as_ref(), + &generics, + type_ref, + ); + let ty = self.insert_type_vars(ty); + ty } fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { @@ -848,7 +958,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Some(ty) } - fn resolve_variant(&self, path: Option<&Path>) -> (Ty, Option) { + fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option) { let path = if let Some(path) = path { path } else { @@ -862,10 +972,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { match def_id.resolve(self.db) { Def::Struct(s) => { let ty = type_for_struct(self.db, s); + let ty = self.insert_type_vars(ty); (ty, Some(def_id)) } Def::EnumVariant(ev) => { let ty = type_for_enum_variant(self.db, ev); + let ty = self.insert_type_vars(ty); (ty, Some(def_id)) } _ => (Ty::Unknown, None), @@ -1155,11 +1267,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { spread, } => { let (ty, def_id) = self.resolve_variant(path.as_ref()); + let substs = ty.substs().expect("adt should have substs"); for field in fields { let field_ty = if let Some(def_id) = def_id { self.db .type_for_field(def_id, field.name.clone()) .unwrap_or(Ty::Unknown) + .subst(&substs) } else { Ty::Unknown }; @@ -1180,7 +1294,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let i = name.to_string().parse::().ok(); i.and_then(|i| fields.get(i).cloned()) } - Ty::Adt { def_id, .. } => self.db.type_for_field(def_id, name.clone()), + Ty::Adt { + def_id, ref substs, .. + } => self + .db + .type_for_field(def_id, name.clone()) + .map(|ty| ty.subst(substs)), _ => None, }) .unwrap_or(Ty::Unknown); @@ -1193,7 +1312,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Expr::Cast { expr, type_ref } => { let _inner_ty = self.infer_expr(*expr, &Expectation::none()); let cast_ty = self.make_ty(type_ref); - let cast_ty = self.insert_type_vars(cast_ty); // TODO check the cast... cast_ty } @@ -1305,12 +1423,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { type_ref, initializer, } => { - let decl_ty = Ty::from_hir_opt( - self.db, - &self.module, - self.impl_block.as_ref(), - type_ref.as_ref(), - ); + let decl_ty = type_ref + .as_ref() + .map(|tr| self.make_ty(tr)) + .unwrap_or(Ty::Unknown); let decl_ty = self.insert_type_vars(decl_ty); let ty = if let Some(expr) = initializer { let expr_ty = self.infer_expr(*expr, &Expectation::has_type(decl_ty)); @@ -1338,13 +1454,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let body = Arc::clone(&self.body); // avoid borrow checker problem for (type_ref, pat) in signature.params().iter().zip(body.params()) { let ty = self.make_ty(type_ref); - let ty = self.insert_type_vars(ty); self.infer_pat(*pat, &ty); } self.return_ty = { let ty = self.make_ty(signature.ret_type()); - let ty = self.insert_type_vars(ty); ty }; } diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index b221bd142..53b6d15a8 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -8,7 +8,11 @@ use rustc_hash::FxHashMap; use ra_db::SourceRootId; -use crate::{HirDatabase, DefId, module_tree::ModuleId, Module, Crate, Name, Function, impl_block::{ImplId, ImplBlock, ImplItem}}; +use crate::{ + HirDatabase, DefId, module_tree::ModuleId, Module, Crate, Name, Function, + impl_block::{ImplId, ImplBlock, ImplItem}, + generics::Generics +}; use super::Ty; /// This is used as a key for indexing impls. @@ -64,8 +68,15 @@ impl CrateImplBlocks { if let Some(_target_trait) = impl_data.target_trait() { // ignore for now } else { - let target_ty = - Ty::from_hir(db, &module, Some(&impl_block), impl_data.target_type()); + // TODO provide generics of impl + let generics = Generics::default(); + let target_ty = Ty::from_hir( + db, + &module, + Some(&impl_block), + &generics, + impl_data.target_type(), + ); if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { self.impls .entry(target_ty_fp) diff --git a/crates/ra_hir/src/ty/tests/data/function_generics.txt b/crates/ra_hir/src/ty/tests/data/function_generics.txt index 179e136a0..e44d26cfd 100644 --- a/crates/ra_hir/src/ty/tests/data/function_generics.txt +++ b/crates/ra_hir/src/ty/tests/data/function_generics.txt @@ -2,13 +2,13 @@ [21; 26) '{ t }': [unknown] [23; 24) 't': [unknown] [38; 98) '{ ...(1); }': () -[44; 46) 'id': fn([unknown]) -> [unknown] -[44; 52) 'id(1u32)': [unknown] -[47; 51) '1u32': [unknown] -[58; 68) 'id::': fn([unknown]) -> [unknown] -[58; 71) 'id::(1)': [unknown] -[69; 70) '1': [unknown] -[81; 82) 'x': u64 -[90; 92) 'id': fn([unknown]) -> u64 -[90; 95) 'id(1)': u64 -[93; 94) '1': [unknown] +[44; 46) 'id': fn(T) -> T +[44; 52) 'id(1u32)': T +[47; 51) '1u32': u32 +[58; 68) 'id::': fn(T) -> T +[58; 71) 'id::(1)': T +[69; 70) '1': T +[81; 82) 'x': T +[90; 92) 'id': fn(T) -> T +[90; 95) 'id(1)': T +[93; 94) '1': T diff --git a/crates/ra_hir/src/ty/tests/data/generic_chain.txt b/crates/ra_hir/src/ty/tests/data/generic_chain.txt index 720609153..568e00846 100644 --- a/crates/ra_hir/src/ty/tests/data/generic_chain.txt +++ b/crates/ra_hir/src/ty/tests/data/generic_chain.txt @@ -1,29 +1,29 @@ -[53; 57) 'self': A +[53; 57) 'self': A<[unknown]> [65; 87) '{ ... }': [unknown] -[75; 79) 'self': A +[75; 79) 'self': A<[unknown]> [75; 81) 'self.x': [unknown] [99; 100) 't': [unknown] [110; 115) '{ t }': [unknown] [112; 113) 't': [unknown] [135; 261) '{ ....x() }': i128 -[146; 147) 'x': [unknown] -[150; 151) '1': [unknown] -[162; 163) 'y': [unknown] -[166; 168) 'id': fn([unknown]) -> [unknown] -[166; 171) 'id(x)': [unknown] -[169; 170) 'x': [unknown] -[182; 183) 'a': A -[186; 200) 'A { x: id(y) }': A -[193; 195) 'id': fn([unknown]) -> [unknown] -[193; 198) 'id(y)': [unknown] -[196; 197) 'y': [unknown] -[211; 212) 'z': [unknown] -[215; 217) 'id': fn([unknown]) -> [unknown] -[215; 222) 'id(a.x)': [unknown] -[218; 219) 'a': A -[218; 221) 'a.x': [unknown] -[233; 234) 'b': A -[237; 247) 'A { x: z }': A -[244; 245) 'z': [unknown] -[254; 255) 'b': A +[146; 147) 'x': T +[150; 151) '1': T +[162; 163) 'y': T +[166; 168) 'id': fn(T) -> T +[166; 171) 'id(x)': T +[169; 170) 'x': T +[182; 183) 'a': A +[186; 200) 'A { x: id(y) }': A +[193; 195) 'id': fn(T) -> T +[193; 198) 'id(y)': T +[196; 197) 'y': T +[211; 212) 'z': T +[215; 217) 'id': fn(T) -> T +[215; 222) 'id(a.x)': T +[218; 219) 'a': A +[218; 221) 'a.x': T +[233; 234) 'b': A +[237; 247) 'A { x: z }': A +[244; 245) 'z': T +[254; 255) 'b': A [254; 259) 'b.x()': i128 diff --git a/crates/ra_hir/src/ty/tests/data/struct_generics.txt b/crates/ra_hir/src/ty/tests/data/struct_generics.txt index d1026b459..88cd2b409 100644 --- a/crates/ra_hir/src/ty/tests/data/struct_generics.txt +++ b/crates/ra_hir/src/ty/tests/data/struct_generics.txt @@ -1,15 +1,15 @@ -[36; 38) 'a1': A +[36; 38) 'a1': A<[unknown]> [48; 49) 'i': i32 [56; 147) '{ ...3.x; }': () -[62; 64) 'a1': A +[62; 64) 'a1': A<[unknown]> [62; 66) 'a1.x': [unknown] -[76; 78) 'a2': A -[81; 91) 'A { x: i }': A +[76; 78) 'a2': A +[81; 91) 'A { x: i }': A [88; 89) 'i': i32 -[97; 99) 'a2': A -[97; 101) 'a2.x': [unknown] -[111; 113) 'a3': A -[116; 134) 'A:: +[97; 101) 'a2.x': i32 +[111; 113) 'a3': A +[116; 134) 'A:: +[131; 132) '1': i32 +[140; 142) 'a3': A +[140; 144) 'a3.x': i32 -- cgit v1.2.3 From 5862542dedd5aca9bbdcba19c5f8cd895591005d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 13 Jan 2019 00:19:20 +0100 Subject: Add AST/HIR for type args in path segments --- crates/ra_hir/src/code_model_impl/module.rs | 10 +++-- crates/ra_hir/src/nameres.rs | 24 +++++----- crates/ra_hir/src/path.rs | 68 ++++++++++++++++++++++++++--- 3 files changed, 80 insertions(+), 22 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs index 7215236f9..2014f1090 100644 --- a/crates/ra_hir/src/code_model_impl/module.rs +++ b/crates/ra_hir/src/code_model_impl/module.rs @@ -147,7 +147,7 @@ impl Module { .def_id, ); - for name in path.segments.iter() { + for segment in path.segments.iter() { let curr = match curr_per_ns.as_ref().take_types() { Some(r) => r, None => { @@ -163,15 +163,17 @@ impl Module { curr_per_ns = match curr.resolve(db) { Def::Module(m) => { let scope = m.scope(db); - match scope.get(&name) { + match scope.get(&segment.name) { Some(r) => r.def_id, None => PerNs::none(), } } Def::Enum(e) => { // enum variant - let matching_variant = - e.variants(db).into_iter().find(|(n, _variant)| n == name); + let matching_variant = e + .variants(db) + .into_iter() + .find(|(n, _variant)| n == &segment.name); match matching_variant { Some((_n, variant)) => PerNs::both(variant.def_id(), e.def_id()), diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 4efafd409..4874e82f3 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -221,10 +221,10 @@ where }; } for (import_id, import_data) in input.imports.iter() { - if let Some(name) = import_data.path.segments.iter().last() { + if let Some(segment) = import_data.path.segments.iter().last() { if !import_data.is_glob { module_items.items.insert( - name.clone(), + segment.name.clone(), Resolution { def_id: PerNs::none(), import: Some(import_id), @@ -319,13 +319,13 @@ where PathKind::Crate => module_id.crate_root(&self.module_tree), }; - for (i, name) in import.path.segments.iter().enumerate() { + for (i, segment) in import.path.segments.iter().enumerate() { let is_last = i == import.path.segments.len() - 1; - let def_id = match self.result.per_module[&curr].items.get(name) { + let def_id = match self.result.per_module[&curr].items.get(&segment.name) { Some(res) if !res.def_id.is_none() => res.def_id, _ => { - log::debug!("path segment {:?} not found", name); + log::debug!("path segment {:?} not found", segment.name); return false; } }; @@ -336,7 +336,7 @@ where } else { log::debug!( "path segment {:?} resolved to value only, but is not last", - name + segment.name ); return false; }; @@ -358,17 +358,17 @@ where log::debug!("resolving {:?} in other source root", path); let def_id = module.resolve_path(self.db, &path); if !def_id.is_none() { - let name = path.segments.last().unwrap(); + let last_segment = path.segments.last().unwrap(); self.update(module_id, |items| { let res = Resolution { def_id, import: Some(import_id), }; - items.items.insert(name.clone(), res); + items.items.insert(last_segment.name.clone(), res); }); log::debug!( "resolved import {:?} ({:?}) cross-source root to {:?}", - name, + last_segment.name, import, def_id.map(|did| did.loc(self.db)) ); @@ -382,7 +382,7 @@ where _ => { log::debug!( "path segment {:?} resolved to non-module {:?}, but is not last", - name, + segment.name, type_def_id.loc(self.db) ); return true; // this resolved to a non-module, so the path won't ever resolve @@ -391,7 +391,7 @@ where } else { log::debug!( "resolved import {:?} ({:?}) within source root to {:?}", - name, + segment.name, import, def_id.map(|did| did.loc(self.db)) ); @@ -400,7 +400,7 @@ where def_id, import: Some(import_id), }; - items.items.insert(name.clone(), res); + items.items.insert(segment.name.clone(), res); }) } } diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 7b0ce3b61..c3d14c689 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -1,11 +1,35 @@ +use std::sync::Arc; + use ra_syntax::{ast, AstNode}; -use crate::{Name, AsName}; +use crate::{Name, AsName, type_ref::TypeRef}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Path { pub kind: PathKind, - pub segments: Vec, + pub segments: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PathSegment { + pub name: Name, + pub args_and_bindings: Option>, +} + +/// Generic arguments to a path segment (e.g. the `i32` in `Option`). This +/// can (in the future) also include bindings of associated types, like in +/// `Iterator`. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GenericArgs { + pub args: Vec, + // someday also bindings +} + +/// A single generic argument. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum GenericArg { + Type(TypeRef), + // or lifetime... } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -34,7 +58,17 @@ impl Path { loop { let segment = path.segment()?; match segment.kind()? { - ast::PathSegmentKind::Name(name) => segments.push(name.as_name()), + ast::PathSegmentKind::Name(name) => { + let args = segment + .type_arg_list() + .and_then(GenericArgs::from_ast) + .map(Arc::new); + let segment = PathSegment { + name: name.as_name(), + args_and_bindings: args, + }; + segments.push(segment); + } ast::PathSegmentKind::CrateKw => { kind = PathKind::Crate; break; @@ -88,7 +122,23 @@ impl Path { if self.kind != PathKind::Plain || self.segments.len() > 1 { return None; } - self.segments.first() + self.segments.first().map(|s| &s.name) + } +} + +impl GenericArgs { + fn from_ast(node: &ast::TypeArgList) -> Option { + let mut args = Vec::new(); + for type_arg in node.type_args() { + let type_ref = TypeRef::from_ast_opt(type_arg.type_ref()); + args.push(GenericArg::Type(type_ref)); + } + // lifetimes and assoc type args ignored for now + if args.len() > 0 { + Some(GenericArgs { args }) + } else { + None + } } } @@ -96,7 +146,10 @@ impl From for Path { fn from(name: Name) -> Path { Path { kind: PathKind::Plain, - segments: vec![name], + segments: vec![PathSegment { + name, + args_and_bindings: None, + }], } } } @@ -160,7 +213,10 @@ fn convert_path(prefix: Option, path: &ast::Path) -> Option { kind: PathKind::Plain, segments: Vec::with_capacity(1), }); - res.segments.push(name.as_name()); + res.segments.push(PathSegment { + name: name.as_name(), + args_and_bindings: None, // no type args in use + }); res } ast::PathSegmentKind::CrateKw => { -- cgit v1.2.3 From fa7f9d696f5863dbf28b3d0ef14c1bc9143b2d21 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 13 Jan 2019 11:58:41 +0100 Subject: Make Module impl methods crate-private, update some comments --- crates/ra_hir/src/code_model_impl/module.rs | 15 +++++++++------ crates/ra_hir/src/ids.rs | 9 +++++++++ crates/ra_hir/src/ty.rs | 7 +++++-- 3 files changed, 23 insertions(+), 8 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs index 2014f1090..73c212de8 100644 --- a/crates/ra_hir/src/code_model_impl/module.rs +++ b/crates/ra_hir/src/code_model_impl/module.rs @@ -95,7 +95,7 @@ impl Module { } /// Finds a child module with the specified name. - pub fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Option { + pub(crate) fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Option { let loc = self.def_id.loc(db); let module_tree = db.module_tree(loc.source_root_id); let child_id = loc.module_id.child(&module_tree, name)?; @@ -103,7 +103,7 @@ impl Module { } /// Iterates over all child modules. - pub fn children_impl(&self, db: &impl HirDatabase) -> impl Iterator { + pub(crate) fn children_impl(&self, db: &impl HirDatabase) -> impl Iterator { // FIXME this should be implementable without collecting into a vec, but // it's kind of hard since the iterator needs to keep a reference to the // module tree. @@ -117,7 +117,7 @@ impl Module { children.into_iter() } - pub fn parent_impl(&self, db: &impl HirDatabase) -> Option { + pub(crate) fn parent_impl(&self, db: &impl HirDatabase) -> Option { let loc = self.def_id.loc(db); let module_tree = db.module_tree(loc.source_root_id); let parent_id = loc.module_id.parent(&module_tree)?; @@ -125,13 +125,13 @@ impl Module { } /// Returns a `ModuleScope`: a set of items, visible in this module. - pub fn scope_impl(&self, db: &impl HirDatabase) -> ModuleScope { + pub(crate) fn scope_impl(&self, db: &impl HirDatabase) -> ModuleScope { let loc = self.def_id.loc(db); let item_map = db.item_map(loc.source_root_id); item_map.per_module[&loc.module_id].clone() } - pub fn resolve_path_impl(&self, db: &impl HirDatabase, path: &Path) -> PerNs { + pub(crate) fn resolve_path_impl(&self, db: &impl HirDatabase, path: &Path) -> PerNs { let mut curr_per_ns = PerNs::types( match path.kind { PathKind::Crate => self.crate_root(db), @@ -191,7 +191,10 @@ impl Module { curr_per_ns } - pub fn problems_impl(&self, db: &impl HirDatabase) -> Vec<(TreeArc, Problem)> { + pub(crate) fn problems_impl( + &self, + db: &impl HirDatabase, + ) -> Vec<(TreeArc, Problem)> { let loc = self.def_id.loc(db); let module_tree = db.module_tree(loc.source_root_id); loc.module_id.problems(&module_tree, db) diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 0d8e67547..c5408e277 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -151,6 +151,15 @@ pub(crate) enum DefKind { Type, Item, + /// The constructor of a struct. E.g. if we have `struct Foo(usize)`, the + /// name `Foo` needs to resolve to different types depending on whether we + /// are in the types or values namespace: As a type, `Foo` of course refers + /// to the struct `Foo`; as a value, `Foo` is a callable type with signature + /// `(usize) -> Foo`. The cleanest approach to handle this seems to be to + /// have different defs in the two namespaces. + /// + /// rustc does the same; note that it even creates a struct constructor if + /// the struct isn't a tuple struct (see `CtorKind::Fictive` in rustc). StructCtor, } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index b9a48929d..8e93a4457 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -242,7 +242,8 @@ pub enum Ty { // Opaque(DefId, Substs), /// A type parameter; for example, `T` in `fn f(x: T) {} Param { - /// The index of the parameter. + /// The index of the parameter (starting with parameters from the + /// surrounding impl, then the current function). idx: u32, /// The name of the parameter, for displaying. name: Name, @@ -440,7 +441,9 @@ impl Ty { if (idx as usize) < substs.0.len() { substs.0[idx as usize].clone() } else { - // TODO it's yet unclear to me whether we need to shift the indices here + // TODO: does this indicate a bug? i.e. should we always + // have substs for all type params? (they might contain the + // params themselves again...) Ty::Param { idx, name } } } -- cgit v1.2.3 From cc4562ab6ecfced33f5d7a2e428c8caca8f9c3f1 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 13 Jan 2019 21:00:31 +0100 Subject: Collect generic args in type paths E.g. `let x: A` is handled correctly. --- crates/ra_hir/src/ty.rs | 102 ++++++++++++++++++++- .../ra_hir/src/ty/tests/data/struct_generics.txt | 6 +- 2 files changed, 100 insertions(+), 8 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 8e93a4457..fcde918fb 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -38,6 +38,7 @@ use crate::{ name::KnownName, expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, generics::Generics, + path::GenericArg, }; /// The ID of a type variable. @@ -156,6 +157,12 @@ impl Expectation { #[derive(Clone, PartialEq, Eq, Debug)] pub struct Substs(Arc<[Ty]>); +impl Substs { + pub fn empty() -> Substs { + Substs(Arc::new([])) + } +} + /// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs). /// /// This should be cheap to clone. @@ -271,6 +278,9 @@ pub struct FnSig { impl Ty { pub(crate) fn from_hir( db: &impl HirDatabase, + // TODO: the next three parameters basically describe the scope for name + // resolution; this should be refactored into something like a general + // resolver architecture module: &Module, impl_block: Option<&ImplBlock>, generics: &Generics, @@ -371,12 +381,79 @@ impl Ty { } // Resolve in module (in type namespace) - let resolved = if let Some(r) = module.resolve_path(db, path).take_types() { - r - } else { - return Ty::Unknown; + let resolved = match module.resolve_path(db, path).take_types() { + Some(r) => r, + None => return Ty::Unknown, + }; + let ty = db.type_for_def(resolved); + let substs = Ty::substs_from_path(db, module, impl_block, generics, path, resolved); + ty.apply_substs(substs) + } + + /// Collect generic arguments from a path into a `Substs`. See also + /// `create_substs_for_ast_path` and `def_to_ty` in rustc. + fn substs_from_path( + db: &impl HirDatabase, + // the scope of the segment... + module: &Module, + impl_block: Option<&ImplBlock>, + outer_generics: &Generics, + path: &Path, + resolved: DefId, + ) -> Substs { + let mut substs = Vec::new(); + let def = resolved.resolve(db); + let last = path + .segments + .last() + .expect("path should have at least one segment"); + let (def_generics, segment) = match def { + Def::Struct(s) => (s.generics(db), last), + Def::Enum(e) => (e.generics(db), last), + Def::Function(f) => (f.generics(db), last), + Def::Trait(t) => (t.generics(db), last), + Def::EnumVariant(ev) => { + // the generic args for an enum variant may be either specified + // on the segment referring to the enum, or on the segment + // referring to the variant. So `Option::::None` and + // `Option::None::` are both allowed (though the former is + // preferred). See also `def_ids_for_path_segments` in rustc. + let len = path.segments.len(); + let segment = if len >= 2 && path.segments[len - 2].args_and_bindings.is_some() { + // Option::::None + &path.segments[len - 2] + } else { + // Option::None:: + last + }; + (ev.parent_enum(db).generics(db), segment) + } + _ => return Substs::empty(), }; - db.type_for_def(resolved) + // substs_from_path + if let Some(generic_args) = &segment.args_and_bindings { + // if args are provided, it should be all of them, but we can't rely on that + let param_count = def_generics.params.len(); + for arg in generic_args.args.iter().take(param_count) { + match arg { + GenericArg::Type(type_ref) => { + let ty = Ty::from_hir(db, module, impl_block, outer_generics, type_ref); + substs.push(ty); + } + } + } + } + // add placeholders for args that were not provided + // TODO: handle defaults + for _ in segment + .args_and_bindings + .as_ref() + .map(|ga| ga.args.len()) + .unwrap_or(0)..def_generics.params.len() + { + substs.push(Ty::Unknown); + } + Substs(substs.into()) } pub fn unit() -> Self { @@ -432,6 +509,21 @@ impl Ty { } } + /// If this is a type with type parameters (an ADT or function), replaces + /// the `Substs` for these type parameters with the given ones. (So e.g. if + /// `self` is `Option<_>` and the substs contain `u32`, we'll have + /// `Option` afterwards.) + pub fn apply_substs(self, substs: Substs) -> Ty { + match self { + Ty::Adt { def_id, name, .. } => Ty::Adt { + def_id, + name, + substs, + }, + _ => 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.) diff --git a/crates/ra_hir/src/ty/tests/data/struct_generics.txt b/crates/ra_hir/src/ty/tests/data/struct_generics.txt index 88cd2b409..c6be2a5f5 100644 --- a/crates/ra_hir/src/ty/tests/data/struct_generics.txt +++ b/crates/ra_hir/src/ty/tests/data/struct_generics.txt @@ -1,8 +1,8 @@ -[36; 38) 'a1': A<[unknown]> +[36; 38) 'a1': A [48; 49) 'i': i32 [56; 147) '{ ...3.x; }': () -[62; 64) 'a1': A<[unknown]> -[62; 66) 'a1.x': [unknown] +[62; 64) 'a1': A +[62; 66) 'a1.x': u32 [76; 78) 'a2': A [81; 91) 'A { x: i }': A [88; 89) 'i': i32 -- cgit v1.2.3 From d37bb128effd19e3aec347e3d4f2e27b5cdb9404 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 13 Jan 2019 21:12:36 +0100 Subject: Collect generic args in struct variant paths as well --- crates/ra_hir/src/ty.rs | 29 ++++++++++++++-------- .../ra_hir/src/ty/tests/data/struct_generics.txt | 10 ++++---- 2 files changed, 24 insertions(+), 15 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index fcde918fb..1d2d1b906 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -1054,25 +1054,34 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option) { - let path = if let Some(path) = path { - path - } else { - return (Ty::Unknown, None); + let path = match path { + Some(path) => path, + None => return (Ty::Unknown, None), }; - let def_id = if let Some(def_id) = self.module.resolve_path(self.db, &path).take_types() { - def_id - } else { - return (Ty::Unknown, None); + let def_id = match self.module.resolve_path(self.db, &path).take_types() { + Some(def_id) => def_id, + _ => return (Ty::Unknown, None), }; + // TODO remove the duplication between here and `Ty::from_path`? + // TODO provide generics of function + let generics = Generics::default(); + let substs = Ty::substs_from_path( + self.db, + &self.module, + self.impl_block.as_ref(), + &generics, + path, + def_id, + ); match def_id.resolve(self.db) { Def::Struct(s) => { let ty = type_for_struct(self.db, s); - let ty = self.insert_type_vars(ty); + let ty = self.insert_type_vars(ty.apply_substs(substs)); (ty, Some(def_id)) } Def::EnumVariant(ev) => { let ty = type_for_enum_variant(self.db, ev); - let ty = self.insert_type_vars(ty); + let ty = self.insert_type_vars(ty.apply_substs(substs)); (ty, Some(def_id)) } _ => (Ty::Unknown, None), diff --git a/crates/ra_hir/src/ty/tests/data/struct_generics.txt b/crates/ra_hir/src/ty/tests/data/struct_generics.txt index c6be2a5f5..1024a39a9 100644 --- a/crates/ra_hir/src/ty/tests/data/struct_generics.txt +++ b/crates/ra_hir/src/ty/tests/data/struct_generics.txt @@ -8,8 +8,8 @@ [88; 89) 'i': i32 [97; 99) 'a2': A [97; 101) 'a2.x': i32 -[111; 113) 'a3': A -[116; 134) 'A:: -[131; 132) '1': i32 -[140; 142) 'a3': A -[140; 144) 'a3.x': i32 +[111; 113) 'a3': A +[116; 134) 'A:: +[131; 132) '1': i128 +[140; 142) 'a3': A +[140; 144) 'a3.x': i128 -- cgit v1.2.3 From 9e4b5ecec4fa4f6a20bb4d47f09de602e9c29608 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 19 Jan 2019 15:48:55 +0100 Subject: Make generics work in struct patterns --- crates/ra_hir/src/ty.rs | 61 ++++++++++++++++------ crates/ra_hir/src/ty/tests.rs | 26 +++++++++ .../src/ty/tests/data/generics_in_patterns.txt | 17 ++++++ 3 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 crates/ra_hir/src/ty/tests/data/generics_in_patterns.txt (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 1d2d1b906..3608daae4 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -683,9 +683,9 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Ty { pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Option { let def = def_id.resolve(db); - let variant_data = match def { - Def::Struct(s) => s.variant_data(db), - Def::EnumVariant(ev) => ev.variant_data(db), + let (variant_data, generics) = match def { + Def::Struct(s) => (s.variant_data(db), s.generics(db)), + Def::EnumVariant(ev) => (ev.variant_data(db), ev.parent_enum(db).generics(db)), // TODO: unions _ => panic!( "trying to get type for field in non-struct/variant {:?}", @@ -694,7 +694,6 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) }; let module = def_id.module(db); let impl_block = def_id.impl_block(db); - let generics = db.generics(def_id); let type_ref = variant_data.get_field_type_ref(&field)?; Some(Ty::from_hir( db, @@ -893,6 +892,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { ty } + fn unify_substs(&mut self, substs1: &Substs, substs2: &Substs) -> bool { + substs1 + .0 + .iter() + .zip(substs2.0.iter()) + .all(|(t1, t2)| self.unify(t1, t2)) + } + fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { // try to resolve type vars first let ty1 = self.resolve_ty_shallow(ty1); @@ -913,12 +920,16 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { (Ty::Bool, _) | (Ty::Str, _) | (Ty::Never, _) | (Ty::Char, _) => ty1 == ty2, ( Ty::Adt { - def_id: def_id1, .. + def_id: def_id1, + substs: substs1, + .. }, Ty::Adt { - def_id: def_id2, .. + def_id: def_id2, + substs: substs2, + .. }, - ) if def_id1 == def_id2 => true, + ) if def_id1 == def_id2 => self.unify_substs(substs1, substs2), (Ty::Slice(t1), Ty::Slice(t2)) => self.unify(t1, t2), (Ty::RawPtr(t1, m1), Ty::RawPtr(t2, m2)) if m1 == m2 => self.unify(t1, t2), (Ty::Ref(t1, m1), Ty::Ref(t2, m2)) if m1 == m2 => self.unify(t1, t2), @@ -1088,49 +1099,65 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } - fn resolve_fields(&self, path: Option<&Path>) -> Option<(Ty, Vec)> { - let def_id = self.module.resolve_path(self.db, path?).take_types()?; + fn resolve_fields(&mut self, path: Option<&Path>) -> Option<(Ty, Vec)> { + let (ty, def_id) = self.resolve_variant(path); + let def_id = def_id?; let def = def_id.resolve(self.db); match def { Def::Struct(s) => { let fields = s.fields(self.db); - Some((type_for_struct(self.db, s), fields)) + Some((ty, fields)) } Def::EnumVariant(ev) => { let fields = ev.fields(self.db); - Some((type_for_enum_variant(self.db, ev), fields)) + Some((ty, fields)) } _ => None, } } - fn infer_tuple_struct_pat(&mut self, path: Option<&Path>, subpats: &[PatId]) -> Ty { + fn infer_tuple_struct_pat( + &mut self, + path: Option<&Path>, + subpats: &[PatId], + expected: &Ty, + ) -> Ty { let (ty, fields) = self .resolve_fields(path) .unwrap_or((Ty::Unknown, Vec::new())); + self.unify(&ty, expected); + + let substs = ty.substs().expect("adt should have substs"); + for (i, &subpat) in subpats.iter().enumerate() { let expected_ty = fields .get(i) .and_then(|field| field.ty(self.db)) - .unwrap_or(Ty::Unknown); + .unwrap_or(Ty::Unknown) + .subst(&substs); self.infer_pat(subpat, &expected_ty); } ty } - fn infer_struct_pat(&mut self, path: Option<&Path>, subpats: &[FieldPat]) -> Ty { + fn infer_struct_pat(&mut self, path: Option<&Path>, subpats: &[FieldPat], expected: &Ty) -> Ty { let (ty, fields) = self .resolve_fields(path) .unwrap_or((Ty::Unknown, Vec::new())); + self.unify(&ty, expected); + + let substs = ty.substs().expect("adt should have substs"); + for subpat in subpats { let matching_field = fields.iter().find(|field| field.name() == &subpat.name); let expected_ty = matching_field .and_then(|field| field.ty(self.db)) - .unwrap_or(Ty::Unknown); + .unwrap_or(Ty::Unknown) + .subst(&substs); self.infer_pat(subpat.pat, &expected_ty); } @@ -1175,11 +1202,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Pat::TupleStruct { path: ref p, args: ref subpats, - } => self.infer_tuple_struct_pat(p.as_ref(), subpats), + } => self.infer_tuple_struct_pat(p.as_ref(), subpats, expected), Pat::Struct { path: ref p, args: ref fields, - } => self.infer_struct_pat(p.as_ref(), fields), + } => self.infer_struct_pat(p.as_ref(), fields, expected), Pat::Path(path) => self .module .resolve_path(self.db, &path) diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index c590a09db..06e32df59 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -438,6 +438,32 @@ fn test(a1: A, i: i32) { ); } +#[test] +fn infer_generics_in_patterns() { + check_inference( + r#" +struct A { + x: T, +} + +enum Option { + Some(T), + None, +} + +fn test(a1: A, o: Option) { + let A { x: x2 } = a1; + let A:: { x: x3 } = A { x: 1 }; + match o { + Option::Some(t) => t, + _ => 1, + }; +} +"#, + "generics_in_patterns.txt", + ); +} + #[test] fn infer_function_generics() { check_inference( diff --git a/crates/ra_hir/src/ty/tests/data/generics_in_patterns.txt b/crates/ra_hir/src/ty/tests/data/generics_in_patterns.txt new file mode 100644 index 000000000..1b01ef19e --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/generics_in_patterns.txt @@ -0,0 +1,17 @@ +[79; 81) 'a1': A +[91; 92) 'o': Option +[107; 244) '{ ... }; }': () +[117; 128) 'A { x: x2 }': A +[124; 126) 'x2': u32 +[131; 133) 'a1': A +[143; 161) 'A:: +[157; 159) 'x3': i64 +[164; 174) 'A { x: 1 }': A +[171; 172) '1': i64 +[180; 241) 'match ... }': u64 +[186; 187) 'o': Option +[198; 213) 'Option::Some(t)': Option +[211; 212) 't': u64 +[217; 218) 't': u64 +[228; 229) '_': Option +[233; 234) '1': u64 -- cgit v1.2.3 From 969f588025ab2c3224757e282ef78a546c4f56c4 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 19 Jan 2019 18:58:04 +0100 Subject: Generics -> GenericParams --- crates/ra_hir/src/code_model_api.rs | 22 +++++++++--------- crates/ra_hir/src/db.rs | 6 ++--- crates/ra_hir/src/generics.rs | 8 +++---- crates/ra_hir/src/mock.rs | 2 +- crates/ra_hir/src/ty.rs | 38 +++++++++++++++---------------- crates/ra_hir/src/ty/method_resolution.rs | 4 ++-- 6 files changed, 40 insertions(+), 40 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 6c85e5939..88eda5ed0 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -12,7 +12,7 @@ use crate::{ expr::BodySyntaxMapping, ty::InferenceResult, adt::VariantData, - generics::Generics, + generics::GenericParams, code_model_impl::def_id_to_ast, }; @@ -203,8 +203,8 @@ impl Struct { def_id_to_ast(db, self.def_id) } - pub fn generics(&self, db: &impl HirDatabase) -> Arc { - db.generics(self.def_id) + pub fn generic_params(&self, db: &impl HirDatabase) -> Arc { + db.generic_params(self.def_id) } } @@ -234,8 +234,8 @@ impl Enum { def_id_to_ast(db, self.def_id) } - pub fn generics(&self, db: &impl HirDatabase) -> Arc { - db.generics(self.def_id) + pub fn generic_params(&self, db: &impl HirDatabase) -> Arc { + db.generic_params(self.def_id) } } @@ -349,8 +349,8 @@ impl Function { db.infer(self.def_id) } - pub fn generics(&self, db: &impl HirDatabase) -> Arc { - db.generics(self.def_id) + pub fn generic_params(&self, db: &impl HirDatabase) -> Arc { + db.generic_params(self.def_id) } } @@ -398,8 +398,8 @@ impl Trait { def_id_to_ast(db, self.def_id) } - pub fn generics(&self, db: &impl HirDatabase) -> Arc { - db.generics(self.def_id) + pub fn generic_params(&self, db: &impl HirDatabase) -> Arc { + db.generic_params(self.def_id) } } @@ -417,7 +417,7 @@ impl Type { def_id_to_ast(db, self.def_id) } - pub fn generics(&self, db: &impl HirDatabase) -> Arc { - db.generics(self.def_id) + pub fn generic_params(&self, db: &impl HirDatabase) -> Arc { + db.generic_params(self.def_id) } } diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 10e64f880..f383701d7 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -14,7 +14,7 @@ use crate::{ ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks}, adt::{StructData, EnumData, EnumVariantData}, impl_block::ModuleImplBlocks, - generics::Generics, + generics::GenericParams, }; #[salsa::query_group] @@ -102,8 +102,8 @@ pub trait HirDatabase: #[salsa::invoke(crate::expr::body_syntax_mapping)] fn body_syntax_mapping(&self, def_id: DefId) -> Arc; - #[salsa::invoke(crate::generics::Generics::generics_query)] - fn generics(&self, def_id: DefId) -> Arc; + #[salsa::invoke(crate::generics::GenericParams::generic_params_query)] + fn generic_params(&self, def_id: DefId) -> Arc; #[salsa::invoke(crate::FnSignature::fn_signature_query)] fn fn_signature(&self, def_id: DefId) -> Arc; diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index cdabc0424..d8248ad49 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -18,14 +18,14 @@ pub struct GenericParam { /// Data about the generic parameters of a function, struct, impl, etc. #[derive(Clone, PartialEq, Eq, Debug, Default)] -pub struct Generics { +pub struct GenericParams { pub(crate) params: Vec, } -impl Generics { - pub(crate) fn generics_query(db: &impl HirDatabase, def_id: DefId) -> Arc { +impl GenericParams { + pub(crate) fn generic_params_query(db: &impl HirDatabase, def_id: DefId) -> Arc { let (_file_id, node) = def_id.source(db); - let mut generics = Generics::default(); + let mut generics = GenericParams::default(); if let Some(type_param_list) = node.children().find_map(TypeParamList::cast) { for (idx, type_param) in type_param_list.type_params().enumerate() { let name = type_param diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index d674db8e6..6e6f2f04b 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -243,7 +243,7 @@ salsa::database_storage! { fn body_hir() for db::BodyHirQuery; fn body_syntax_mapping() for db::BodySyntaxMappingQuery; fn fn_signature() for db::FnSignatureQuery; - fn generics() for db::GenericsQuery; + fn generic_params() for db::GenericParamsQuery; } } } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 3608daae4..3170ed24e 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -37,7 +37,7 @@ use crate::{ type_ref::{TypeRef, Mutability}, name::KnownName, expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, - generics::Generics, + generics::GenericParams, path::GenericArg, }; @@ -283,7 +283,7 @@ impl Ty { // resolver architecture module: &Module, impl_block: Option<&ImplBlock>, - generics: &Generics, + generics: &GenericParams, type_ref: &TypeRef, ) -> Self { match type_ref { @@ -335,7 +335,7 @@ impl Ty { db: &impl HirDatabase, module: &Module, impl_block: Option<&ImplBlock>, - generics: &Generics, + generics: &GenericParams, type_ref: Option<&TypeRef>, ) -> Self { type_ref.map_or(Ty::Unknown, |t| { @@ -347,7 +347,7 @@ impl Ty { db: &impl HirDatabase, module: &Module, impl_block: Option<&ImplBlock>, - generics: &Generics, + generics: &GenericParams, path: &Path, ) -> Self { if let Some(name) = path.as_ident() { @@ -357,7 +357,7 @@ impl Ty { return Ty::Float(float_ty); } else if name.as_known_name() == Some(KnownName::SelfType) { // TODO pass the impl block's generics? - let generics = &Generics::default(); + let generics = &GenericParams::default(); return Ty::from_hir_opt( db, module, @@ -397,7 +397,7 @@ impl Ty { // the scope of the segment... module: &Module, impl_block: Option<&ImplBlock>, - outer_generics: &Generics, + outer_generics: &GenericParams, path: &Path, resolved: DefId, ) -> Substs { @@ -408,10 +408,10 @@ impl Ty { .last() .expect("path should have at least one segment"); let (def_generics, segment) = match def { - Def::Struct(s) => (s.generics(db), last), - Def::Enum(e) => (e.generics(db), last), - Def::Function(f) => (f.generics(db), last), - Def::Trait(t) => (t.generics(db), last), + Def::Struct(s) => (s.generic_params(db), last), + Def::Enum(e) => (e.generic_params(db), last), + Def::Function(f) => (f.generic_params(db), last), + Def::Trait(t) => (t.generic_params(db), last), Def::EnumVariant(ev) => { // the generic args for an enum variant may be either specified // on the segment referring to the enum, or on the segment @@ -426,7 +426,7 @@ impl Ty { // Option::None:: last }; - (ev.parent_enum(db).generics(db), segment) + (ev.parent_enum(db).generic_params(db), segment) } _ => return Substs::empty(), }; @@ -607,7 +607,7 @@ fn type_for_fn(db: &impl HirDatabase, f: Function) -> Ty { let signature = f.signature(db); let module = f.module(db); let impl_block = f.impl_block(db); - let generics = f.generics(db); + let generics = f.generic_params(db); let input = signature .params() .iter() @@ -624,7 +624,7 @@ fn type_for_fn(db: &impl HirDatabase, f: Function) -> Ty { Ty::FnPtr(Arc::new(sig)) } -fn make_substs(generics: &Generics) -> Substs { +fn make_substs(generics: &GenericParams) -> Substs { Substs( generics .params @@ -636,7 +636,7 @@ fn make_substs(generics: &Generics) -> Substs { } fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty { - let generics = s.generics(db); + let generics = s.generic_params(db); Ty::Adt { def_id: s.def_id(), name: s.name(db).unwrap_or_else(Name::missing), @@ -645,7 +645,7 @@ fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty { } pub(crate) fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Ty { - let generics = s.generics(db); + let generics = s.generic_params(db); Ty::Adt { def_id: s.def_id(), name: s.name(db).unwrap_or_else(Name::missing), @@ -684,8 +684,8 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Ty { pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Option { let def = def_id.resolve(db); let (variant_data, generics) = match def { - Def::Struct(s) => (s.variant_data(db), s.generics(db)), - Def::EnumVariant(ev) => (ev.variant_data(db), ev.parent_enum(db).generics(db)), + Def::Struct(s) => (s.variant_data(db), s.generic_params(db)), + Def::EnumVariant(ev) => (ev.variant_data(db), ev.parent_enum(db).generic_params(db)), // TODO: unions _ => panic!( "trying to get type for field in non-struct/variant {:?}", @@ -880,7 +880,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { fn make_ty(&mut self, type_ref: &TypeRef) -> Ty { // TODO provide generics of function - let generics = Generics::default(); + let generics = GenericParams::default(); let ty = Ty::from_hir( self.db, &self.module, @@ -1075,7 +1075,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }; // TODO remove the duplication between here and `Ty::from_path`? // TODO provide generics of function - let generics = Generics::default(); + let generics = GenericParams::default(); let substs = Ty::substs_from_path( self.db, &self.module, diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 53b6d15a8..9f65c5fe1 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -11,7 +11,7 @@ use ra_db::SourceRootId; use crate::{ HirDatabase, DefId, module_tree::ModuleId, Module, Crate, Name, Function, impl_block::{ImplId, ImplBlock, ImplItem}, - generics::Generics + generics::GenericParams }; use super::Ty; @@ -69,7 +69,7 @@ impl CrateImplBlocks { // ignore for now } else { // TODO provide generics of impl - let generics = Generics::default(); + let generics = GenericParams::default(); let target_ty = Ty::from_hir( db, &module, -- cgit v1.2.3 From 5f3509e140d19b989db418a00ac6778c622cde5d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 19 Jan 2019 19:00:36 +0100 Subject: Add an assert --- crates/ra_hir/src/ty.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 3170ed24e..9b7182485 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -453,6 +453,7 @@ impl Ty { { substs.push(Ty::Unknown); } + assert_eq!(substs.len(), def_generics.params.len()); Substs(substs.into()) } -- cgit v1.2.3