mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-16 09:48:10 +00:00
Merge #576
576: Beginnings of generics r=matklad a=flodiebold This implements the beginnings of the generics infrastructure; generic parameters for structs work and are correctly substituted in fields. Functions and methods aren't handled at all yet (as the tests show). The name resolution in `ty` really needs refactoring now, I hope to do that next ;) Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
1c296d54e3
21 changed files with 805 additions and 97 deletions
|
@ -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 std::sync::Arc;
|
||||||
|
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
|
@ -62,7 +65,7 @@ fn get_def_id(
|
||||||
};
|
};
|
||||||
let loc = DefLoc {
|
let loc = DefLoc {
|
||||||
kind: expected_kind,
|
kind: expected_kind,
|
||||||
source_item_id: source_item_id,
|
source_item_id,
|
||||||
..*same_file_loc
|
..*same_file_loc
|
||||||
};
|
};
|
||||||
loc.id(db)
|
loc.id(db)
|
||||||
|
|
|
@ -12,6 +12,7 @@ use crate::{
|
||||||
expr::BodySyntaxMapping,
|
expr::BodySyntaxMapping,
|
||||||
ty::InferenceResult,
|
ty::InferenceResult,
|
||||||
adt::VariantData,
|
adt::VariantData,
|
||||||
|
generics::GenericParams,
|
||||||
code_model_impl::def_id_to_ast,
|
code_model_impl::def_id_to_ast,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -201,6 +202,10 @@ impl Struct {
|
||||||
pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<ast::StructDef>) {
|
pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<ast::StructDef>) {
|
||||||
def_id_to_ast(db, self.def_id)
|
def_id_to_ast(db, self.def_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn generic_params(&self, db: &impl HirDatabase) -> Arc<GenericParams> {
|
||||||
|
db.generic_params(self.def_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -228,6 +233,10 @@ impl Enum {
|
||||||
pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<ast::EnumDef>) {
|
pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<ast::EnumDef>) {
|
||||||
def_id_to_ast(db, self.def_id)
|
def_id_to_ast(db, self.def_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn generic_params(&self, db: &impl HirDatabase) -> Arc<GenericParams> {
|
||||||
|
db.generic_params(self.def_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -339,6 +348,10 @@ impl Function {
|
||||||
pub fn infer(&self, db: &impl HirDatabase) -> Arc<InferenceResult> {
|
pub fn infer(&self, db: &impl HirDatabase) -> Arc<InferenceResult> {
|
||||||
db.infer(self.def_id)
|
db.infer(self.def_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn generic_params(&self, db: &impl HirDatabase) -> Arc<GenericParams> {
|
||||||
|
db.generic_params(self.def_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -384,6 +397,10 @@ impl Trait {
|
||||||
pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<ast::TraitDef>) {
|
pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<ast::TraitDef>) {
|
||||||
def_id_to_ast(db, self.def_id)
|
def_id_to_ast(db, self.def_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn generic_params(&self, db: &impl HirDatabase) -> Arc<GenericParams> {
|
||||||
|
db.generic_params(self.def_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -399,4 +416,8 @@ impl Type {
|
||||||
pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<ast::TypeDef>) {
|
pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<ast::TypeDef>) {
|
||||||
def_id_to_ast(db, self.def_id)
|
def_id_to_ast(db, self.def_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn generic_params(&self, db: &impl HirDatabase) -> Arc<GenericParams> {
|
||||||
|
db.generic_params(self.def_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ impl Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds a child module with the specified name.
|
/// Finds a child module with the specified name.
|
||||||
pub fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Option<Module> {
|
pub(crate) fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Option<Module> {
|
||||||
let loc = self.def_id.loc(db);
|
let loc = self.def_id.loc(db);
|
||||||
let module_tree = db.module_tree(loc.source_root_id);
|
let module_tree = db.module_tree(loc.source_root_id);
|
||||||
let child_id = loc.module_id.child(&module_tree, name)?;
|
let child_id = loc.module_id.child(&module_tree, name)?;
|
||||||
|
@ -103,7 +103,7 @@ impl Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterates over all child modules.
|
/// Iterates over all child modules.
|
||||||
pub fn children_impl(&self, db: &impl HirDatabase) -> impl Iterator<Item = Module> {
|
pub(crate) fn children_impl(&self, db: &impl HirDatabase) -> impl Iterator<Item = Module> {
|
||||||
// FIXME this should be implementable without collecting into a vec, but
|
// 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
|
// it's kind of hard since the iterator needs to keep a reference to the
|
||||||
// module tree.
|
// module tree.
|
||||||
|
@ -117,7 +117,7 @@ impl Module {
|
||||||
children.into_iter()
|
children.into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parent_impl(&self, db: &impl HirDatabase) -> Option<Module> {
|
pub(crate) fn parent_impl(&self, db: &impl HirDatabase) -> Option<Module> {
|
||||||
let loc = self.def_id.loc(db);
|
let loc = self.def_id.loc(db);
|
||||||
let module_tree = db.module_tree(loc.source_root_id);
|
let module_tree = db.module_tree(loc.source_root_id);
|
||||||
let parent_id = loc.module_id.parent(&module_tree)?;
|
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.
|
/// 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 loc = self.def_id.loc(db);
|
||||||
let item_map = db.item_map(loc.source_root_id);
|
let item_map = db.item_map(loc.source_root_id);
|
||||||
item_map.per_module[&loc.module_id].clone()
|
item_map.per_module[&loc.module_id].clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_path_impl(&self, db: &impl HirDatabase, path: &Path) -> PerNs<DefId> {
|
pub(crate) fn resolve_path_impl(&self, db: &impl HirDatabase, path: &Path) -> PerNs<DefId> {
|
||||||
let mut curr_per_ns = PerNs::types(
|
let mut curr_per_ns = PerNs::types(
|
||||||
match path.kind {
|
match path.kind {
|
||||||
PathKind::Crate => self.crate_root(db),
|
PathKind::Crate => self.crate_root(db),
|
||||||
|
@ -147,7 +147,7 @@ impl Module {
|
||||||
.def_id,
|
.def_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
for name in path.segments.iter() {
|
for segment in path.segments.iter() {
|
||||||
let curr = match curr_per_ns.as_ref().take_types() {
|
let curr = match curr_per_ns.as_ref().take_types() {
|
||||||
Some(r) => r,
|
Some(r) => r,
|
||||||
None => {
|
None => {
|
||||||
|
@ -163,15 +163,17 @@ impl Module {
|
||||||
curr_per_ns = match curr.resolve(db) {
|
curr_per_ns = match curr.resolve(db) {
|
||||||
Def::Module(m) => {
|
Def::Module(m) => {
|
||||||
let scope = m.scope(db);
|
let scope = m.scope(db);
|
||||||
match scope.get(&name) {
|
match scope.get(&segment.name) {
|
||||||
Some(r) => r.def_id,
|
Some(r) => r.def_id,
|
||||||
None => PerNs::none(),
|
None => PerNs::none(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Def::Enum(e) => {
|
Def::Enum(e) => {
|
||||||
// enum variant
|
// enum variant
|
||||||
let matching_variant =
|
let matching_variant = e
|
||||||
e.variants(db).into_iter().find(|(n, _variant)| n == name);
|
.variants(db)
|
||||||
|
.into_iter()
|
||||||
|
.find(|(n, _variant)| n == &segment.name);
|
||||||
|
|
||||||
match matching_variant {
|
match matching_variant {
|
||||||
Some((_n, variant)) => PerNs::both(variant.def_id(), e.def_id()),
|
Some((_n, variant)) => PerNs::both(variant.def_id(), e.def_id()),
|
||||||
|
@ -189,7 +191,10 @@ impl Module {
|
||||||
curr_per_ns
|
curr_per_ns
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn problems_impl(&self, db: &impl HirDatabase) -> Vec<(TreeArc<SyntaxNode>, Problem)> {
|
pub(crate) fn problems_impl(
|
||||||
|
&self,
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
) -> Vec<(TreeArc<SyntaxNode>, Problem)> {
|
||||||
let loc = self.def_id.loc(db);
|
let loc = self.def_id.loc(db);
|
||||||
let module_tree = db.module_tree(loc.source_root_id);
|
let module_tree = db.module_tree(loc.source_root_id);
|
||||||
loc.module_id.problems(&module_tree, db)
|
loc.module_id.problems(&module_tree, db)
|
||||||
|
|
|
@ -14,6 +14,7 @@ use crate::{
|
||||||
ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks},
|
ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks},
|
||||||
adt::{StructData, EnumData, EnumVariantData},
|
adt::{StructData, EnumData, EnumVariantData},
|
||||||
impl_block::ModuleImplBlocks,
|
impl_block::ModuleImplBlocks,
|
||||||
|
generics::GenericParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[salsa::query_group]
|
#[salsa::query_group]
|
||||||
|
@ -101,6 +102,9 @@ pub trait HirDatabase:
|
||||||
#[salsa::invoke(crate::expr::body_syntax_mapping)]
|
#[salsa::invoke(crate::expr::body_syntax_mapping)]
|
||||||
fn body_syntax_mapping(&self, def_id: DefId) -> Arc<crate::expr::BodySyntaxMapping>;
|
fn body_syntax_mapping(&self, def_id: DefId) -> Arc<crate::expr::BodySyntaxMapping>;
|
||||||
|
|
||||||
|
#[salsa::invoke(crate::generics::GenericParams::generic_params_query)]
|
||||||
|
fn generic_params(&self, def_id: DefId) -> Arc<GenericParams>;
|
||||||
|
|
||||||
#[salsa::invoke(crate::FnSignature::fn_signature_query)]
|
#[salsa::invoke(crate::FnSignature::fn_signature_query)]
|
||||||
fn fn_signature(&self, def_id: DefId) -> Arc<FnSignature>;
|
fn fn_signature(&self, def_id: DefId) -> Arc<FnSignature>;
|
||||||
}
|
}
|
||||||
|
|
48
crates/ra_hir/src/generics.rs
Normal file
48
crates/ra_hir/src/generics.rs
Normal file
|
@ -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 GenericParams {
|
||||||
|
pub(crate) params: Vec<GenericParam>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericParams {
|
||||||
|
pub(crate) fn generic_params_query(db: &impl HirDatabase, def_id: DefId) -> Arc<GenericParams> {
|
||||||
|
let (_file_id, node) = def_id.source(db);
|
||||||
|
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
|
||||||
|
.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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -151,6 +151,15 @@ pub(crate) enum DefKind {
|
||||||
Type,
|
Type,
|
||||||
Item,
|
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,
|
StructCtor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ mod type_ref;
|
||||||
mod ty;
|
mod ty;
|
||||||
mod impl_block;
|
mod impl_block;
|
||||||
mod expr;
|
mod expr;
|
||||||
|
mod generics;
|
||||||
|
|
||||||
mod code_model_api;
|
mod code_model_api;
|
||||||
mod code_model_impl;
|
mod code_model_impl;
|
||||||
|
|
|
@ -243,6 +243,7 @@ salsa::database_storage! {
|
||||||
fn body_hir() for db::BodyHirQuery;
|
fn body_hir() for db::BodyHirQuery;
|
||||||
fn body_syntax_mapping() for db::BodySyntaxMappingQuery;
|
fn body_syntax_mapping() for db::BodySyntaxMappingQuery;
|
||||||
fn fn_signature() for db::FnSignatureQuery;
|
fn fn_signature() for db::FnSignatureQuery;
|
||||||
|
fn generic_params() for db::GenericParamsQuery;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,10 +221,10 @@ where
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
for (import_id, import_data) in input.imports.iter() {
|
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 {
|
if !import_data.is_glob {
|
||||||
module_items.items.insert(
|
module_items.items.insert(
|
||||||
name.clone(),
|
segment.name.clone(),
|
||||||
Resolution {
|
Resolution {
|
||||||
def_id: PerNs::none(),
|
def_id: PerNs::none(),
|
||||||
import: Some(import_id),
|
import: Some(import_id),
|
||||||
|
@ -319,13 +319,13 @@ where
|
||||||
PathKind::Crate => module_id.crate_root(&self.module_tree),
|
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 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,
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -336,7 +336,7 @@ where
|
||||||
} else {
|
} else {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"path segment {:?} resolved to value only, but is not last",
|
"path segment {:?} resolved to value only, but is not last",
|
||||||
name
|
segment.name
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
@ -358,17 +358,17 @@ where
|
||||||
log::debug!("resolving {:?} in other source root", path);
|
log::debug!("resolving {:?} in other source root", path);
|
||||||
let def_id = module.resolve_path(self.db, &path);
|
let def_id = module.resolve_path(self.db, &path);
|
||||||
if !def_id.is_none() {
|
if !def_id.is_none() {
|
||||||
let name = path.segments.last().unwrap();
|
let last_segment = path.segments.last().unwrap();
|
||||||
self.update(module_id, |items| {
|
self.update(module_id, |items| {
|
||||||
let res = Resolution {
|
let res = Resolution {
|
||||||
def_id,
|
def_id,
|
||||||
import: Some(import_id),
|
import: Some(import_id),
|
||||||
};
|
};
|
||||||
items.items.insert(name.clone(), res);
|
items.items.insert(last_segment.name.clone(), res);
|
||||||
});
|
});
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"resolved import {:?} ({:?}) cross-source root to {:?}",
|
"resolved import {:?} ({:?}) cross-source root to {:?}",
|
||||||
name,
|
last_segment.name,
|
||||||
import,
|
import,
|
||||||
def_id.map(|did| did.loc(self.db))
|
def_id.map(|did| did.loc(self.db))
|
||||||
);
|
);
|
||||||
|
@ -382,7 +382,7 @@ where
|
||||||
_ => {
|
_ => {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"path segment {:?} resolved to non-module {:?}, but is not last",
|
"path segment {:?} resolved to non-module {:?}, but is not last",
|
||||||
name,
|
segment.name,
|
||||||
type_def_id.loc(self.db)
|
type_def_id.loc(self.db)
|
||||||
);
|
);
|
||||||
return true; // this resolved to a non-module, so the path won't ever resolve
|
return true; // this resolved to a non-module, so the path won't ever resolve
|
||||||
|
@ -391,7 +391,7 @@ where
|
||||||
} else {
|
} else {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"resolved import {:?} ({:?}) within source root to {:?}",
|
"resolved import {:?} ({:?}) within source root to {:?}",
|
||||||
name,
|
segment.name,
|
||||||
import,
|
import,
|
||||||
def_id.map(|did| did.loc(self.db))
|
def_id.map(|did| did.loc(self.db))
|
||||||
);
|
);
|
||||||
|
@ -400,7 +400,7 @@ where
|
||||||
def_id,
|
def_id,
|
||||||
import: Some(import_id),
|
import: Some(import_id),
|
||||||
};
|
};
|
||||||
items.items.insert(name.clone(), res);
|
items.items.insert(segment.name.clone(), res);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,35 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ra_syntax::{ast, AstNode};
|
use ra_syntax::{ast, AstNode};
|
||||||
|
|
||||||
use crate::{Name, AsName};
|
use crate::{Name, AsName, type_ref::TypeRef};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Path {
|
pub struct Path {
|
||||||
pub kind: PathKind,
|
pub kind: PathKind,
|
||||||
pub segments: Vec<Name>,
|
pub segments: Vec<PathSegment>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct PathSegment {
|
||||||
|
pub name: Name,
|
||||||
|
pub args_and_bindings: Option<Arc<GenericArgs>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
|
||||||
|
/// can (in the future) also include bindings of associated types, like in
|
||||||
|
/// `Iterator<Item = Foo>`.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct GenericArgs {
|
||||||
|
pub args: Vec<GenericArg>,
|
||||||
|
// 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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -34,7 +58,17 @@ impl Path {
|
||||||
loop {
|
loop {
|
||||||
let segment = path.segment()?;
|
let segment = path.segment()?;
|
||||||
match segment.kind()? {
|
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 => {
|
ast::PathSegmentKind::CrateKw => {
|
||||||
kind = PathKind::Crate;
|
kind = PathKind::Crate;
|
||||||
break;
|
break;
|
||||||
|
@ -88,7 +122,23 @@ impl Path {
|
||||||
if self.kind != PathKind::Plain || self.segments.len() > 1 {
|
if self.kind != PathKind::Plain || self.segments.len() > 1 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
self.segments.first()
|
self.segments.first().map(|s| &s.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericArgs {
|
||||||
|
fn from_ast(node: &ast::TypeArgList) -> Option<GenericArgs> {
|
||||||
|
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<Name> for Path {
|
||||||
fn from(name: Name) -> Path {
|
fn from(name: Name) -> Path {
|
||||||
Path {
|
Path {
|
||||||
kind: PathKind::Plain,
|
kind: PathKind::Plain,
|
||||||
segments: vec![name],
|
segments: vec![PathSegment {
|
||||||
|
name,
|
||||||
|
args_and_bindings: None,
|
||||||
|
}],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +213,10 @@ fn convert_path(prefix: Option<Path>, path: &ast::Path) -> Option<Path> {
|
||||||
kind: PathKind::Plain,
|
kind: PathKind::Plain,
|
||||||
segments: Vec::with_capacity(1),
|
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
|
res
|
||||||
}
|
}
|
||||||
ast::PathSegmentKind::CrateKw => {
|
ast::PathSegmentKind::CrateKw => {
|
||||||
|
|
|
@ -37,6 +37,8 @@ use crate::{
|
||||||
type_ref::{TypeRef, Mutability},
|
type_ref::{TypeRef, Mutability},
|
||||||
name::KnownName,
|
name::KnownName,
|
||||||
expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat},
|
expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat},
|
||||||
|
generics::GenericParams,
|
||||||
|
path::GenericArg,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The ID of a type variable.
|
/// The ID of a type variable.
|
||||||
|
@ -151,10 +153,20 @@ impl Expectation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A list of substitutions for generic parameters.
|
||||||
|
#[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).
|
/// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs).
|
||||||
///
|
///
|
||||||
/// This should be cheap to clone.
|
/// This should be cheap to clone.
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Ty {
|
pub enum Ty {
|
||||||
/// The primitive boolean type. Written as `bool`.
|
/// The primitive boolean type. Written as `bool`.
|
||||||
Bool,
|
Bool,
|
||||||
|
@ -175,7 +187,8 @@ pub enum Ty {
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
/// The name, for displaying.
|
/// The name, for displaying.
|
||||||
name: Name,
|
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`.
|
/// The pointee of a string slice. Written as `str`.
|
||||||
|
@ -234,9 +247,15 @@ pub enum Ty {
|
||||||
|
|
||||||
// Opaque (`impl Trait`) type found in a return type.
|
// Opaque (`impl Trait`) type found in a return type.
|
||||||
// Opaque(DefId, Substs),
|
// Opaque(DefId, Substs),
|
||||||
|
/// A type parameter; for example, `T` in `fn f<T>(x: T) {}
|
||||||
|
Param {
|
||||||
|
/// 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,
|
||||||
|
},
|
||||||
|
|
||||||
// A type parameter; for example, `T` in `fn f<T>(x: T) {}
|
|
||||||
// Param(ParamTy),
|
|
||||||
/// A type variable used during type checking. Not to be confused with a
|
/// A type variable used during type checking. Not to be confused with a
|
||||||
/// type parameter.
|
/// type parameter.
|
||||||
Infer(InferTy),
|
Infer(InferTy),
|
||||||
|
@ -250,7 +269,7 @@ pub enum Ty {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A function signature.
|
/// A function signature.
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct FnSig {
|
pub struct FnSig {
|
||||||
input: Vec<Ty>,
|
input: Vec<Ty>,
|
||||||
output: Ty,
|
output: Ty,
|
||||||
|
@ -259,8 +278,12 @@ pub struct FnSig {
|
||||||
impl Ty {
|
impl Ty {
|
||||||
pub(crate) fn from_hir(
|
pub(crate) fn from_hir(
|
||||||
db: &impl HirDatabase,
|
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,
|
module: &Module,
|
||||||
impl_block: Option<&ImplBlock>,
|
impl_block: Option<&ImplBlock>,
|
||||||
|
generics: &GenericParams,
|
||||||
type_ref: &TypeRef,
|
type_ref: &TypeRef,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match type_ref {
|
match type_ref {
|
||||||
|
@ -268,32 +291,32 @@ impl Ty {
|
||||||
TypeRef::Tuple(inner) => {
|
TypeRef::Tuple(inner) => {
|
||||||
let inner_tys = inner
|
let inner_tys = inner
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tr| Ty::from_hir(db, module, impl_block, tr))
|
.map(|tr| Ty::from_hir(db, module, impl_block, generics, tr))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
Ty::Tuple(inner_tys.into())
|
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) => {
|
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)
|
Ty::RawPtr(Arc::new(inner_ty), *mutability)
|
||||||
}
|
}
|
||||||
TypeRef::Array(inner) => {
|
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))
|
Ty::Array(Arc::new(inner_ty))
|
||||||
}
|
}
|
||||||
TypeRef::Slice(inner) => {
|
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))
|
Ty::Slice(Arc::new(inner_ty))
|
||||||
}
|
}
|
||||||
TypeRef::Reference(inner, mutability) => {
|
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)
|
Ty::Ref(Arc::new(inner_ty), *mutability)
|
||||||
}
|
}
|
||||||
TypeRef::Placeholder => Ty::Unknown,
|
TypeRef::Placeholder => Ty::Unknown,
|
||||||
TypeRef::Fn(params) => {
|
TypeRef::Fn(params) => {
|
||||||
let mut inner_tys = params
|
let mut inner_tys = params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tr| Ty::from_hir(db, module, impl_block, tr))
|
.map(|tr| Ty::from_hir(db, module, impl_block, generics, tr))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let return_ty = inner_tys
|
let return_ty = inner_tys
|
||||||
.pop()
|
.pop()
|
||||||
|
@ -312,15 +335,19 @@ impl Ty {
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
impl_block: Option<&ImplBlock>,
|
impl_block: Option<&ImplBlock>,
|
||||||
|
generics: &GenericParams,
|
||||||
type_ref: Option<&TypeRef>,
|
type_ref: Option<&TypeRef>,
|
||||||
) -> Self {
|
) -> 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(
|
pub(crate) fn from_hir_path(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
impl_block: Option<&ImplBlock>,
|
impl_block: Option<&ImplBlock>,
|
||||||
|
generics: &GenericParams,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
if let Some(name) = path.as_ident() {
|
if let Some(name) = path.as_ident() {
|
||||||
|
@ -329,7 +356,15 @@ impl Ty {
|
||||||
} else if let Some(float_ty) = primitive::UncertainFloatTy::from_name(name) {
|
} else if let Some(float_ty) = primitive::UncertainFloatTy::from_name(name) {
|
||||||
return Ty::Float(float_ty);
|
return Ty::Float(float_ty);
|
||||||
} else if name.as_known_name() == Some(KnownName::SelfType) {
|
} 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 = &GenericParams::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() {
|
} else if let Some(known) = name.as_known_name() {
|
||||||
match known {
|
match known {
|
||||||
KnownName::Bool => return Ty::Bool,
|
KnownName::Bool => return Ty::Bool,
|
||||||
|
@ -337,16 +372,89 @@ impl Ty {
|
||||||
KnownName::Str => return Ty::Str,
|
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(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve in module (in type namespace)
|
// Resolve in module (in type namespace)
|
||||||
let resolved = if let Some(r) = module.resolve_path(db, path).take_types() {
|
let resolved = match module.resolve_path(db, path).take_types() {
|
||||||
r
|
Some(r) => r,
|
||||||
} else {
|
None => return Ty::Unknown,
|
||||||
return Ty::Unknown;
|
|
||||||
};
|
};
|
||||||
db.type_for_def(resolved)
|
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: &GenericParams,
|
||||||
|
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.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
|
||||||
|
// referring to the variant. So `Option::<T>::None` and
|
||||||
|
// `Option::None::<T>` 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::<T>::None
|
||||||
|
&path.segments[len - 2]
|
||||||
|
} else {
|
||||||
|
// Option::None::<T>
|
||||||
|
last
|
||||||
|
};
|
||||||
|
(ev.parent_enum(db).generic_params(db), segment)
|
||||||
|
}
|
||||||
|
_ => return Substs::empty(),
|
||||||
|
};
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
assert_eq!(substs.len(), def_generics.params.len());
|
||||||
|
Substs(substs.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unit() -> Self {
|
pub fn unit() -> Self {
|
||||||
|
@ -374,7 +482,14 @@ impl Ty {
|
||||||
}
|
}
|
||||||
sig_mut.output.walk_mut(f);
|
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 +509,49 @@ impl Ty {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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<u32>` 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.)
|
||||||
|
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: 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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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<u32>`, this returns the `u32`.
|
||||||
|
fn substs(&self) -> Option<Substs> {
|
||||||
|
match self {
|
||||||
|
Ty::Adt { substs, .. } => Some(substs.clone()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Ty {
|
impl fmt::Display for Ty {
|
||||||
|
@ -425,7 +583,17 @@ impl fmt::Display for Ty {
|
||||||
.to_fmt(f)?;
|
.to_fmt(f)?;
|
||||||
write!(f, " -> {}", sig.output)
|
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::Unknown => write!(f, "[unknown]"),
|
||||||
Ty::Infer(..) => write!(f, "_"),
|
Ty::Infer(..) => write!(f, "_"),
|
||||||
}
|
}
|
||||||
|
@ -440,28 +608,49 @@ fn type_for_fn(db: &impl HirDatabase, f: Function) -> Ty {
|
||||||
let signature = f.signature(db);
|
let signature = f.signature(db);
|
||||||
let module = f.module(db);
|
let module = f.module(db);
|
||||||
let impl_block = f.impl_block(db);
|
let impl_block = f.impl_block(db);
|
||||||
// TODO we ignore type parameters for now
|
let generics = f.generic_params(db);
|
||||||
let input = signature
|
let input = signature
|
||||||
.params()
|
.params()
|
||||||
.iter()
|
.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::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
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 };
|
let sig = FnSig { input, output };
|
||||||
Ty::FnPtr(Arc::new(sig))
|
Ty::FnPtr(Arc::new(sig))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_substs(generics: &GenericParams) -> Substs {
|
||||||
|
Substs(
|
||||||
|
generics
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.map(|_p| Ty::Unknown)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty {
|
fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty {
|
||||||
|
let generics = s.generic_params(db);
|
||||||
Ty::Adt {
|
Ty::Adt {
|
||||||
def_id: s.def_id(),
|
def_id: s.def_id(),
|
||||||
name: s.name(db).unwrap_or_else(Name::missing),
|
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 {
|
pub(crate) fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Ty {
|
||||||
|
let generics = s.generic_params(db);
|
||||||
Ty::Adt {
|
Ty::Adt {
|
||||||
def_id: s.def_id(),
|
def_id: s.def_id(),
|
||||||
name: s.name(db).unwrap_or_else(Name::missing),
|
name: s.name(db).unwrap_or_else(Name::missing),
|
||||||
|
substs: make_substs(&generics),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,9 +684,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<Ty> {
|
pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Option<Ty> {
|
||||||
let def = def_id.resolve(db);
|
let def = def_id.resolve(db);
|
||||||
let variant_data = match def {
|
let (variant_data, generics) = match def {
|
||||||
Def::Struct(s) => s.variant_data(db),
|
Def::Struct(s) => (s.variant_data(db), s.generic_params(db)),
|
||||||
Def::EnumVariant(ev) => ev.variant_data(db),
|
Def::EnumVariant(ev) => (ev.variant_data(db), ev.parent_enum(db).generic_params(db)),
|
||||||
// TODO: unions
|
// TODO: unions
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"trying to get type for field in non-struct/variant {:?}",
|
"trying to get type for field in non-struct/variant {:?}",
|
||||||
|
@ -507,7 +696,13 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name)
|
||||||
let module = def_id.module(db);
|
let module = def_id.module(db);
|
||||||
let impl_block = def_id.impl_block(db);
|
let impl_block = def_id.impl_block(db);
|
||||||
let type_ref = variant_data.get_field_type_ref(&field)?;
|
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.
|
/// The result of type inference: A mapping from expressions and patterns to types.
|
||||||
|
@ -684,8 +879,26 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
self.type_of_pat.insert(pat, ty);
|
self.type_of_pat.insert(pat, ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_ty(&self, type_ref: &TypeRef) -> Ty {
|
fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
|
||||||
Ty::from_hir(self.db, &self.module, self.impl_block.as_ref(), type_ref)
|
// TODO provide generics of function
|
||||||
|
let generics = GenericParams::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_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 {
|
fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
||||||
|
@ -708,12 +921,16 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
(Ty::Bool, _) | (Ty::Str, _) | (Ty::Never, _) | (Ty::Char, _) => ty1 == ty2,
|
(Ty::Bool, _) | (Ty::Str, _) | (Ty::Never, _) | (Ty::Char, _) => ty1 == ty2,
|
||||||
(
|
(
|
||||||
Ty::Adt {
|
Ty::Adt {
|
||||||
def_id: def_id1, ..
|
def_id: def_id1,
|
||||||
|
substs: substs1,
|
||||||
|
..
|
||||||
},
|
},
|
||||||
Ty::Adt {
|
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::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::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),
|
(Ty::Ref(t1, m1), Ty::Ref(t2, m2)) if m1 == m2 => self.unify(t1, t2),
|
||||||
|
@ -848,73 +1065,100 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
Some(ty)
|
Some(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_variant(&self, path: Option<&Path>) -> (Ty, Option<DefId>) {
|
fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<DefId>) {
|
||||||
let path = if let Some(path) = path {
|
let path = match path {
|
||||||
path
|
Some(path) => path,
|
||||||
} else {
|
None => return (Ty::Unknown, None),
|
||||||
return (Ty::Unknown, None);
|
|
||||||
};
|
};
|
||||||
let def_id = if let Some(def_id) = self.module.resolve_path(self.db, &path).take_types() {
|
let def_id = match self.module.resolve_path(self.db, &path).take_types() {
|
||||||
def_id
|
Some(def_id) => def_id,
|
||||||
} else {
|
_ => return (Ty::Unknown, None),
|
||||||
return (Ty::Unknown, None);
|
|
||||||
};
|
};
|
||||||
|
// TODO remove the duplication between here and `Ty::from_path`?
|
||||||
|
// TODO provide generics of function
|
||||||
|
let generics = GenericParams::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) {
|
match def_id.resolve(self.db) {
|
||||||
Def::Struct(s) => {
|
Def::Struct(s) => {
|
||||||
let ty = type_for_struct(self.db, s);
|
let ty = type_for_struct(self.db, s);
|
||||||
|
let ty = self.insert_type_vars(ty.apply_substs(substs));
|
||||||
(ty, Some(def_id))
|
(ty, Some(def_id))
|
||||||
}
|
}
|
||||||
Def::EnumVariant(ev) => {
|
Def::EnumVariant(ev) => {
|
||||||
let ty = type_for_enum_variant(self.db, ev);
|
let ty = type_for_enum_variant(self.db, ev);
|
||||||
|
let ty = self.insert_type_vars(ty.apply_substs(substs));
|
||||||
(ty, Some(def_id))
|
(ty, Some(def_id))
|
||||||
}
|
}
|
||||||
_ => (Ty::Unknown, None),
|
_ => (Ty::Unknown, None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_fields(&self, path: Option<&Path>) -> Option<(Ty, Vec<StructField>)> {
|
fn resolve_fields(&mut self, path: Option<&Path>) -> Option<(Ty, Vec<StructField>)> {
|
||||||
let def_id = self.module.resolve_path(self.db, path?).take_types()?;
|
let (ty, def_id) = self.resolve_variant(path);
|
||||||
|
let def_id = def_id?;
|
||||||
let def = def_id.resolve(self.db);
|
let def = def_id.resolve(self.db);
|
||||||
|
|
||||||
match def {
|
match def {
|
||||||
Def::Struct(s) => {
|
Def::Struct(s) => {
|
||||||
let fields = s.fields(self.db);
|
let fields = s.fields(self.db);
|
||||||
Some((type_for_struct(self.db, s), fields))
|
Some((ty, fields))
|
||||||
}
|
}
|
||||||
Def::EnumVariant(ev) => {
|
Def::EnumVariant(ev) => {
|
||||||
let fields = ev.fields(self.db);
|
let fields = ev.fields(self.db);
|
||||||
Some((type_for_enum_variant(self.db, ev), fields))
|
Some((ty, fields))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => 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
|
let (ty, fields) = self
|
||||||
.resolve_fields(path)
|
.resolve_fields(path)
|
||||||
.unwrap_or((Ty::Unknown, Vec::new()));
|
.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() {
|
for (i, &subpat) in subpats.iter().enumerate() {
|
||||||
let expected_ty = fields
|
let expected_ty = fields
|
||||||
.get(i)
|
.get(i)
|
||||||
.and_then(|field| field.ty(self.db))
|
.and_then(|field| field.ty(self.db))
|
||||||
.unwrap_or(Ty::Unknown);
|
.unwrap_or(Ty::Unknown)
|
||||||
|
.subst(&substs);
|
||||||
self.infer_pat(subpat, &expected_ty);
|
self.infer_pat(subpat, &expected_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
let (ty, fields) = self
|
||||||
.resolve_fields(path)
|
.resolve_fields(path)
|
||||||
.unwrap_or((Ty::Unknown, Vec::new()));
|
.unwrap_or((Ty::Unknown, Vec::new()));
|
||||||
|
|
||||||
|
self.unify(&ty, expected);
|
||||||
|
|
||||||
|
let substs = ty.substs().expect("adt should have substs");
|
||||||
|
|
||||||
for subpat in subpats {
|
for subpat in subpats {
|
||||||
let matching_field = fields.iter().find(|field| field.name() == &subpat.name);
|
let matching_field = fields.iter().find(|field| field.name() == &subpat.name);
|
||||||
let expected_ty = matching_field
|
let expected_ty = matching_field
|
||||||
.and_then(|field| field.ty(self.db))
|
.and_then(|field| field.ty(self.db))
|
||||||
.unwrap_or(Ty::Unknown);
|
.unwrap_or(Ty::Unknown)
|
||||||
|
.subst(&substs);
|
||||||
self.infer_pat(subpat.pat, &expected_ty);
|
self.infer_pat(subpat.pat, &expected_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -959,11 +1203,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
Pat::TupleStruct {
|
Pat::TupleStruct {
|
||||||
path: ref p,
|
path: ref p,
|
||||||
args: ref subpats,
|
args: ref subpats,
|
||||||
} => self.infer_tuple_struct_pat(p.as_ref(), subpats),
|
} => self.infer_tuple_struct_pat(p.as_ref(), subpats, expected),
|
||||||
Pat::Struct {
|
Pat::Struct {
|
||||||
path: ref p,
|
path: ref p,
|
||||||
args: ref fields,
|
args: ref fields,
|
||||||
} => self.infer_struct_pat(p.as_ref(), fields),
|
} => self.infer_struct_pat(p.as_ref(), fields, expected),
|
||||||
Pat::Path(path) => self
|
Pat::Path(path) => self
|
||||||
.module
|
.module
|
||||||
.resolve_path(self.db, &path)
|
.resolve_path(self.db, &path)
|
||||||
|
@ -1155,11 +1399,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
spread,
|
spread,
|
||||||
} => {
|
} => {
|
||||||
let (ty, def_id) = self.resolve_variant(path.as_ref());
|
let (ty, def_id) = self.resolve_variant(path.as_ref());
|
||||||
|
let substs = ty.substs().expect("adt should have substs");
|
||||||
for field in fields {
|
for field in fields {
|
||||||
let field_ty = if let Some(def_id) = def_id {
|
let field_ty = if let Some(def_id) = def_id {
|
||||||
self.db
|
self.db
|
||||||
.type_for_field(def_id, field.name.clone())
|
.type_for_field(def_id, field.name.clone())
|
||||||
.unwrap_or(Ty::Unknown)
|
.unwrap_or(Ty::Unknown)
|
||||||
|
.subst(&substs)
|
||||||
} else {
|
} else {
|
||||||
Ty::Unknown
|
Ty::Unknown
|
||||||
};
|
};
|
||||||
|
@ -1180,7 +1426,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
let i = name.to_string().parse::<usize>().ok();
|
let i = name.to_string().parse::<usize>().ok();
|
||||||
i.and_then(|i| fields.get(i).cloned())
|
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,
|
_ => None,
|
||||||
})
|
})
|
||||||
.unwrap_or(Ty::Unknown);
|
.unwrap_or(Ty::Unknown);
|
||||||
|
@ -1193,7 +1444,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
Expr::Cast { expr, type_ref } => {
|
Expr::Cast { expr, type_ref } => {
|
||||||
let _inner_ty = self.infer_expr(*expr, &Expectation::none());
|
let _inner_ty = self.infer_expr(*expr, &Expectation::none());
|
||||||
let cast_ty = self.make_ty(type_ref);
|
let cast_ty = self.make_ty(type_ref);
|
||||||
let cast_ty = self.insert_type_vars(cast_ty);
|
|
||||||
// TODO check the cast...
|
// TODO check the cast...
|
||||||
cast_ty
|
cast_ty
|
||||||
}
|
}
|
||||||
|
@ -1305,12 +1555,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
type_ref,
|
type_ref,
|
||||||
initializer,
|
initializer,
|
||||||
} => {
|
} => {
|
||||||
let decl_ty = Ty::from_hir_opt(
|
let decl_ty = type_ref
|
||||||
self.db,
|
.as_ref()
|
||||||
&self.module,
|
.map(|tr| self.make_ty(tr))
|
||||||
self.impl_block.as_ref(),
|
.unwrap_or(Ty::Unknown);
|
||||||
type_ref.as_ref(),
|
|
||||||
);
|
|
||||||
let decl_ty = self.insert_type_vars(decl_ty);
|
let decl_ty = self.insert_type_vars(decl_ty);
|
||||||
let ty = if let Some(expr) = initializer {
|
let ty = if let Some(expr) = initializer {
|
||||||
let expr_ty = self.infer_expr(*expr, &Expectation::has_type(decl_ty));
|
let expr_ty = self.infer_expr(*expr, &Expectation::has_type(decl_ty));
|
||||||
|
@ -1338,13 +1586,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
let body = Arc::clone(&self.body); // avoid borrow checker problem
|
let body = Arc::clone(&self.body); // avoid borrow checker problem
|
||||||
for (type_ref, pat) in signature.params().iter().zip(body.params()) {
|
for (type_ref, pat) in signature.params().iter().zip(body.params()) {
|
||||||
let ty = self.make_ty(type_ref);
|
let ty = self.make_ty(type_ref);
|
||||||
let ty = self.insert_type_vars(ty);
|
|
||||||
|
|
||||||
self.infer_pat(*pat, &ty);
|
self.infer_pat(*pat, &ty);
|
||||||
}
|
}
|
||||||
self.return_ty = {
|
self.return_ty = {
|
||||||
let ty = self.make_ty(signature.ret_type());
|
let ty = self.make_ty(signature.ret_type());
|
||||||
let ty = self.insert_type_vars(ty);
|
|
||||||
ty
|
ty
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,11 @@ use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use ra_db::SourceRootId;
|
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::GenericParams
|
||||||
|
};
|
||||||
use super::Ty;
|
use super::Ty;
|
||||||
|
|
||||||
/// This is used as a key for indexing impls.
|
/// This is used as a key for indexing impls.
|
||||||
|
@ -64,8 +68,15 @@ impl CrateImplBlocks {
|
||||||
if let Some(_target_trait) = impl_data.target_trait() {
|
if let Some(_target_trait) = impl_data.target_trait() {
|
||||||
// ignore for now
|
// ignore for now
|
||||||
} else {
|
} else {
|
||||||
let target_ty =
|
// TODO provide generics of impl
|
||||||
Ty::from_hir(db, &module, Some(&impl_block), impl_data.target_type());
|
let generics = GenericParams::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) {
|
if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) {
|
||||||
self.impls
|
self.impls
|
||||||
.entry(target_ty_fp)
|
.entry(target_ty_fp)
|
||||||
|
|
|
@ -418,6 +418,95 @@ fn test() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_struct_generics() {
|
||||||
|
check_inference(
|
||||||
|
r#"
|
||||||
|
struct A<T> {
|
||||||
|
x: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test(a1: A<u32>, i: i32) {
|
||||||
|
a1.x;
|
||||||
|
let a2 = A { x: i };
|
||||||
|
a2.x;
|
||||||
|
let a3 = A::<i128> { x: 1 };
|
||||||
|
a3.x;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"struct_generics.txt",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_generics_in_patterns() {
|
||||||
|
check_inference(
|
||||||
|
r#"
|
||||||
|
struct A<T> {
|
||||||
|
x: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Option<T> {
|
||||||
|
Some(T),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test(a1: A<u32>, o: Option<u64>) {
|
||||||
|
let A { x: x2 } = a1;
|
||||||
|
let A::<i64> { x: x3 } = A { x: 1 };
|
||||||
|
match o {
|
||||||
|
Option::Some(t) => t,
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"generics_in_patterns.txt",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_function_generics() {
|
||||||
|
check_inference(
|
||||||
|
r#"
|
||||||
|
fn id<T>(t: T) -> T { t }
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
id(1u32);
|
||||||
|
id::<i128>(1);
|
||||||
|
let x: u64 = id(1);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"function_generics.txt",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_generic_chain() {
|
||||||
|
check_inference(
|
||||||
|
r#"
|
||||||
|
struct A<T> {
|
||||||
|
x: T,
|
||||||
|
}
|
||||||
|
impl<T2> A<T2> {
|
||||||
|
fn x(self) -> T2 {
|
||||||
|
self.x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn id<T>(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 {
|
fn infer(content: &str) -> String {
|
||||||
let (db, _, file_id) = MockDatabase::with_single_file(content);
|
let (db, _, file_id) = MockDatabase::with_single_file(content);
|
||||||
let source_file = db.source_file(file_id);
|
let source_file = db.source_file(file_id);
|
||||||
|
|
14
crates/ra_hir/src/ty/tests/data/function_generics.txt
Normal file
14
crates/ra_hir/src/ty/tests/data/function_generics.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[10; 11) 't': [unknown]
|
||||||
|
[21; 26) '{ t }': [unknown]
|
||||||
|
[23; 24) 't': [unknown]
|
||||||
|
[38; 98) '{ ...(1); }': ()
|
||||||
|
[44; 46) 'id': fn(T) -> T
|
||||||
|
[44; 52) 'id(1u32)': T
|
||||||
|
[47; 51) '1u32': u32
|
||||||
|
[58; 68) 'id::<i128>': fn(T) -> T
|
||||||
|
[58; 71) 'id::<i128>(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
|
29
crates/ra_hir/src/ty/tests/data/generic_chain.txt
Normal file
29
crates/ra_hir/src/ty/tests/data/generic_chain.txt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
[53; 57) 'self': A<[unknown]>
|
||||||
|
[65; 87) '{ ... }': [unknown]
|
||||||
|
[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': 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<T>
|
||||||
|
[186; 200) 'A { x: id(y) }': A<T>
|
||||||
|
[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<T>
|
||||||
|
[218; 221) 'a.x': T
|
||||||
|
[233; 234) 'b': A<T>
|
||||||
|
[237; 247) 'A { x: z }': A<T>
|
||||||
|
[244; 245) 'z': T
|
||||||
|
[254; 255) 'b': A<T>
|
||||||
|
[254; 259) 'b.x()': i128
|
17
crates/ra_hir/src/ty/tests/data/generics_in_patterns.txt
Normal file
17
crates/ra_hir/src/ty/tests/data/generics_in_patterns.txt
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[79; 81) 'a1': A<u32>
|
||||||
|
[91; 92) 'o': Option<u64>
|
||||||
|
[107; 244) '{ ... }; }': ()
|
||||||
|
[117; 128) 'A { x: x2 }': A<u32>
|
||||||
|
[124; 126) 'x2': u32
|
||||||
|
[131; 133) 'a1': A<u32>
|
||||||
|
[143; 161) 'A::<i6...: x3 }': A<i64>
|
||||||
|
[157; 159) 'x3': i64
|
||||||
|
[164; 174) 'A { x: 1 }': A<i64>
|
||||||
|
[171; 172) '1': i64
|
||||||
|
[180; 241) 'match ... }': u64
|
||||||
|
[186; 187) 'o': Option<u64>
|
||||||
|
[198; 213) 'Option::Some(t)': Option<u64>
|
||||||
|
[211; 212) 't': u64
|
||||||
|
[217; 218) 't': u64
|
||||||
|
[228; 229) '_': Option<u64>
|
||||||
|
[233; 234) '1': u64
|
15
crates/ra_hir/src/ty/tests/data/struct_generics.txt
Normal file
15
crates/ra_hir/src/ty/tests/data/struct_generics.txt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[36; 38) 'a1': A<u32>
|
||||||
|
[48; 49) 'i': i32
|
||||||
|
[56; 147) '{ ...3.x; }': ()
|
||||||
|
[62; 64) 'a1': A<u32>
|
||||||
|
[62; 66) 'a1.x': u32
|
||||||
|
[76; 78) 'a2': A<i32>
|
||||||
|
[81; 91) 'A { x: i }': A<i32>
|
||||||
|
[88; 89) 'i': i32
|
||||||
|
[97; 99) 'a2': A<i32>
|
||||||
|
[97; 101) 'a2.x': i32
|
||||||
|
[111; 113) 'a3': A<i128>
|
||||||
|
[116; 134) 'A::<i1...x: 1 }': A<i128>
|
||||||
|
[131; 132) '1': i128
|
||||||
|
[140; 142) 'a3': A<i128>
|
||||||
|
[140; 144) 'a3.x': i128
|
|
@ -24,7 +24,9 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
|
fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
|
||||||
for receiver in receiver.autoderef(ctx.db) {
|
for receiver in receiver.autoderef(ctx.db) {
|
||||||
match receiver {
|
match receiver {
|
||||||
Ty::Adt { def_id, .. } => {
|
Ty::Adt {
|
||||||
|
def_id, ref substs, ..
|
||||||
|
} => {
|
||||||
match def_id.resolve(ctx.db) {
|
match def_id.resolve(ctx.db) {
|
||||||
Def::Struct(s) => {
|
Def::Struct(s) => {
|
||||||
for field in s.fields(ctx.db) {
|
for field in s.fields(ctx.db) {
|
||||||
|
@ -33,7 +35,7 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty)
|
||||||
field.name().to_string(),
|
field.name().to_string(),
|
||||||
)
|
)
|
||||||
.kind(CompletionItemKind::Field)
|
.kind(CompletionItemKind::Field)
|
||||||
.set_detail(field.ty(ctx.db).map(|ty| ty.to_string()))
|
.set_detail(field.ty(ctx.db).map(|ty| ty.subst(substs).to_string()))
|
||||||
.add_to(acc);
|
.add_to(acc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,7 @@ salsa::database_storage! {
|
||||||
fn body_hir() for hir::db::BodyHirQuery;
|
fn body_hir() for hir::db::BodyHirQuery;
|
||||||
fn body_syntax_mapping() for hir::db::BodySyntaxMappingQuery;
|
fn body_syntax_mapping() for hir::db::BodySyntaxMappingQuery;
|
||||||
fn fn_signature() for hir::db::FnSignatureQuery;
|
fn fn_signature() for hir::db::FnSignatureQuery;
|
||||||
|
fn generic_params() for hir::db::GenericParamsQuery;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,38 @@ impl ArrayType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssocTypeArg
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct AssocTypeArg {
|
||||||
|
pub(crate) syntax: SyntaxNode,
|
||||||
|
}
|
||||||
|
unsafe impl TransparentNewType for AssocTypeArg {
|
||||||
|
type Repr = rowan::SyntaxNode<RaTypes>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AstNode for AssocTypeArg {
|
||||||
|
fn cast(syntax: &SyntaxNode) -> Option<&Self> {
|
||||||
|
match syntax.kind() {
|
||||||
|
ASSOC_TYPE_ARG => Some(AssocTypeArg::from_repr(syntax.into_repr())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||||
|
fn to_owned(&self) -> TreeArc<AssocTypeArg> { TreeArc::cast(self.syntax.to_owned()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl AssocTypeArg {
|
||||||
|
pub fn name_ref(&self) -> Option<&NameRef> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_ref(&self) -> Option<&TypeRef> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Attr
|
// Attr
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
@ -1397,6 +1429,34 @@ impl AstNode for Lifetime {
|
||||||
impl ast::AstToken for Lifetime {}
|
impl ast::AstToken for Lifetime {}
|
||||||
impl Lifetime {}
|
impl Lifetime {}
|
||||||
|
|
||||||
|
// LifetimeArg
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct LifetimeArg {
|
||||||
|
pub(crate) syntax: SyntaxNode,
|
||||||
|
}
|
||||||
|
unsafe impl TransparentNewType for LifetimeArg {
|
||||||
|
type Repr = rowan::SyntaxNode<RaTypes>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AstNode for LifetimeArg {
|
||||||
|
fn cast(syntax: &SyntaxNode) -> Option<&Self> {
|
||||||
|
match syntax.kind() {
|
||||||
|
LIFETIME_ARG => Some(LifetimeArg::from_repr(syntax.into_repr())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||||
|
fn to_owned(&self) -> TreeArc<LifetimeArg> { TreeArc::cast(self.syntax.to_owned()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl LifetimeArg {
|
||||||
|
pub fn lifetime(&self) -> Option<&Lifetime> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// LifetimeParam
|
// LifetimeParam
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
@ -2355,6 +2415,10 @@ impl PathSegment {
|
||||||
pub fn name_ref(&self) -> Option<&NameRef> {
|
pub fn name_ref(&self) -> Option<&NameRef> {
|
||||||
super::child_opt(self)
|
super::child_opt(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_arg_list(&self) -> Option<&TypeArgList> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PathType
|
// PathType
|
||||||
|
@ -3335,6 +3399,70 @@ impl TupleType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TypeArg
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct TypeArg {
|
||||||
|
pub(crate) syntax: SyntaxNode,
|
||||||
|
}
|
||||||
|
unsafe impl TransparentNewType for TypeArg {
|
||||||
|
type Repr = rowan::SyntaxNode<RaTypes>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AstNode for TypeArg {
|
||||||
|
fn cast(syntax: &SyntaxNode) -> Option<&Self> {
|
||||||
|
match syntax.kind() {
|
||||||
|
TYPE_ARG => Some(TypeArg::from_repr(syntax.into_repr())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||||
|
fn to_owned(&self) -> TreeArc<TypeArg> { TreeArc::cast(self.syntax.to_owned()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl TypeArg {
|
||||||
|
pub fn type_ref(&self) -> Option<&TypeRef> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeArgList
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct TypeArgList {
|
||||||
|
pub(crate) syntax: SyntaxNode,
|
||||||
|
}
|
||||||
|
unsafe impl TransparentNewType for TypeArgList {
|
||||||
|
type Repr = rowan::SyntaxNode<RaTypes>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AstNode for TypeArgList {
|
||||||
|
fn cast(syntax: &SyntaxNode) -> Option<&Self> {
|
||||||
|
match syntax.kind() {
|
||||||
|
TYPE_ARG_LIST => Some(TypeArgList::from_repr(syntax.into_repr())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||||
|
fn to_owned(&self) -> TreeArc<TypeArgList> { TreeArc::cast(self.syntax.to_owned()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl TypeArgList {
|
||||||
|
pub fn type_args(&self) -> impl Iterator<Item = &TypeArg> {
|
||||||
|
super::children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lifetime_args(&self) -> impl Iterator<Item = &LifetimeArg> {
|
||||||
|
super::children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assoc_type_args(&self) -> impl Iterator<Item = &AssocTypeArg> {
|
||||||
|
super::children(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TypeDef
|
// TypeDef
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
|
|
@ -597,8 +597,16 @@ Grammar(
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
"PathSegment": (
|
"PathSegment": (
|
||||||
options: [ "NameRef" ]
|
options: [ "NameRef", "TypeArgList" ]
|
||||||
),
|
),
|
||||||
|
"TypeArgList": (collections: [
|
||||||
|
["type_args", "TypeArg"],
|
||||||
|
["lifetime_args", "LifetimeArg"],
|
||||||
|
["assoc_type_args", "AssocTypeArg"],
|
||||||
|
]),
|
||||||
|
"TypeArg": (options: ["TypeRef"]),
|
||||||
|
"AssocTypeArg": (options: ["NameRef", "TypeRef"]),
|
||||||
|
"LifetimeArg": (options: ["Lifetime"]),
|
||||||
"Comment": ( traits: ["AstToken"] ),
|
"Comment": ( traits: ["AstToken"] ),
|
||||||
"Whitespace": ( traits: ["AstToken"] ),
|
"Whitespace": ( traits: ["AstToken"] ),
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue