diff options
57 files changed, 2875 insertions, 1291 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d443b124c..52d72c3c5 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -1712,15 +1712,17 @@ impl Type { | |||
1712 | resolver: &Resolver, | 1712 | resolver: &Resolver, |
1713 | ty: Ty, | 1713 | ty: Ty, |
1714 | ) -> Type { | 1714 | ) -> Type { |
1715 | let environment = | 1715 | let environment = resolver |
1716 | resolver.generic_def().map_or_else(Default::default, |d| db.trait_environment(d)); | 1716 | .generic_def() |
1717 | .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d)); | ||
1717 | Type { krate, env: environment, ty } | 1718 | Type { krate, env: environment, ty } |
1718 | } | 1719 | } |
1719 | 1720 | ||
1720 | fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type { | 1721 | fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type { |
1721 | let resolver = lexical_env.resolver(db.upcast()); | 1722 | let resolver = lexical_env.resolver(db.upcast()); |
1722 | let environment = | 1723 | let environment = resolver |
1723 | resolver.generic_def().map_or_else(Default::default, |d| db.trait_environment(d)); | 1724 | .generic_def() |
1725 | .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d)); | ||
1724 | Type { krate, env: environment, ty } | 1726 | Type { krate, env: environment, ty } |
1725 | } | 1727 | } |
1726 | 1728 | ||
@@ -2051,11 +2053,7 @@ impl Type { | |||
2051 | name: Option<&Name>, | 2053 | name: Option<&Name>, |
2052 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | 2054 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, |
2053 | ) -> Option<T> { | 2055 | ) -> Option<T> { |
2054 | // There should be no inference vars in types passed here | 2056 | let canonical = hir_ty::replace_errors_with_variables(self.ty.clone()); |
2055 | // FIXME check that? | ||
2056 | // FIXME replace Unknown by bound vars here | ||
2057 | let canonical = | ||
2058 | Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(&Interner) }; | ||
2059 | 2057 | ||
2060 | let env = self.env.clone(); | 2058 | let env = self.env.clone(); |
2061 | let krate = krate.id; | 2059 | let krate = krate.id; |
@@ -2223,8 +2221,9 @@ impl Type { | |||
2223 | walk_type(db, self, &mut cb); | 2221 | walk_type(db, self, &mut cb); |
2224 | } | 2222 | } |
2225 | 2223 | ||
2226 | pub fn could_unify_with(&self, other: &Type) -> bool { | 2224 | pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool { |
2227 | could_unify(&self.ty, &other.ty) | 2225 | let tys = hir_ty::replace_errors_with_variables((self.ty.clone(), other.ty.clone())); |
2226 | could_unify(db, self.env.clone(), &tys) | ||
2228 | } | 2227 | } |
2229 | } | 2228 | } |
2230 | 2229 | ||
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index aadd4e44a..89a1ea770 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::{ | 3 | use std::{ |
4 | convert::{TryFrom, TryInto}, | 4 | convert::{TryFrom, TryInto}, |
5 | ops, | 5 | fmt, ops, |
6 | sync::Arc, | 6 | sync::Arc, |
7 | }; | 7 | }; |
8 | 8 | ||
@@ -648,6 +648,15 @@ pub enum AttrInput { | |||
648 | TokenTree(Subtree), | 648 | TokenTree(Subtree), |
649 | } | 649 | } |
650 | 650 | ||
651 | impl fmt::Display for AttrInput { | ||
652 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
653 | match self { | ||
654 | AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()), | ||
655 | AttrInput::TokenTree(subtree) => subtree.fmt(f), | ||
656 | } | ||
657 | } | ||
658 | } | ||
659 | |||
651 | impl Attr { | 660 | impl Attr { |
652 | fn from_src( | 661 | fn from_src( |
653 | db: &dyn DefDatabase, | 662 | db: &dyn DefDatabase, |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index cad8a7479..528270d49 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | //! A simplified AST that only contains items. | 1 | //! A simplified AST that only contains items. |
2 | 2 | ||
3 | mod lower; | 3 | mod lower; |
4 | mod pretty; | ||
5 | #[cfg(test)] | ||
6 | mod tests; | ||
4 | 7 | ||
5 | use std::{ | 8 | use std::{ |
6 | any::type_name, | 9 | any::type_name, |
@@ -132,6 +135,7 @@ impl ItemTree { | |||
132 | let ItemTreeData { | 135 | let ItemTreeData { |
133 | imports, | 136 | imports, |
134 | extern_crates, | 137 | extern_crates, |
138 | extern_blocks, | ||
135 | functions, | 139 | functions, |
136 | params, | 140 | params, |
137 | structs, | 141 | structs, |
@@ -154,6 +158,7 @@ impl ItemTree { | |||
154 | 158 | ||
155 | imports.shrink_to_fit(); | 159 | imports.shrink_to_fit(); |
156 | extern_crates.shrink_to_fit(); | 160 | extern_crates.shrink_to_fit(); |
161 | extern_blocks.shrink_to_fit(); | ||
157 | functions.shrink_to_fit(); | 162 | functions.shrink_to_fit(); |
158 | params.shrink_to_fit(); | 163 | params.shrink_to_fit(); |
159 | structs.shrink_to_fit(); | 164 | structs.shrink_to_fit(); |
@@ -203,6 +208,10 @@ impl ItemTree { | |||
203 | } | 208 | } |
204 | } | 209 | } |
205 | 210 | ||
211 | pub fn pretty_print(&self) -> String { | ||
212 | pretty::print_item_tree(self) | ||
213 | } | ||
214 | |||
206 | fn data(&self) -> &ItemTreeData { | 215 | fn data(&self) -> &ItemTreeData { |
207 | self.data.as_ref().expect("attempted to access data of empty ItemTree") | 216 | self.data.as_ref().expect("attempted to access data of empty ItemTree") |
208 | } | 217 | } |
@@ -239,6 +248,7 @@ static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module(ModPath::from_kind(P | |||
239 | struct ItemTreeData { | 248 | struct ItemTreeData { |
240 | imports: Arena<Import>, | 249 | imports: Arena<Import>, |
241 | extern_crates: Arena<ExternCrate>, | 250 | extern_crates: Arena<ExternCrate>, |
251 | extern_blocks: Arena<ExternBlock>, | ||
242 | functions: Arena<Function>, | 252 | functions: Arena<Function>, |
243 | params: Arena<Param>, | 253 | params: Arena<Param>, |
244 | structs: Arena<Struct>, | 254 | structs: Arena<Struct>, |
@@ -432,6 +442,7 @@ macro_rules! mod_items { | |||
432 | mod_items! { | 442 | mod_items! { |
433 | Import in imports -> ast::Use, | 443 | Import in imports -> ast::Use, |
434 | ExternCrate in extern_crates -> ast::ExternCrate, | 444 | ExternCrate in extern_crates -> ast::ExternCrate, |
445 | ExternBlock in extern_blocks -> ast::ExternBlock, | ||
435 | Function in functions -> ast::Fn, | 446 | Function in functions -> ast::Fn, |
436 | Struct in structs -> ast::Struct, | 447 | Struct in structs -> ast::Struct, |
437 | Union in unions -> ast::Union, | 448 | Union in unions -> ast::Union, |
@@ -508,6 +519,13 @@ pub struct ExternCrate { | |||
508 | } | 519 | } |
509 | 520 | ||
510 | #[derive(Debug, Clone, Eq, PartialEq)] | 521 | #[derive(Debug, Clone, Eq, PartialEq)] |
522 | pub struct ExternBlock { | ||
523 | pub abi: Option<Interned<str>>, | ||
524 | pub ast_id: FileAstId<ast::ExternBlock>, | ||
525 | pub children: Box<[ModItem]>, | ||
526 | } | ||
527 | |||
528 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
511 | pub struct Function { | 529 | pub struct Function { |
512 | pub name: Name, | 530 | pub name: Name, |
513 | pub visibility: RawVisibilityId, | 531 | pub visibility: RawVisibilityId, |
@@ -549,17 +567,6 @@ pub struct Struct { | |||
549 | pub generic_params: Interned<GenericParams>, | 567 | pub generic_params: Interned<GenericParams>, |
550 | pub fields: Fields, | 568 | pub fields: Fields, |
551 | pub ast_id: FileAstId<ast::Struct>, | 569 | pub ast_id: FileAstId<ast::Struct>, |
552 | pub kind: StructDefKind, | ||
553 | } | ||
554 | |||
555 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
556 | pub enum StructDefKind { | ||
557 | /// `struct S { ... }` - type namespace only. | ||
558 | Record, | ||
559 | /// `struct S(...);` | ||
560 | Tuple, | ||
561 | /// `struct S;` | ||
562 | Unit, | ||
563 | } | 570 | } |
564 | 571 | ||
565 | #[derive(Debug, Clone, Eq, PartialEq)] | 572 | #[derive(Debug, Clone, Eq, PartialEq)] |
@@ -691,6 +698,7 @@ impl ModItem { | |||
691 | match self { | 698 | match self { |
692 | ModItem::Import(_) | 699 | ModItem::Import(_) |
693 | | ModItem::ExternCrate(_) | 700 | | ModItem::ExternCrate(_) |
701 | | ModItem::ExternBlock(_) | ||
694 | | ModItem::Struct(_) | 702 | | ModItem::Struct(_) |
695 | | ModItem::Union(_) | 703 | | ModItem::Union(_) |
696 | | ModItem::Enum(_) | 704 | | ModItem::Enum(_) |
@@ -715,6 +723,7 @@ impl ModItem { | |||
715 | match self { | 723 | match self { |
716 | ModItem::Import(it) => tree[it.index].ast_id().upcast(), | 724 | ModItem::Import(it) => tree[it.index].ast_id().upcast(), |
717 | ModItem::ExternCrate(it) => tree[it.index].ast_id().upcast(), | 725 | ModItem::ExternCrate(it) => tree[it.index].ast_id().upcast(), |
726 | ModItem::ExternBlock(it) => tree[it.index].ast_id().upcast(), | ||
718 | ModItem::Function(it) => tree[it.index].ast_id().upcast(), | 727 | ModItem::Function(it) => tree[it.index].ast_id().upcast(), |
719 | ModItem::Struct(it) => tree[it.index].ast_id().upcast(), | 728 | ModItem::Struct(it) => tree[it.index].ast_id().upcast(), |
720 | ModItem::Union(it) => tree[it.index].ast_id().upcast(), | 729 | ModItem::Union(it) => tree[it.index].ast_id().upcast(), |
@@ -774,6 +783,10 @@ impl<T> IdRange<T> { | |||
774 | fn new(range: Range<Idx<T>>) -> Self { | 783 | fn new(range: Range<Idx<T>>) -> Self { |
775 | Self { range: range.start.into_raw().into()..range.end.into_raw().into(), _p: PhantomData } | 784 | Self { range: range.start.into_raw().into()..range.end.into_raw().into(), _p: PhantomData } |
776 | } | 785 | } |
786 | |||
787 | fn is_empty(&self) -> bool { | ||
788 | self.range.is_empty() | ||
789 | } | ||
777 | } | 790 | } |
778 | 791 | ||
779 | impl<T> Iterator for IdRange<T> { | 792 | impl<T> Iterator for IdRange<T> { |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index fe348091d..91cf75371 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -147,9 +147,7 @@ impl<'a> Ctx<'a> { | |||
147 | ast::Item::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), | 147 | ast::Item::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), |
148 | ast::Item::MacroRules(ast) => self.lower_macro_rules(ast).map(Into::into), | 148 | ast::Item::MacroRules(ast) => self.lower_macro_rules(ast).map(Into::into), |
149 | ast::Item::MacroDef(ast) => self.lower_macro_def(ast).map(Into::into), | 149 | ast::Item::MacroDef(ast) => self.lower_macro_def(ast).map(Into::into), |
150 | ast::Item::ExternBlock(ast) => { | 150 | ast::Item::ExternBlock(ast) => Some(self.lower_extern_block(ast).into()), |
151 | Some(ModItems(self.lower_extern_block(ast).into_iter().collect::<SmallVec<_>>())) | ||
152 | } | ||
153 | }; | 151 | }; |
154 | 152 | ||
155 | if !attrs.is_empty() { | 153 | if !attrs.is_empty() { |
@@ -230,12 +228,7 @@ impl<'a> Ctx<'a> { | |||
230 | let generic_params = self.lower_generic_params(GenericsOwner::Struct, strukt); | 228 | let generic_params = self.lower_generic_params(GenericsOwner::Struct, strukt); |
231 | let fields = self.lower_fields(&strukt.kind()); | 229 | let fields = self.lower_fields(&strukt.kind()); |
232 | let ast_id = self.source_ast_id_map.ast_id(strukt); | 230 | let ast_id = self.source_ast_id_map.ast_id(strukt); |
233 | let kind = match strukt.kind() { | 231 | let res = Struct { name, visibility, generic_params, fields, ast_id }; |
234 | ast::StructKind::Record(_) => StructDefKind::Record, | ||
235 | ast::StructKind::Tuple(_) => StructDefKind::Tuple, | ||
236 | ast::StructKind::Unit => StructDefKind::Unit, | ||
237 | }; | ||
238 | let res = Struct { name, visibility, generic_params, fields, ast_id, kind }; | ||
239 | Some(id(self.data().structs.alloc(res))) | 232 | Some(id(self.data().structs.alloc(res))) |
240 | } | 233 | } |
241 | 234 | ||
@@ -397,19 +390,7 @@ impl<'a> Ctx<'a> { | |||
397 | ret_type | 390 | ret_type |
398 | }; | 391 | }; |
399 | 392 | ||
400 | let abi = func.abi().map(|abi| { | 393 | let abi = func.abi().map(lower_abi); |
401 | // FIXME: Abi::abi() -> Option<SyntaxToken>? | ||
402 | match abi.syntax().last_token() { | ||
403 | Some(tok) if tok.kind() == SyntaxKind::STRING => { | ||
404 | // FIXME: Better way to unescape? | ||
405 | Interned::new_str(tok.text().trim_matches('"')) | ||
406 | } | ||
407 | _ => { | ||
408 | // `extern` default to be `extern "C"`. | ||
409 | Interned::new_str("C") | ||
410 | } | ||
411 | } | ||
412 | }); | ||
413 | 394 | ||
414 | let ast_id = self.source_ast_id_map.ast_id(func); | 395 | let ast_id = self.source_ast_id_map.ast_id(func); |
415 | 396 | ||
@@ -647,8 +628,10 @@ impl<'a> Ctx<'a> { | |||
647 | Some(id(self.data().macro_defs.alloc(res))) | 628 | Some(id(self.data().macro_defs.alloc(res))) |
648 | } | 629 | } |
649 | 630 | ||
650 | fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> { | 631 | fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> FileItemTreeId<ExternBlock> { |
651 | block.extern_item_list().map_or(Vec::new(), |list| { | 632 | let ast_id = self.source_ast_id_map.ast_id(block); |
633 | let abi = block.abi().map(lower_abi); | ||
634 | let children: Box<[_]> = block.extern_item_list().map_or(Box::new([]), |list| { | ||
652 | list.extern_items() | 635 | list.extern_items() |
653 | .filter_map(|item| { | 636 | .filter_map(|item| { |
654 | self.collect_inner_items(item.syntax()); | 637 | self.collect_inner_items(item.syntax()); |
@@ -673,13 +656,20 @@ impl<'a> Ctx<'a> { | |||
673 | self.data().type_aliases[foreign_ty.index].is_extern = true; | 656 | self.data().type_aliases[foreign_ty.index].is_extern = true; |
674 | foreign_ty.into() | 657 | foreign_ty.into() |
675 | } | 658 | } |
676 | ast::ExternItem::MacroCall(_) => return None, | 659 | ast::ExternItem::MacroCall(call) => { |
660 | // FIXME: we need some way of tracking that the macro call is in an | ||
661 | // extern block | ||
662 | self.lower_macro_call(&call)?.into() | ||
663 | } | ||
677 | }; | 664 | }; |
678 | self.add_attrs(id.into(), attrs); | 665 | self.add_attrs(id.into(), attrs); |
679 | Some(id) | 666 | Some(id) |
680 | }) | 667 | }) |
681 | .collect() | 668 | .collect() |
682 | }) | 669 | }); |
670 | |||
671 | let res = ExternBlock { abi, ast_id, children }; | ||
672 | id(self.data().extern_blocks.alloc(res)) | ||
683 | } | 673 | } |
684 | 674 | ||
685 | /// Lowers generics defined on `node` and collects inner items defined within. | 675 | /// Lowers generics defined on `node` and collects inner items defined within. |
@@ -879,3 +869,17 @@ fn is_intrinsic_fn_unsafe(name: &Name) -> bool { | |||
879 | ] | 869 | ] |
880 | .contains(&name) | 870 | .contains(&name) |
881 | } | 871 | } |
872 | |||
873 | fn lower_abi(abi: ast::Abi) -> Interned<str> { | ||
874 | // FIXME: Abi::abi() -> Option<SyntaxToken>? | ||
875 | match abi.syntax().last_token() { | ||
876 | Some(tok) if tok.kind() == SyntaxKind::STRING => { | ||
877 | // FIXME: Better way to unescape? | ||
878 | Interned::new_str(tok.text().trim_matches('"')) | ||
879 | } | ||
880 | _ => { | ||
881 | // `extern` default to be `extern "C"`. | ||
882 | Interned::new_str("C") | ||
883 | } | ||
884 | } | ||
885 | } | ||
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs new file mode 100644 index 000000000..5ec02d1be --- /dev/null +++ b/crates/hir_def/src/item_tree/pretty.rs | |||
@@ -0,0 +1,525 @@ | |||
1 | //! `ItemTree` debug printer. | ||
2 | |||
3 | use std::fmt::{self, Write}; | ||
4 | |||
5 | use crate::{attr::RawAttrs, visibility::RawVisibility}; | ||
6 | |||
7 | use super::*; | ||
8 | |||
9 | pub(super) fn print_item_tree(tree: &ItemTree) -> String { | ||
10 | let mut p = Printer { tree, buf: String::new(), indent_level: 0, needs_indent: true }; | ||
11 | |||
12 | if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) { | ||
13 | p.print_attrs(attrs, true); | ||
14 | } | ||
15 | p.blank(); | ||
16 | |||
17 | for item in tree.top_level_items() { | ||
18 | p.print_mod_item(*item); | ||
19 | } | ||
20 | |||
21 | let mut s = p.buf.trim_end_matches('\n').to_string(); | ||
22 | s.push('\n'); | ||
23 | s | ||
24 | } | ||
25 | |||
26 | macro_rules! w { | ||
27 | ($dst:expr, $($arg:tt)*) => { | ||
28 | drop(write!($dst, $($arg)*)) | ||
29 | }; | ||
30 | } | ||
31 | |||
32 | macro_rules! wln { | ||
33 | ($dst:expr) => { | ||
34 | drop(writeln!($dst)) | ||
35 | }; | ||
36 | ($dst:expr, $($arg:tt)*) => { | ||
37 | drop(writeln!($dst, $($arg)*)) | ||
38 | }; | ||
39 | } | ||
40 | |||
41 | struct Printer<'a> { | ||
42 | tree: &'a ItemTree, | ||
43 | buf: String, | ||
44 | indent_level: usize, | ||
45 | needs_indent: bool, | ||
46 | } | ||
47 | |||
48 | impl<'a> Printer<'a> { | ||
49 | fn indented(&mut self, f: impl FnOnce(&mut Self)) { | ||
50 | self.indent_level += 1; | ||
51 | wln!(self); | ||
52 | f(self); | ||
53 | self.indent_level -= 1; | ||
54 | self.buf = self.buf.trim_end_matches('\n').to_string(); | ||
55 | } | ||
56 | |||
57 | /// Ensures that a blank line is output before the next text. | ||
58 | fn blank(&mut self) { | ||
59 | let mut iter = self.buf.chars().rev().fuse(); | ||
60 | match (iter.next(), iter.next()) { | ||
61 | (Some('\n'), Some('\n')) | (Some('\n'), None) | (None, None) => {} | ||
62 | (Some('\n'), Some(_)) => { | ||
63 | self.buf.push('\n'); | ||
64 | } | ||
65 | (Some(_), _) => { | ||
66 | self.buf.push('\n'); | ||
67 | self.buf.push('\n'); | ||
68 | } | ||
69 | (None, Some(_)) => unreachable!(), | ||
70 | } | ||
71 | } | ||
72 | |||
73 | fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool) { | ||
74 | let inner = if inner { "!" } else { "" }; | ||
75 | for attr in &**attrs { | ||
76 | wln!( | ||
77 | self, | ||
78 | "#{}[{}{}] // {:?}", | ||
79 | inner, | ||
80 | attr.path, | ||
81 | attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(), | ||
82 | attr.id, | ||
83 | ); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | fn print_attrs_of(&mut self, of: impl Into<AttrOwner>) { | ||
88 | if let Some(attrs) = self.tree.attrs.get(&of.into()) { | ||
89 | self.print_attrs(attrs, false); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | fn print_visibility(&mut self, vis: RawVisibilityId) { | ||
94 | match &self.tree[vis] { | ||
95 | RawVisibility::Module(path) => w!(self, "pub({}) ", path), | ||
96 | RawVisibility::Public => w!(self, "pub "), | ||
97 | }; | ||
98 | } | ||
99 | |||
100 | fn print_fields(&mut self, fields: &Fields) { | ||
101 | match fields { | ||
102 | Fields::Record(fields) => { | ||
103 | w!(self, " {{"); | ||
104 | self.indented(|this| { | ||
105 | for field in fields.clone() { | ||
106 | let Field { visibility, name, type_ref } = &this.tree[field]; | ||
107 | this.print_attrs_of(field); | ||
108 | this.print_visibility(*visibility); | ||
109 | w!(this, "{}: ", name); | ||
110 | this.print_type_ref(type_ref); | ||
111 | wln!(this, ","); | ||
112 | } | ||
113 | }); | ||
114 | w!(self, "}}"); | ||
115 | } | ||
116 | Fields::Tuple(fields) => { | ||
117 | w!(self, "("); | ||
118 | self.indented(|this| { | ||
119 | for field in fields.clone() { | ||
120 | let Field { visibility, name, type_ref } = &this.tree[field]; | ||
121 | this.print_attrs_of(field); | ||
122 | this.print_visibility(*visibility); | ||
123 | w!(this, "{}: ", name); | ||
124 | this.print_type_ref(type_ref); | ||
125 | wln!(this, ","); | ||
126 | } | ||
127 | }); | ||
128 | w!(self, ")"); | ||
129 | } | ||
130 | Fields::Unit => {} | ||
131 | } | ||
132 | } | ||
133 | |||
134 | fn print_mod_item(&mut self, item: ModItem) { | ||
135 | self.print_attrs_of(item); | ||
136 | |||
137 | match item { | ||
138 | ModItem::Import(it) => { | ||
139 | let Import { visibility, path, is_glob, alias, ast_id: _, index } = &self.tree[it]; | ||
140 | self.print_visibility(*visibility); | ||
141 | w!(self, "use {}", path); | ||
142 | if *is_glob { | ||
143 | w!(self, "::*"); | ||
144 | } | ||
145 | if let Some(alias) = alias { | ||
146 | w!(self, " as {}", alias); | ||
147 | } | ||
148 | wln!(self, "; // {}", index); | ||
149 | } | ||
150 | ModItem::ExternCrate(it) => { | ||
151 | let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it]; | ||
152 | self.print_visibility(*visibility); | ||
153 | w!(self, "extern crate {}", name); | ||
154 | if let Some(alias) = alias { | ||
155 | w!(self, " as {}", alias); | ||
156 | } | ||
157 | wln!(self, ";"); | ||
158 | } | ||
159 | ModItem::ExternBlock(it) => { | ||
160 | let ExternBlock { abi, ast_id: _, children } = &self.tree[it]; | ||
161 | w!(self, "extern "); | ||
162 | if let Some(abi) = abi { | ||
163 | w!(self, "\"{}\" ", abi); | ||
164 | } | ||
165 | w!(self, "{{"); | ||
166 | self.indented(|this| { | ||
167 | for child in &**children { | ||
168 | this.print_mod_item(*child); | ||
169 | } | ||
170 | }); | ||
171 | wln!(self, "}}"); | ||
172 | } | ||
173 | ModItem::Function(it) => { | ||
174 | let Function { | ||
175 | name, | ||
176 | visibility, | ||
177 | generic_params: _, // FIXME print these somehow | ||
178 | abi, | ||
179 | params, | ||
180 | ret_type, | ||
181 | ast_id: _, | ||
182 | flags, | ||
183 | } = &self.tree[it]; | ||
184 | if flags.bits != 0 { | ||
185 | wln!(self, "// flags = 0x{:X}", flags.bits); | ||
186 | } | ||
187 | self.print_visibility(*visibility); | ||
188 | if let Some(abi) = abi { | ||
189 | w!(self, "extern \"{}\" ", abi); | ||
190 | } | ||
191 | w!(self, "fn {}(", name); | ||
192 | if !params.is_empty() { | ||
193 | self.indented(|this| { | ||
194 | for param in params.clone() { | ||
195 | this.print_attrs_of(param); | ||
196 | match &this.tree[param] { | ||
197 | Param::Normal(ty) => { | ||
198 | w!(this, "_: "); | ||
199 | this.print_type_ref(ty); | ||
200 | wln!(this, ","); | ||
201 | } | ||
202 | Param::Varargs => { | ||
203 | wln!(this, "..."); | ||
204 | } | ||
205 | }; | ||
206 | } | ||
207 | }); | ||
208 | } | ||
209 | w!(self, ") -> "); | ||
210 | self.print_type_ref(ret_type); | ||
211 | wln!(self, ";"); | ||
212 | } | ||
213 | ModItem::Struct(it) => { | ||
214 | let Struct { visibility, name, fields, generic_params: _, ast_id: _ } = | ||
215 | &self.tree[it]; | ||
216 | self.print_visibility(*visibility); | ||
217 | w!(self, "struct {}", name); | ||
218 | self.print_fields(fields); | ||
219 | if matches!(fields, Fields::Record(_)) { | ||
220 | wln!(self); | ||
221 | } else { | ||
222 | wln!(self, ";"); | ||
223 | } | ||
224 | } | ||
225 | ModItem::Union(it) => { | ||
226 | let Union { name, visibility, fields, generic_params: _, ast_id: _ } = | ||
227 | &self.tree[it]; | ||
228 | self.print_visibility(*visibility); | ||
229 | w!(self, "union {}", name); | ||
230 | self.print_fields(fields); | ||
231 | if matches!(fields, Fields::Record(_)) { | ||
232 | wln!(self); | ||
233 | } else { | ||
234 | wln!(self, ";"); | ||
235 | } | ||
236 | } | ||
237 | ModItem::Enum(it) => { | ||
238 | let Enum { name, visibility, variants, generic_params: _, ast_id: _ } = | ||
239 | &self.tree[it]; | ||
240 | self.print_visibility(*visibility); | ||
241 | w!(self, "enum {} {{", name); | ||
242 | self.indented(|this| { | ||
243 | for variant in variants.clone() { | ||
244 | let Variant { name, fields } = &this.tree[variant]; | ||
245 | this.print_attrs_of(variant); | ||
246 | w!(this, "{}", name); | ||
247 | this.print_fields(fields); | ||
248 | wln!(this, ","); | ||
249 | } | ||
250 | }); | ||
251 | wln!(self, "}}"); | ||
252 | } | ||
253 | ModItem::Const(it) => { | ||
254 | let Const { name, visibility, type_ref, ast_id: _ } = &self.tree[it]; | ||
255 | self.print_visibility(*visibility); | ||
256 | w!(self, "const "); | ||
257 | match name { | ||
258 | Some(name) => w!(self, "{}", name), | ||
259 | None => w!(self, "_"), | ||
260 | } | ||
261 | w!(self, ": "); | ||
262 | self.print_type_ref(type_ref); | ||
263 | wln!(self, " = _;"); | ||
264 | } | ||
265 | ModItem::Static(it) => { | ||
266 | let Static { name, visibility, mutable, is_extern, type_ref, ast_id: _ } = | ||
267 | &self.tree[it]; | ||
268 | self.print_visibility(*visibility); | ||
269 | w!(self, "static "); | ||
270 | if *mutable { | ||
271 | w!(self, "mut "); | ||
272 | } | ||
273 | w!(self, "{}: ", name); | ||
274 | self.print_type_ref(type_ref); | ||
275 | w!(self, " = _;"); | ||
276 | if *is_extern { | ||
277 | w!(self, " // extern"); | ||
278 | } | ||
279 | wln!(self); | ||
280 | } | ||
281 | ModItem::Trait(it) => { | ||
282 | let Trait { | ||
283 | name, | ||
284 | visibility, | ||
285 | is_auto, | ||
286 | is_unsafe, | ||
287 | bounds, | ||
288 | items, | ||
289 | generic_params: _, | ||
290 | ast_id: _, | ||
291 | } = &self.tree[it]; | ||
292 | self.print_visibility(*visibility); | ||
293 | if *is_unsafe { | ||
294 | w!(self, "unsafe "); | ||
295 | } | ||
296 | if *is_auto { | ||
297 | w!(self, "auto "); | ||
298 | } | ||
299 | w!(self, "trait {}", name); | ||
300 | if !bounds.is_empty() { | ||
301 | w!(self, ": "); | ||
302 | self.print_type_bounds(bounds); | ||
303 | } | ||
304 | w!(self, " {{"); | ||
305 | self.indented(|this| { | ||
306 | for item in &**items { | ||
307 | this.print_mod_item((*item).into()); | ||
308 | } | ||
309 | }); | ||
310 | wln!(self, "}}"); | ||
311 | } | ||
312 | ModItem::Impl(it) => { | ||
313 | let Impl { | ||
314 | target_trait, | ||
315 | self_ty, | ||
316 | is_negative, | ||
317 | items, | ||
318 | generic_params: _, | ||
319 | ast_id: _, | ||
320 | } = &self.tree[it]; | ||
321 | w!(self, "impl "); | ||
322 | if *is_negative { | ||
323 | w!(self, "!"); | ||
324 | } | ||
325 | if let Some(tr) = target_trait { | ||
326 | self.print_path(&tr.path); | ||
327 | w!(self, " for "); | ||
328 | } | ||
329 | self.print_type_ref(self_ty); | ||
330 | w!(self, " {{"); | ||
331 | self.indented(|this| { | ||
332 | for item in &**items { | ||
333 | this.print_mod_item((*item).into()); | ||
334 | } | ||
335 | }); | ||
336 | wln!(self, "}}"); | ||
337 | } | ||
338 | ModItem::TypeAlias(it) => { | ||
339 | let TypeAlias { | ||
340 | name, | ||
341 | visibility, | ||
342 | bounds, | ||
343 | type_ref, | ||
344 | is_extern, | ||
345 | generic_params: _, | ||
346 | ast_id: _, | ||
347 | } = &self.tree[it]; | ||
348 | self.print_visibility(*visibility); | ||
349 | w!(self, "type {}", name); | ||
350 | if !bounds.is_empty() { | ||
351 | w!(self, ": "); | ||
352 | self.print_type_bounds(bounds); | ||
353 | } | ||
354 | if let Some(ty) = type_ref { | ||
355 | w!(self, " = "); | ||
356 | self.print_type_ref(ty); | ||
357 | } | ||
358 | w!(self, ";"); | ||
359 | if *is_extern { | ||
360 | w!(self, " // extern"); | ||
361 | } | ||
362 | wln!(self); | ||
363 | } | ||
364 | ModItem::Mod(it) => { | ||
365 | let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it]; | ||
366 | self.print_visibility(*visibility); | ||
367 | w!(self, "mod {}", name); | ||
368 | match kind { | ||
369 | ModKind::Inline { items } => { | ||
370 | w!(self, " {{"); | ||
371 | self.indented(|this| { | ||
372 | for item in &**items { | ||
373 | this.print_mod_item((*item).into()); | ||
374 | } | ||
375 | }); | ||
376 | wln!(self, "}}"); | ||
377 | } | ||
378 | ModKind::Outline {} => { | ||
379 | wln!(self, ";"); | ||
380 | } | ||
381 | } | ||
382 | } | ||
383 | ModItem::MacroCall(it) => { | ||
384 | let MacroCall { path, ast_id: _, fragment: _ } = &self.tree[it]; | ||
385 | wln!(self, "{}!(...);", path); | ||
386 | } | ||
387 | ModItem::MacroRules(it) => { | ||
388 | let MacroRules { name, ast_id: _ } = &self.tree[it]; | ||
389 | wln!(self, "macro_rules! {} {{ ... }}", name); | ||
390 | } | ||
391 | ModItem::MacroDef(it) => { | ||
392 | let MacroDef { name, visibility, ast_id: _ } = &self.tree[it]; | ||
393 | self.print_visibility(*visibility); | ||
394 | wln!(self, "macro {} {{ ... }}", name); | ||
395 | } | ||
396 | } | ||
397 | |||
398 | self.blank(); | ||
399 | } | ||
400 | |||
401 | fn print_type_ref(&mut self, type_ref: &TypeRef) { | ||
402 | // FIXME: deduplicate with `HirDisplay` impl | ||
403 | match type_ref { | ||
404 | TypeRef::Never => w!(self, "!"), | ||
405 | TypeRef::Placeholder => w!(self, "_"), | ||
406 | TypeRef::Tuple(fields) => { | ||
407 | w!(self, "("); | ||
408 | for (i, field) in fields.iter().enumerate() { | ||
409 | if i != 0 { | ||
410 | w!(self, ", "); | ||
411 | } | ||
412 | self.print_type_ref(field); | ||
413 | } | ||
414 | w!(self, ")"); | ||
415 | } | ||
416 | TypeRef::Path(path) => self.print_path(path), | ||
417 | TypeRef::RawPtr(pointee, mtbl) => { | ||
418 | let mtbl = match mtbl { | ||
419 | Mutability::Shared => "*const", | ||
420 | Mutability::Mut => "*mut", | ||
421 | }; | ||
422 | w!(self, "{} ", mtbl); | ||
423 | self.print_type_ref(pointee); | ||
424 | } | ||
425 | TypeRef::Reference(pointee, lt, mtbl) => { | ||
426 | let mtbl = match mtbl { | ||
427 | Mutability::Shared => "", | ||
428 | Mutability::Mut => "mut ", | ||
429 | }; | ||
430 | w!(self, "&"); | ||
431 | if let Some(lt) = lt { | ||
432 | w!(self, "{} ", lt.name); | ||
433 | } | ||
434 | w!(self, "{}", mtbl); | ||
435 | self.print_type_ref(pointee); | ||
436 | } | ||
437 | TypeRef::Array(elem, len) => { | ||
438 | w!(self, "["); | ||
439 | self.print_type_ref(elem); | ||
440 | w!(self, "; {}]", len); | ||
441 | } | ||
442 | TypeRef::Slice(elem) => { | ||
443 | w!(self, "["); | ||
444 | self.print_type_ref(elem); | ||
445 | w!(self, "]"); | ||
446 | } | ||
447 | TypeRef::Fn(args_and_ret, varargs) => { | ||
448 | let (ret, args) = | ||
449 | args_and_ret.split_last().expect("TypeRef::Fn is missing return type"); | ||
450 | w!(self, "fn("); | ||
451 | for (i, arg) in args.iter().enumerate() { | ||
452 | if i != 0 { | ||
453 | w!(self, ", "); | ||
454 | } | ||
455 | self.print_type_ref(arg); | ||
456 | } | ||
457 | if *varargs { | ||
458 | if !args.is_empty() { | ||
459 | w!(self, ", "); | ||
460 | } | ||
461 | w!(self, "..."); | ||
462 | } | ||
463 | w!(self, ") -> "); | ||
464 | self.print_type_ref(ret); | ||
465 | } | ||
466 | TypeRef::Macro(_ast_id) => { | ||
467 | w!(self, "<macro>"); | ||
468 | } | ||
469 | TypeRef::Error => drop(write!(self, "{{unknown}}")), | ||
470 | TypeRef::ImplTrait(bounds) => { | ||
471 | w!(self, "impl "); | ||
472 | self.print_type_bounds(bounds); | ||
473 | } | ||
474 | TypeRef::DynTrait(bounds) => { | ||
475 | w!(self, "dyn "); | ||
476 | self.print_type_bounds(bounds); | ||
477 | } | ||
478 | } | ||
479 | } | ||
480 | |||
481 | fn print_type_bounds(&mut self, bounds: &[TypeBound]) { | ||
482 | for (i, bound) in bounds.iter().enumerate() { | ||
483 | if i != 0 { | ||
484 | w!(self, " + "); | ||
485 | } | ||
486 | |||
487 | match bound { | ||
488 | TypeBound::Path(path) => self.print_path(path), | ||
489 | TypeBound::Lifetime(lt) => w!(self, "{}", lt.name), | ||
490 | TypeBound::Error => w!(self, "{{unknown}}"), | ||
491 | } | ||
492 | } | ||
493 | } | ||
494 | |||
495 | fn print_path(&mut self, path: &Path) { | ||
496 | if path.type_anchor().is_none() | ||
497 | && path.segments().iter().all(|seg| seg.args_and_bindings.is_none()) | ||
498 | { | ||
499 | w!(self, "{}", path.mod_path()); | ||
500 | } else { | ||
501 | // too complicated, just use `Debug` | ||
502 | w!(self, "{:?}", path); | ||
503 | } | ||
504 | } | ||
505 | } | ||
506 | |||
507 | impl<'a> Write for Printer<'a> { | ||
508 | fn write_str(&mut self, s: &str) -> fmt::Result { | ||
509 | for line in s.split_inclusive('\n') { | ||
510 | if self.needs_indent { | ||
511 | match self.buf.chars().last() { | ||
512 | Some('\n') | None => {} | ||
513 | _ => self.buf.push('\n'), | ||
514 | } | ||
515 | self.buf.push_str(&" ".repeat(self.indent_level)); | ||
516 | self.needs_indent = false; | ||
517 | } | ||
518 | |||
519 | self.buf.push_str(line); | ||
520 | self.needs_indent = line.ends_with('\n'); | ||
521 | } | ||
522 | |||
523 | Ok(()) | ||
524 | } | ||
525 | } | ||
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs new file mode 100644 index 000000000..100ae9b97 --- /dev/null +++ b/crates/hir_def/src/item_tree/tests.rs | |||
@@ -0,0 +1,244 @@ | |||
1 | use base_db::fixture::WithFixture; | ||
2 | use expect_test::{expect, Expect}; | ||
3 | |||
4 | use crate::{db::DefDatabase, test_db::TestDB}; | ||
5 | |||
6 | fn check(ra_fixture: &str, expect: Expect) { | ||
7 | let (db, file_id) = TestDB::with_single_file(ra_fixture); | ||
8 | let item_tree = db.file_item_tree(file_id.into()); | ||
9 | let pretty = item_tree.pretty_print(); | ||
10 | expect.assert_eq(&pretty); | ||
11 | } | ||
12 | |||
13 | #[test] | ||
14 | fn imports() { | ||
15 | check( | ||
16 | r#" | ||
17 | //! file comment | ||
18 | #![no_std] | ||
19 | //! another file comment | ||
20 | |||
21 | extern crate self as renamed; | ||
22 | pub(super) extern crate bli; | ||
23 | |||
24 | pub use crate::path::{nested, items as renamed, Trait as _}; | ||
25 | use globs::*; | ||
26 | |||
27 | /// docs on import | ||
28 | use crate::{A, B}; | ||
29 | "#, | ||
30 | expect![[r##" | ||
31 | #![doc = " file comment"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
32 | #![no_std] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
33 | #![doc = " another file comment"] // AttrId { is_doc_comment: true, ast_index: 1 } | ||
34 | |||
35 | pub(self) extern crate self as renamed; | ||
36 | |||
37 | pub(super) extern crate bli; | ||
38 | |||
39 | pub use crate::path::nested; // 0 | ||
40 | |||
41 | pub use crate::path::items as renamed; // 1 | ||
42 | |||
43 | pub use crate::path::Trait as _; // 2 | ||
44 | |||
45 | pub(self) use globs::*; // 0 | ||
46 | |||
47 | #[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
48 | pub(self) use crate::A; // 0 | ||
49 | |||
50 | #[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
51 | pub(self) use crate::B; // 1 | ||
52 | "##]], | ||
53 | ); | ||
54 | } | ||
55 | |||
56 | #[test] | ||
57 | fn extern_blocks() { | ||
58 | check( | ||
59 | r#" | ||
60 | #[on_extern_block] | ||
61 | extern "C" { | ||
62 | #[on_extern_type] | ||
63 | type ExType; | ||
64 | |||
65 | #[on_extern_static] | ||
66 | static EX_STATIC: u8; | ||
67 | |||
68 | #[on_extern_fn] | ||
69 | fn ex_fn(); | ||
70 | } | ||
71 | "#, | ||
72 | expect![[r##" | ||
73 | #[on_extern_block] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
74 | extern "C" { | ||
75 | #[on_extern_type] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
76 | pub(self) type ExType; // extern | ||
77 | |||
78 | #[on_extern_static] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
79 | pub(self) static EX_STATIC: u8 = _; // extern | ||
80 | |||
81 | #[on_extern_fn] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
82 | // flags = 0x60 | ||
83 | pub(self) fn ex_fn() -> (); | ||
84 | } | ||
85 | "##]], | ||
86 | ); | ||
87 | } | ||
88 | |||
89 | #[test] | ||
90 | fn adts() { | ||
91 | check( | ||
92 | r#" | ||
93 | struct Unit; | ||
94 | |||
95 | #[derive(Debug)] | ||
96 | struct Struct { | ||
97 | /// fld docs | ||
98 | fld: (), | ||
99 | } | ||
100 | |||
101 | struct Tuple(#[attr] u8); | ||
102 | |||
103 | union Ize { | ||
104 | a: (), | ||
105 | b: (), | ||
106 | } | ||
107 | |||
108 | enum E { | ||
109 | /// comment on Unit | ||
110 | Unit, | ||
111 | /// comment on Tuple | ||
112 | Tuple(u8), | ||
113 | Struct { | ||
114 | /// comment on a: u8 | ||
115 | a: u8, | ||
116 | } | ||
117 | } | ||
118 | "#, | ||
119 | expect![[r##" | ||
120 | pub(self) struct Unit; | ||
121 | |||
122 | #[derive(Debug)] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
123 | pub(self) struct Struct { | ||
124 | #[doc = " fld docs"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
125 | pub(self) fld: (), | ||
126 | } | ||
127 | |||
128 | pub(self) struct Tuple( | ||
129 | #[attr] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
130 | pub(self) 0: u8, | ||
131 | ); | ||
132 | |||
133 | pub(self) union Ize { | ||
134 | pub(self) a: (), | ||
135 | pub(self) b: (), | ||
136 | } | ||
137 | |||
138 | pub(self) enum E { | ||
139 | #[doc = " comment on Unit"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
140 | Unit, | ||
141 | #[doc = " comment on Tuple"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
142 | Tuple( | ||
143 | pub(self) 0: u8, | ||
144 | ), | ||
145 | Struct { | ||
146 | #[doc = " comment on a: u8"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
147 | pub(self) a: u8, | ||
148 | }, | ||
149 | } | ||
150 | "##]], | ||
151 | ); | ||
152 | } | ||
153 | |||
154 | #[test] | ||
155 | fn misc() { | ||
156 | check( | ||
157 | r#" | ||
158 | pub static mut ST: () = (); | ||
159 | |||
160 | const _: Anon = (); | ||
161 | |||
162 | #[attr] | ||
163 | fn f(#[attr] arg: u8, _: ()) { | ||
164 | #![inner_attr_in_fn] | ||
165 | } | ||
166 | |||
167 | trait Tr: SuperTrait + 'lifetime { | ||
168 | type Assoc: AssocBound = Default; | ||
169 | fn method(&self); | ||
170 | } | ||
171 | "#, | ||
172 | expect![[r##" | ||
173 | pub static mut ST: () = _; | ||
174 | |||
175 | pub(self) const _: Anon = _; | ||
176 | |||
177 | #[attr] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
178 | #[inner_attr_in_fn] // AttrId { is_doc_comment: false, ast_index: 1 } | ||
179 | // flags = 0x2 | ||
180 | pub(self) fn f( | ||
181 | #[attr] // AttrId { is_doc_comment: false, ast_index: 0 } | ||
182 | _: u8, | ||
183 | _: (), | ||
184 | ) -> (); | ||
185 | |||
186 | pub(self) trait Tr: SuperTrait + 'lifetime { | ||
187 | pub(self) type Assoc: AssocBound = Default; | ||
188 | |||
189 | // flags = 0x1 | ||
190 | pub(self) fn method( | ||
191 | _: &Self, | ||
192 | ) -> (); | ||
193 | } | ||
194 | "##]], | ||
195 | ); | ||
196 | } | ||
197 | |||
198 | #[test] | ||
199 | fn modules() { | ||
200 | check( | ||
201 | r#" | ||
202 | /// outer | ||
203 | mod inline { | ||
204 | //! inner | ||
205 | |||
206 | use super::*; | ||
207 | |||
208 | fn fn_in_module() {} | ||
209 | } | ||
210 | "#, | ||
211 | expect![[r##" | ||
212 | #[doc = " outer"] // AttrId { is_doc_comment: true, ast_index: 0 } | ||
213 | #[doc = " inner"] // AttrId { is_doc_comment: true, ast_index: 1 } | ||
214 | pub(self) mod inline { | ||
215 | pub(self) use super::*; // 0 | ||
216 | |||
217 | // flags = 0x2 | ||
218 | pub(self) fn fn_in_module() -> (); | ||
219 | } | ||
220 | "##]], | ||
221 | ); | ||
222 | } | ||
223 | |||
224 | #[test] | ||
225 | fn macros() { | ||
226 | check( | ||
227 | r#" | ||
228 | macro_rules! m { | ||
229 | () => {}; | ||
230 | } | ||
231 | |||
232 | pub macro m2() {} | ||
233 | |||
234 | m!(); | ||
235 | "#, | ||
236 | expect![[r#" | ||
237 | macro_rules! m { ... } | ||
238 | |||
239 | pub macro m2 { ... } | ||
240 | |||
241 | m!(...); | ||
242 | "#]], | ||
243 | ); | ||
244 | } | ||
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 3896be25d..014ea4de4 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -16,19 +16,20 @@ use hir_expand::{ | |||
16 | FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | 16 | FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, |
17 | }; | 17 | }; |
18 | use hir_expand::{InFile, MacroCallLoc}; | 18 | use hir_expand::{InFile, MacroCallLoc}; |
19 | use itertools::Itertools; | ||
19 | use rustc_hash::{FxHashMap, FxHashSet}; | 20 | use rustc_hash::{FxHashMap, FxHashSet}; |
20 | use syntax::ast; | 21 | use syntax::ast; |
21 | 22 | ||
22 | use crate::{ | 23 | use crate::{ |
23 | attr::{AttrId, Attrs}, | 24 | attr::{Attr, AttrId, AttrInput, Attrs}, |
24 | builtin_attr, | 25 | builtin_attr, |
25 | db::DefDatabase, | 26 | db::DefDatabase, |
26 | derive_macro_as_call_id, | 27 | derive_macro_as_call_id, |
27 | intern::Interned, | 28 | intern::Interned, |
28 | item_scope::{ImportType, PerNsGlobImports}, | 29 | item_scope::{ImportType, PerNsGlobImports}, |
29 | item_tree::{ | 30 | item_tree::{ |
30 | self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod, ModItem, | 31 | self, Fields, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod, |
31 | ModKind, StructDefKind, | 32 | ModItem, ModKind, |
32 | }, | 33 | }, |
33 | macro_call_as_call_id, | 34 | macro_call_as_call_id, |
34 | nameres::{ | 35 | nameres::{ |
@@ -94,14 +95,16 @@ pub(super) fn collect_defs( | |||
94 | unresolved_imports: Vec::new(), | 95 | unresolved_imports: Vec::new(), |
95 | resolved_imports: Vec::new(), | 96 | resolved_imports: Vec::new(), |
96 | 97 | ||
97 | unexpanded_macros: Vec::new(), | 98 | unresolved_macros: Vec::new(), |
98 | mod_dirs: FxHashMap::default(), | 99 | mod_dirs: FxHashMap::default(), |
99 | cfg_options, | 100 | cfg_options, |
100 | proc_macros, | 101 | proc_macros, |
101 | exports_proc_macros: false, | 102 | exports_proc_macros: false, |
102 | from_glob_import: Default::default(), | 103 | from_glob_import: Default::default(), |
103 | ignore_attrs_on: FxHashSet::default(), | 104 | skip_attrs: Default::default(), |
104 | derive_helpers_in_scope: FxHashMap::default(), | 105 | derive_helpers_in_scope: Default::default(), |
106 | registered_attrs: Default::default(), | ||
107 | registered_tools: Default::default(), | ||
105 | }; | 108 | }; |
106 | match block { | 109 | match block { |
107 | Some(block) => { | 110 | Some(block) => { |
@@ -237,7 +240,7 @@ struct DefCollector<'a> { | |||
237 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, | 240 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, |
238 | unresolved_imports: Vec<ImportDirective>, | 241 | unresolved_imports: Vec<ImportDirective>, |
239 | resolved_imports: Vec<ImportDirective>, | 242 | resolved_imports: Vec<ImportDirective>, |
240 | unexpanded_macros: Vec<MacroDirective>, | 243 | unresolved_macros: Vec<MacroDirective>, |
241 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, | 244 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, |
242 | cfg_options: &'a CfgOptions, | 245 | cfg_options: &'a CfgOptions, |
243 | /// List of procedural macros defined by this crate. This is read from the dynamic library | 246 | /// List of procedural macros defined by this crate. This is read from the dynamic library |
@@ -247,10 +250,20 @@ struct DefCollector<'a> { | |||
247 | proc_macros: Vec<(Name, ProcMacroExpander)>, | 250 | proc_macros: Vec<(Name, ProcMacroExpander)>, |
248 | exports_proc_macros: bool, | 251 | exports_proc_macros: bool, |
249 | from_glob_import: PerNsGlobImports, | 252 | from_glob_import: PerNsGlobImports, |
250 | ignore_attrs_on: FxHashSet<InFile<ModItem>>, | 253 | /// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute. |
254 | /// This map is used to skip all attributes up to and including the one that failed to resolve, | ||
255 | /// in order to not expand them twice. | ||
256 | /// | ||
257 | /// This also stores the attributes to skip when we resolve derive helpers and non-macro | ||
258 | /// non-builtin attributes in general. | ||
259 | skip_attrs: FxHashMap<InFile<ModItem>, AttrId>, | ||
251 | /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper | 260 | /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper |
252 | /// attributes. | 261 | /// attributes. |
253 | derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>, | 262 | derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>, |
263 | /// Custom attributes registered with `#![register_attr]`. | ||
264 | registered_attrs: Vec<String>, | ||
265 | /// Custom tool modules registered with `#![register_tool]`. | ||
266 | registered_tools: Vec<String>, | ||
254 | } | 267 | } |
255 | 268 | ||
256 | impl DefCollector<'_> { | 269 | impl DefCollector<'_> { |
@@ -259,11 +272,39 @@ impl DefCollector<'_> { | |||
259 | let item_tree = self.db.file_item_tree(file_id.into()); | 272 | let item_tree = self.db.file_item_tree(file_id.into()); |
260 | let module_id = self.def_map.root; | 273 | let module_id = self.def_map.root; |
261 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; | 274 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; |
262 | if item_tree | 275 | |
263 | .top_level_attrs(self.db, self.def_map.krate) | 276 | let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); |
264 | .cfg() | 277 | if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) { |
265 | .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) | 278 | // Process other crate-level attributes. |
266 | { | 279 | for attr in &*attrs { |
280 | let attr_name = match attr.path.as_ident() { | ||
281 | Some(name) => name, | ||
282 | None => continue, | ||
283 | }; | ||
284 | |||
285 | let registered_name = if *attr_name == hir_expand::name![register_attr] | ||
286 | || *attr_name == hir_expand::name![register_tool] | ||
287 | { | ||
288 | match &attr.input { | ||
289 | Some(AttrInput::TokenTree(subtree)) => match &*subtree.token_trees { | ||
290 | [tt::TokenTree::Leaf(tt::Leaf::Ident(name))] => name.as_name(), | ||
291 | _ => continue, | ||
292 | }, | ||
293 | _ => continue, | ||
294 | } | ||
295 | } else { | ||
296 | continue; | ||
297 | }; | ||
298 | |||
299 | if *attr_name == hir_expand::name![register_attr] { | ||
300 | self.registered_attrs.push(registered_name.to_string()); | ||
301 | cov_mark::hit!(register_attr); | ||
302 | } else { | ||
303 | self.registered_tools.push(registered_name.to_string()); | ||
304 | cov_mark::hit!(register_tool); | ||
305 | } | ||
306 | } | ||
307 | |||
267 | ModCollector { | 308 | ModCollector { |
268 | def_collector: &mut *self, | 309 | def_collector: &mut *self, |
269 | macro_depth: 0, | 310 | macro_depth: 0, |
@@ -319,7 +360,7 @@ impl DefCollector<'_> { | |||
319 | } | 360 | } |
320 | } | 361 | } |
321 | 362 | ||
322 | if self.reseed_with_unresolved_attributes() == ReachedFixedPoint::Yes { | 363 | if self.reseed_with_unresolved_attribute() == ReachedFixedPoint::Yes { |
323 | break; | 364 | break; |
324 | } | 365 | } |
325 | } | 366 | } |
@@ -362,27 +403,23 @@ impl DefCollector<'_> { | |||
362 | } | 403 | } |
363 | 404 | ||
364 | /// When the fixed-point loop reaches a stable state, we might still have some unresolved | 405 | /// When the fixed-point loop reaches a stable state, we might still have some unresolved |
365 | /// attributes (or unexpanded attribute proc macros) left over. This takes them, and feeds the | 406 | /// attributes (or unexpanded attribute proc macros) left over. This takes one of them, and |
366 | /// item they're applied to back into name resolution. | 407 | /// feeds the item it's applied to back into name resolution. |
367 | /// | 408 | /// |
368 | /// This effectively ignores the fact that the macro is there and just treats the items as | 409 | /// This effectively ignores the fact that the macro is there and just treats the items as |
369 | /// normal code. | 410 | /// normal code. |
370 | /// | 411 | /// |
371 | /// This improves UX when proc macros are turned off or don't work, and replicates the behavior | 412 | /// This improves UX when proc macros are turned off or don't work, and replicates the behavior |
372 | /// before we supported proc. attribute macros. | 413 | /// before we supported proc. attribute macros. |
373 | fn reseed_with_unresolved_attributes(&mut self) -> ReachedFixedPoint { | 414 | fn reseed_with_unresolved_attribute(&mut self) -> ReachedFixedPoint { |
374 | cov_mark::hit!(unresolved_attribute_fallback); | 415 | cov_mark::hit!(unresolved_attribute_fallback); |
375 | 416 | ||
376 | let mut added_items = false; | 417 | let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new()); |
377 | let unexpanded_macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | 418 | let pos = unresolved_macros.iter().position(|directive| { |
378 | for directive in &unexpanded_macros { | 419 | if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind { |
379 | if let MacroDirectiveKind::Attr { ast_id, mod_item, .. } = &directive.kind { | 420 | self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr); |
380 | // Make sure to only add such items once. | ||
381 | if !self.ignore_attrs_on.insert(ast_id.ast_id.with_value(*mod_item)) { | ||
382 | continue; | ||
383 | } | ||
384 | 421 | ||
385 | let file_id = self.def_map[directive.module_id].definition_source(self.db).file_id; | 422 | let file_id = ast_id.ast_id.file_id; |
386 | let item_tree = self.db.file_item_tree(file_id); | 423 | let item_tree = self.db.file_item_tree(file_id); |
387 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); | 424 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); |
388 | ModCollector { | 425 | ModCollector { |
@@ -394,14 +431,20 @@ impl DefCollector<'_> { | |||
394 | mod_dir, | 431 | mod_dir, |
395 | } | 432 | } |
396 | .collect(&[*mod_item]); | 433 | .collect(&[*mod_item]); |
397 | added_items = true; | 434 | true |
435 | } else { | ||
436 | false | ||
398 | } | 437 | } |
438 | }); | ||
439 | |||
440 | if let Some(pos) = pos { | ||
441 | unresolved_macros.remove(pos); | ||
399 | } | 442 | } |
400 | 443 | ||
401 | // The collection above might add new unresolved macros (eg. derives), so merge the lists. | 444 | // The collection above might add new unresolved macros (eg. derives), so merge the lists. |
402 | self.unexpanded_macros.extend(unexpanded_macros); | 445 | self.unresolved_macros.extend(unresolved_macros); |
403 | 446 | ||
404 | if added_items { | 447 | if pos.is_some() { |
405 | // Continue name resolution with the new data. | 448 | // Continue name resolution with the new data. |
406 | ReachedFixedPoint::No | 449 | ReachedFixedPoint::No |
407 | } else { | 450 | } else { |
@@ -873,7 +916,7 @@ impl DefCollector<'_> { | |||
873 | } | 916 | } |
874 | 917 | ||
875 | fn resolve_macros(&mut self) -> ReachedFixedPoint { | 918 | fn resolve_macros(&mut self) -> ReachedFixedPoint { |
876 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | 919 | let mut macros = std::mem::replace(&mut self.unresolved_macros, Vec::new()); |
877 | let mut resolved = Vec::new(); | 920 | let mut resolved = Vec::new(); |
878 | let mut res = ReachedFixedPoint::Yes; | 921 | let mut res = ReachedFixedPoint::Yes; |
879 | macros.retain(|directive| { | 922 | macros.retain(|directive| { |
@@ -922,14 +965,43 @@ impl DefCollector<'_> { | |||
922 | Err(UnresolvedMacro { .. }) => (), | 965 | Err(UnresolvedMacro { .. }) => (), |
923 | } | 966 | } |
924 | } | 967 | } |
925 | MacroDirectiveKind::Attr { .. } => { | 968 | MacroDirectiveKind::Attr { ast_id, mod_item, attr } => { |
926 | // not yet :) | 969 | if let Some(ident) = ast_id.path.as_ident() { |
970 | if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id.ast_id) { | ||
971 | if helpers.contains(ident) { | ||
972 | cov_mark::hit!(resolved_derive_helper); | ||
973 | |||
974 | // Resolved to derive helper. Collect the item's attributes again, | ||
975 | // starting after the derive helper. | ||
976 | let file_id = ast_id.ast_id.file_id; | ||
977 | let item_tree = self.db.file_item_tree(file_id); | ||
978 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); | ||
979 | self.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr); | ||
980 | ModCollector { | ||
981 | def_collector: &mut *self, | ||
982 | macro_depth: directive.depth, | ||
983 | module_id: directive.module_id, | ||
984 | file_id, | ||
985 | item_tree: &item_tree, | ||
986 | mod_dir, | ||
987 | } | ||
988 | .collect(&[*mod_item]); | ||
989 | |||
990 | // Remove the original directive since we resolved it. | ||
991 | return false; | ||
992 | } | ||
993 | } | ||
994 | } | ||
995 | |||
996 | // Not resolved to a derive helper, so try to resolve as a macro. | ||
997 | // FIXME: not yet :) | ||
927 | } | 998 | } |
928 | } | 999 | } |
929 | 1000 | ||
930 | true | 1001 | true |
931 | }); | 1002 | }); |
932 | self.unexpanded_macros = macros; | 1003 | // Attribute resolution can add unresolved macro invocations, so concatenate the lists. |
1004 | self.unresolved_macros.extend(macros); | ||
933 | 1005 | ||
934 | for (module_id, macro_call_id, depth) in resolved { | 1006 | for (module_id, macro_call_id, depth) in resolved { |
935 | self.collect_macro_expansion(module_id, macro_call_id, depth); | 1007 | self.collect_macro_expansion(module_id, macro_call_id, depth); |
@@ -1000,7 +1072,7 @@ impl DefCollector<'_> { | |||
1000 | fn finish(mut self) -> DefMap { | 1072 | fn finish(mut self) -> DefMap { |
1001 | // Emit diagnostics for all remaining unexpanded macros. | 1073 | // Emit diagnostics for all remaining unexpanded macros. |
1002 | 1074 | ||
1003 | for directive in &self.unexpanded_macros { | 1075 | for directive in &self.unresolved_macros { |
1004 | match &directive.kind { | 1076 | match &directive.kind { |
1005 | MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id( | 1077 | MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id( |
1006 | ast_id, | 1078 | ast_id, |
@@ -1102,7 +1174,7 @@ impl ModCollector<'_, '_> { | |||
1102 | 1174 | ||
1103 | // Prelude module is always considered to be `#[macro_use]`. | 1175 | // Prelude module is always considered to be `#[macro_use]`. |
1104 | if let Some(prelude_module) = self.def_collector.def_map.prelude { | 1176 | if let Some(prelude_module) = self.def_collector.def_map.prelude { |
1105 | if prelude_module.krate != self.def_collector.def_map.krate { | 1177 | if prelude_module.krate != krate { |
1106 | cov_mark::hit!(prelude_is_macro_use); | 1178 | cov_mark::hit!(prelude_is_macro_use); |
1107 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); | 1179 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); |
1108 | } | 1180 | } |
@@ -1172,6 +1244,7 @@ impl ModCollector<'_, '_> { | |||
1172 | status: PartialResolvedImport::Unresolved, | 1244 | status: PartialResolvedImport::Unresolved, |
1173 | }) | 1245 | }) |
1174 | } | 1246 | } |
1247 | ModItem::ExternBlock(block) => self.collect(&self.item_tree[block].children), | ||
1175 | ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]), | 1248 | ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]), |
1176 | ModItem::MacroRules(id) => self.collect_macro_rules(id), | 1249 | ModItem::MacroRules(id) => self.collect_macro_rules(id), |
1177 | ModItem::MacroDef(id) => self.collect_macro_def(id), | 1250 | ModItem::MacroDef(id) => self.collect_macro_def(id), |
@@ -1203,28 +1276,18 @@ impl ModCollector<'_, '_> { | |||
1203 | ModItem::Struct(id) => { | 1276 | ModItem::Struct(id) => { |
1204 | let it = &self.item_tree[id]; | 1277 | let it = &self.item_tree[id]; |
1205 | 1278 | ||
1206 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
1207 | // in which case we don't add the invocation, just a single attribute | ||
1208 | // macro invocation | ||
1209 | self.collect_derives(&attrs, it.ast_id.upcast()); | ||
1210 | |||
1211 | def = Some(DefData { | 1279 | def = Some(DefData { |
1212 | id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) } | 1280 | id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1213 | .intern(self.def_collector.db) | 1281 | .intern(self.def_collector.db) |
1214 | .into(), | 1282 | .into(), |
1215 | name: &it.name, | 1283 | name: &it.name, |
1216 | visibility: &self.item_tree[it.visibility], | 1284 | visibility: &self.item_tree[it.visibility], |
1217 | has_constructor: it.kind != StructDefKind::Record, | 1285 | has_constructor: !matches!(it.fields, Fields::Record(_)), |
1218 | }); | 1286 | }); |
1219 | } | 1287 | } |
1220 | ModItem::Union(id) => { | 1288 | ModItem::Union(id) => { |
1221 | let it = &self.item_tree[id]; | 1289 | let it = &self.item_tree[id]; |
1222 | 1290 | ||
1223 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
1224 | // in which case we don't add the invocation, just a single attribute | ||
1225 | // macro invocation | ||
1226 | self.collect_derives(&attrs, it.ast_id.upcast()); | ||
1227 | |||
1228 | def = Some(DefData { | 1291 | def = Some(DefData { |
1229 | id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) } | 1292 | id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1230 | .intern(self.def_collector.db) | 1293 | .intern(self.def_collector.db) |
@@ -1237,11 +1300,6 @@ impl ModCollector<'_, '_> { | |||
1237 | ModItem::Enum(id) => { | 1300 | ModItem::Enum(id) => { |
1238 | let it = &self.item_tree[id]; | 1301 | let it = &self.item_tree[id]; |
1239 | 1302 | ||
1240 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
1241 | // in which case we don't add the invocation, just a single attribute | ||
1242 | // macro invocation | ||
1243 | self.collect_derives(&attrs, it.ast_id.upcast()); | ||
1244 | |||
1245 | def = Some(DefData { | 1303 | def = Some(DefData { |
1246 | id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) } | 1304 | id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1247 | .intern(self.def_collector.db) | 1305 | .intern(self.def_collector.db) |
@@ -1453,76 +1511,116 @@ impl ModCollector<'_, '_> { | |||
1453 | /// | 1511 | /// |
1454 | /// Returns `Err` when some attributes could not be resolved to builtins and have been | 1512 | /// Returns `Err` when some attributes could not be resolved to builtins and have been |
1455 | /// registered as unresolved. | 1513 | /// registered as unresolved. |
1514 | /// | ||
1515 | /// If `ignore_up_to` is `Some`, attributes precending and including that attribute will be | ||
1516 | /// assumed to be resolved already. | ||
1456 | fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> { | 1517 | fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> { |
1457 | fn is_builtin_attr(path: &ModPath) -> bool { | 1518 | let mut ignore_up_to = |
1458 | if path.kind == PathKind::Plain { | 1519 | self.def_collector.skip_attrs.get(&InFile::new(self.file_id, mod_item)).copied(); |
1459 | if let Some(tool_module) = path.segments().first() { | 1520 | let iter = attrs |
1460 | let tool_module = tool_module.to_string(); | 1521 | .iter() |
1461 | if builtin_attr::TOOL_MODULES.iter().any(|m| tool_module == *m) { | 1522 | .dedup_by(|a, b| { |
1462 | return true; | 1523 | // FIXME: this should not be required, all attributes on an item should have a |
1463 | } | 1524 | // unique ID! |
1464 | } | 1525 | // Still, this occurs because `#[cfg_attr]` can "expand" to multiple attributes: |
1465 | 1526 | // #[cfg_attr(not(off), unresolved, unresolved)] | |
1466 | if let Some(name) = path.as_ident() { | 1527 | // struct S; |
1467 | let name = name.to_string(); | 1528 | // We should come up with a different way to ID attributes. |
1468 | if builtin_attr::INERT_ATTRIBUTES | 1529 | a.id == b.id |
1469 | .iter() | 1530 | }) |
1470 | .chain(builtin_attr::EXTRA_ATTRIBUTES) | 1531 | .skip_while(|attr| match ignore_up_to { |
1471 | .any(|attr| name == *attr) | 1532 | Some(id) if attr.id == id => { |
1472 | { | 1533 | ignore_up_to = None; |
1473 | return true; | 1534 | true |
1474 | } | ||
1475 | } | 1535 | } |
1476 | } | 1536 | Some(_) => true, |
1477 | 1537 | None => false, | |
1478 | false | 1538 | }); |
1479 | } | 1539 | |
1480 | 1540 | for attr in iter { | |
1481 | // We failed to resolve an attribute on this item earlier, and are falling back to treating | 1541 | if attr.path.as_ident() == Some(&hir_expand::name![derive]) { |
1482 | // the item as-is. | 1542 | self.collect_derive(attr, mod_item); |
1483 | if self.def_collector.ignore_attrs_on.contains(&InFile::new(self.file_id, mod_item)) { | 1543 | } else if self.is_builtin_or_registered_attr(&attr.path) { |
1484 | return Ok(()); | 1544 | continue; |
1485 | } | 1545 | } else { |
1486 | 1546 | log::debug!("non-builtin attribute {}", attr.path); | |
1487 | match attrs.iter().find(|attr| !is_builtin_attr(&attr.path)) { | ||
1488 | Some(non_builtin_attr) => { | ||
1489 | log::debug!("non-builtin attribute {}", non_builtin_attr.path); | ||
1490 | 1547 | ||
1491 | let ast_id = AstIdWithPath::new( | 1548 | let ast_id = AstIdWithPath::new( |
1492 | self.file_id, | 1549 | self.file_id, |
1493 | mod_item.ast_id(self.item_tree), | 1550 | mod_item.ast_id(self.item_tree), |
1494 | non_builtin_attr.path.as_ref().clone(), | 1551 | attr.path.as_ref().clone(), |
1495 | ); | 1552 | ); |
1496 | self.def_collector.unexpanded_macros.push(MacroDirective { | 1553 | self.def_collector.unresolved_macros.push(MacroDirective { |
1497 | module_id: self.module_id, | 1554 | module_id: self.module_id, |
1498 | depth: self.macro_depth + 1, | 1555 | depth: self.macro_depth + 1, |
1499 | kind: MacroDirectiveKind::Attr { ast_id, attr: non_builtin_attr.id, mod_item }, | 1556 | kind: MacroDirectiveKind::Attr { ast_id, attr: attr.id, mod_item }, |
1500 | }); | 1557 | }); |
1501 | 1558 | ||
1502 | Err(()) | 1559 | return Err(()); |
1503 | } | 1560 | } |
1504 | None => Ok(()), | ||
1505 | } | 1561 | } |
1562 | |||
1563 | Ok(()) | ||
1506 | } | 1564 | } |
1507 | 1565 | ||
1508 | fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { | 1566 | fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool { |
1509 | for derive in attrs.by_key("derive").attrs() { | 1567 | if path.kind == PathKind::Plain { |
1510 | match derive.parse_derive() { | 1568 | if let Some(tool_module) = path.segments().first() { |
1511 | Some(derive_macros) => { | 1569 | let tool_module = tool_module.to_string(); |
1512 | for path in derive_macros { | 1570 | if builtin_attr::TOOL_MODULES |
1513 | let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); | 1571 | .iter() |
1514 | self.def_collector.unexpanded_macros.push(MacroDirective { | 1572 | .copied() |
1515 | module_id: self.module_id, | 1573 | .chain(self.def_collector.registered_tools.iter().map(|s| &**s)) |
1516 | depth: self.macro_depth + 1, | 1574 | .any(|m| tool_module == *m) |
1517 | kind: MacroDirectiveKind::Derive { ast_id, derive_attr: derive.id }, | 1575 | { |
1518 | }); | 1576 | return true; |
1519 | } | ||
1520 | } | 1577 | } |
1521 | None => { | 1578 | } |
1522 | // FIXME: diagnose | 1579 | |
1523 | log::debug!("malformed derive: {:?}", derive); | 1580 | if let Some(name) = path.as_ident() { |
1581 | let name = name.to_string(); | ||
1582 | if builtin_attr::INERT_ATTRIBUTES | ||
1583 | .iter() | ||
1584 | .chain(builtin_attr::EXTRA_ATTRIBUTES) | ||
1585 | .copied() | ||
1586 | .chain(self.def_collector.registered_attrs.iter().map(|s| &**s)) | ||
1587 | .any(|attr| name == *attr) | ||
1588 | { | ||
1589 | return true; | ||
1590 | } | ||
1591 | } | ||
1592 | } | ||
1593 | |||
1594 | false | ||
1595 | } | ||
1596 | |||
1597 | fn collect_derive(&mut self, attr: &Attr, mod_item: ModItem) { | ||
1598 | let ast_id: FileAstId<ast::Item> = match mod_item { | ||
1599 | ModItem::Struct(it) => self.item_tree[it].ast_id.upcast(), | ||
1600 | ModItem::Union(it) => self.item_tree[it].ast_id.upcast(), | ||
1601 | ModItem::Enum(it) => self.item_tree[it].ast_id.upcast(), | ||
1602 | _ => { | ||
1603 | // Cannot use derive on this item. | ||
1604 | // FIXME: diagnose | ||
1605 | return; | ||
1606 | } | ||
1607 | }; | ||
1608 | |||
1609 | match attr.parse_derive() { | ||
1610 | Some(derive_macros) => { | ||
1611 | for path in derive_macros { | ||
1612 | let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); | ||
1613 | self.def_collector.unresolved_macros.push(MacroDirective { | ||
1614 | module_id: self.module_id, | ||
1615 | depth: self.macro_depth + 1, | ||
1616 | kind: MacroDirectiveKind::Derive { ast_id, derive_attr: attr.id }, | ||
1617 | }); | ||
1524 | } | 1618 | } |
1525 | } | 1619 | } |
1620 | None => { | ||
1621 | // FIXME: diagnose | ||
1622 | log::debug!("malformed derive: {:?}", attr); | ||
1623 | } | ||
1526 | } | 1624 | } |
1527 | } | 1625 | } |
1528 | 1626 | ||
@@ -1686,7 +1784,7 @@ impl ModCollector<'_, '_> { | |||
1686 | ast_id.path.kind = PathKind::Super(0); | 1784 | ast_id.path.kind = PathKind::Super(0); |
1687 | } | 1785 | } |
1688 | 1786 | ||
1689 | self.def_collector.unexpanded_macros.push(MacroDirective { | 1787 | self.def_collector.unresolved_macros.push(MacroDirective { |
1690 | module_id: self.module_id, | 1788 | module_id: self.module_id, |
1691 | depth: self.macro_depth + 1, | 1789 | depth: self.macro_depth + 1, |
1692 | kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment }, | 1790 | kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment }, |
@@ -1731,14 +1829,16 @@ mod tests { | |||
1731 | glob_imports: FxHashMap::default(), | 1829 | glob_imports: FxHashMap::default(), |
1732 | unresolved_imports: Vec::new(), | 1830 | unresolved_imports: Vec::new(), |
1733 | resolved_imports: Vec::new(), | 1831 | resolved_imports: Vec::new(), |
1734 | unexpanded_macros: Vec::new(), | 1832 | unresolved_macros: Vec::new(), |
1735 | mod_dirs: FxHashMap::default(), | 1833 | mod_dirs: FxHashMap::default(), |
1736 | cfg_options: &CfgOptions::default(), | 1834 | cfg_options: &CfgOptions::default(), |
1737 | proc_macros: Default::default(), | 1835 | proc_macros: Default::default(), |
1738 | exports_proc_macros: false, | 1836 | exports_proc_macros: false, |
1739 | from_glob_import: Default::default(), | 1837 | from_glob_import: Default::default(), |
1740 | ignore_attrs_on: FxHashSet::default(), | 1838 | skip_attrs: Default::default(), |
1741 | derive_helpers_in_scope: FxHashMap::default(), | 1839 | derive_helpers_in_scope: Default::default(), |
1840 | registered_attrs: Default::default(), | ||
1841 | registered_tools: Default::default(), | ||
1742 | }; | 1842 | }; |
1743 | collector.seed_with_top_level(); | 1843 | collector.seed_with_top_level(); |
1744 | collector.collect(); | 1844 | collector.collect(); |
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index 543975e07..75147d973 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -237,3 +237,20 @@ fn good_out_dir_diagnostic() { | |||
237 | "#, | 237 | "#, |
238 | ); | 238 | ); |
239 | } | 239 | } |
240 | |||
241 | #[test] | ||
242 | fn register_attr_and_tool() { | ||
243 | cov_mark::check!(register_attr); | ||
244 | cov_mark::check!(register_tool); | ||
245 | check_no_diagnostics( | ||
246 | r#" | ||
247 | #![register_tool(tool)] | ||
248 | #![register_attr(attr)] | ||
249 | |||
250 | #[tool::path] | ||
251 | #[attr] | ||
252 | struct S; | ||
253 | "#, | ||
254 | ); | ||
255 | // NB: we don't currently emit diagnostics here | ||
256 | } | ||
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index 6eb5f97be..3065efd65 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs | |||
@@ -736,6 +736,80 @@ fn unresolved_attributes_fall_back_track_per_file_moditems() { | |||
736 | } | 736 | } |
737 | 737 | ||
738 | #[test] | 738 | #[test] |
739 | fn unresolved_attrs_extern_block_hang() { | ||
740 | // Regression test for https://github.com/rust-analyzer/rust-analyzer/issues/8905 | ||
741 | check( | ||
742 | r#" | ||
743 | #[unresolved] | ||
744 | extern "C" { | ||
745 | #[unresolved] | ||
746 | fn f(); | ||
747 | } | ||
748 | "#, | ||
749 | expect![[r#" | ||
750 | crate | ||
751 | f: v | ||
752 | "#]], | ||
753 | ); | ||
754 | } | ||
755 | |||
756 | #[test] | ||
757 | fn macros_in_extern_block() { | ||
758 | check( | ||
759 | r#" | ||
760 | macro_rules! m { | ||
761 | () => { static S: u8; }; | ||
762 | } | ||
763 | |||
764 | extern { | ||
765 | m!(); | ||
766 | } | ||
767 | "#, | ||
768 | expect![[r#" | ||
769 | crate | ||
770 | S: v | ||
771 | "#]], | ||
772 | ); | ||
773 | } | ||
774 | |||
775 | #[test] | ||
776 | fn resolves_derive_helper() { | ||
777 | cov_mark::check!(resolved_derive_helper); | ||
778 | check( | ||
779 | r#" | ||
780 | //- /main.rs crate:main deps:proc | ||
781 | #[derive(proc::Derive)] | ||
782 | #[helper] | ||
783 | #[unresolved] | ||
784 | struct S; | ||
785 | |||
786 | //- /proc.rs crate:proc | ||
787 | #[proc_macro_derive(Derive, attributes(helper))] | ||
788 | fn derive() {} | ||
789 | "#, | ||
790 | expect![[r#" | ||
791 | crate | ||
792 | S: t v | ||
793 | "#]], | ||
794 | ); | ||
795 | } | ||
796 | |||
797 | #[test] | ||
798 | fn unresolved_attr_with_cfg_attr_hang() { | ||
799 | // Another regression test for https://github.com/rust-analyzer/rust-analyzer/issues/8905 | ||
800 | check( | ||
801 | r#" | ||
802 | #[cfg_attr(not(off), unresolved, unresolved)] | ||
803 | struct S; | ||
804 | "#, | ||
805 | expect![[r#" | ||
806 | crate | ||
807 | S: t v | ||
808 | "#]], | ||
809 | ); | ||
810 | } | ||
811 | |||
812 | #[test] | ||
739 | fn macro_expansion_overflow() { | 813 | fn macro_expansion_overflow() { |
740 | cov_mark::check!(macro_expansion_overflow); | 814 | cov_mark::check!(macro_expansion_overflow); |
741 | check( | 815 | check( |
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index a43441b1c..9b8873fd2 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -46,6 +46,15 @@ pub enum ImportAlias { | |||
46 | Alias(Name), | 46 | Alias(Name), |
47 | } | 47 | } |
48 | 48 | ||
49 | impl Display for ImportAlias { | ||
50 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
51 | match self { | ||
52 | ImportAlias::Underscore => f.write_str("_"), | ||
53 | ImportAlias::Alias(name) => f.write_str(&name.to_string()), | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
49 | impl ModPath { | 58 | impl ModPath { |
50 | pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { | 59 | pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { |
51 | let ctx = LowerCtx::with_hygiene(db, hygiene); | 60 | let ctx = LowerCtx::with_hygiene(db, hygiene); |
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index 5a5dc9afd..ef67ea2e9 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -164,6 +164,8 @@ pub mod known { | |||
164 | doc, | 164 | doc, |
165 | cfg, | 165 | cfg, |
166 | cfg_attr, | 166 | cfg_attr, |
167 | register_attr, | ||
168 | register_tool, | ||
167 | // Components of known path (value or mod name) | 169 | // Components of known path (value or mod name) |
168 | std, | 170 | std, |
169 | core, | 171 | core, |
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs index e25ef866d..893e727c2 100644 --- a/crates/hir_ty/src/builder.rs +++ b/crates/hir_ty/src/builder.rs | |||
@@ -6,15 +6,15 @@ use chalk_ir::{ | |||
6 | cast::{Cast, CastTo, Caster}, | 6 | cast::{Cast, CastTo, Caster}, |
7 | fold::Fold, | 7 | fold::Fold, |
8 | interner::HasInterner, | 8 | interner::HasInterner, |
9 | AdtId, BoundVar, DebruijnIndex, Safety, Scalar, | 9 | AdtId, BoundVar, DebruijnIndex, Scalar, |
10 | }; | 10 | }; |
11 | use hir_def::{builtin_type::BuiltinType, GenericDefId, TraitId, TypeAliasId}; | 11 | use hir_def::{builtin_type::BuiltinType, GenericDefId, TraitId, TypeAliasId}; |
12 | use smallvec::SmallVec; | 12 | use smallvec::SmallVec; |
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | db::HirDatabase, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, | 15 | db::HirDatabase, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, |
16 | CallableSig, FnPointer, FnSig, FnSubst, GenericArg, Interner, ProjectionTy, Substitution, | 16 | CallableSig, GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, |
17 | TraitRef, Ty, TyDefId, TyExt, TyKind, ValueTyDefId, | 17 | TyKind, ValueTyDefId, |
18 | }; | 18 | }; |
19 | 19 | ||
20 | /// This is a builder for `Ty` or anything that needs a `Substitution`. | 20 | /// This is a builder for `Ty` or anything that needs a `Substitution`. |
@@ -77,15 +77,7 @@ impl TyBuilder<()> { | |||
77 | } | 77 | } |
78 | 78 | ||
79 | pub fn fn_ptr(sig: CallableSig) -> Ty { | 79 | pub fn fn_ptr(sig: CallableSig) -> Ty { |
80 | TyKind::Function(FnPointer { | 80 | TyKind::Function(sig.to_fn_ptr()).intern(&Interner) |
81 | num_binders: 0, | ||
82 | sig: FnSig { abi: (), safety: Safety::Safe, variadic: sig.is_varargs }, | ||
83 | substitution: FnSubst(Substitution::from_iter( | ||
84 | &Interner, | ||
85 | sig.params_and_return.iter().cloned(), | ||
86 | )), | ||
87 | }) | ||
88 | .intern(&Interner) | ||
89 | } | 81 | } |
90 | 82 | ||
91 | pub fn builtin(builtin: BuiltinType) -> Ty { | 83 | pub fn builtin(builtin: BuiltinType) -> Ty { |
diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs index 8f054d06b..b108fd559 100644 --- a/crates/hir_ty/src/chalk_db.rs +++ b/crates/hir_ty/src/chalk_db.rs | |||
@@ -344,20 +344,20 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
344 | } | 344 | } |
345 | 345 | ||
346 | fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> { | 346 | fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> { |
347 | self | 347 | &self.db |
348 | } | 348 | } |
349 | } | 349 | } |
350 | 350 | ||
351 | impl<'a> chalk_ir::UnificationDatabase<Interner> for ChalkContext<'a> { | 351 | impl<'a> chalk_ir::UnificationDatabase<Interner> for &'a dyn HirDatabase { |
352 | fn fn_def_variance( | 352 | fn fn_def_variance( |
353 | &self, | 353 | &self, |
354 | fn_def_id: chalk_ir::FnDefId<Interner>, | 354 | fn_def_id: chalk_ir::FnDefId<Interner>, |
355 | ) -> chalk_ir::Variances<Interner> { | 355 | ) -> chalk_ir::Variances<Interner> { |
356 | self.db.fn_def_variance(self.krate, fn_def_id) | 356 | HirDatabase::fn_def_variance(*self, fn_def_id) |
357 | } | 357 | } |
358 | 358 | ||
359 | fn adt_variance(&self, adt_id: chalk_ir::AdtId<Interner>) -> chalk_ir::Variances<Interner> { | 359 | fn adt_variance(&self, adt_id: chalk_ir::AdtId<Interner>) -> chalk_ir::Variances<Interner> { |
360 | self.db.adt_variance(self.krate, adt_id) | 360 | HirDatabase::adt_variance(*self, adt_id) |
361 | } | 361 | } |
362 | } | 362 | } |
363 | 363 | ||
@@ -651,11 +651,7 @@ pub(crate) fn fn_def_datum_query( | |||
651 | Arc::new(datum) | 651 | Arc::new(datum) |
652 | } | 652 | } |
653 | 653 | ||
654 | pub(crate) fn fn_def_variance_query( | 654 | pub(crate) fn fn_def_variance_query(db: &dyn HirDatabase, fn_def_id: FnDefId) -> Variances { |
655 | db: &dyn HirDatabase, | ||
656 | _krate: CrateId, | ||
657 | fn_def_id: FnDefId, | ||
658 | ) -> Variances { | ||
659 | let callable_def: CallableDefId = from_chalk(db, fn_def_id); | 655 | let callable_def: CallableDefId = from_chalk(db, fn_def_id); |
660 | let generic_params = generics(db.upcast(), callable_def.into()); | 656 | let generic_params = generics(db.upcast(), callable_def.into()); |
661 | Variances::from_iter( | 657 | Variances::from_iter( |
@@ -666,7 +662,6 @@ pub(crate) fn fn_def_variance_query( | |||
666 | 662 | ||
667 | pub(crate) fn adt_variance_query( | 663 | pub(crate) fn adt_variance_query( |
668 | db: &dyn HirDatabase, | 664 | db: &dyn HirDatabase, |
669 | _krate: CrateId, | ||
670 | chalk_ir::AdtId(adt_id): AdtId, | 665 | chalk_ir::AdtId(adt_id): AdtId, |
671 | ) -> Variances { | 666 | ) -> Variances { |
672 | let generic_params = generics(db.upcast(), adt_id.into()); | 667 | let generic_params = generics(db.upcast(), adt_id.into()); |
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs index 5232a7d80..df340a6ca 100644 --- a/crates/hir_ty/src/chalk_ext.rs +++ b/crates/hir_ty/src/chalk_ext.rs | |||
@@ -18,6 +18,7 @@ pub trait TyExt { | |||
18 | fn is_unit(&self) -> bool; | 18 | fn is_unit(&self) -> bool; |
19 | fn is_never(&self) -> bool; | 19 | fn is_never(&self) -> bool; |
20 | fn is_unknown(&self) -> bool; | 20 | fn is_unknown(&self) -> bool; |
21 | fn is_ty_var(&self) -> bool; | ||
21 | 22 | ||
22 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; | 23 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; |
23 | fn as_builtin(&self) -> Option<BuiltinType>; | 24 | fn as_builtin(&self) -> Option<BuiltinType>; |
@@ -55,6 +56,10 @@ impl TyExt for Ty { | |||
55 | matches!(self.kind(&Interner), TyKind::Error) | 56 | matches!(self.kind(&Interner), TyKind::Error) |
56 | } | 57 | } |
57 | 58 | ||
59 | fn is_ty_var(&self) -> bool { | ||
60 | matches!(self.kind(&Interner), TyKind::InferenceVar(_, _)) | ||
61 | } | ||
62 | |||
58 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { | 63 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { |
59 | match self.kind(&Interner) { | 64 | match self.kind(&Interner) { |
60 | TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), | 65 | TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), |
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs index 9da0a02e3..be5b9110e 100644 --- a/crates/hir_ty/src/db.rs +++ b/crates/hir_ty/src/db.rs | |||
@@ -117,10 +117,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
117 | fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc<chalk_db::FnDefDatum>; | 117 | fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc<chalk_db::FnDefDatum>; |
118 | 118 | ||
119 | #[salsa::invoke(chalk_db::fn_def_variance_query)] | 119 | #[salsa::invoke(chalk_db::fn_def_variance_query)] |
120 | fn fn_def_variance(&self, krate: CrateId, fn_def_id: FnDefId) -> chalk_db::Variances; | 120 | fn fn_def_variance(&self, fn_def_id: FnDefId) -> chalk_db::Variances; |
121 | 121 | ||
122 | #[salsa::invoke(chalk_db::adt_variance_query)] | 122 | #[salsa::invoke(chalk_db::adt_variance_query)] |
123 | fn adt_variance(&self, krate: CrateId, adt_id: chalk_db::AdtId) -> chalk_db::Variances; | 123 | fn adt_variance(&self, adt_id: chalk_db::AdtId) -> chalk_db::Variances; |
124 | 124 | ||
125 | #[salsa::invoke(chalk_db::associated_ty_value_query)] | 125 | #[salsa::invoke(chalk_db::associated_ty_value_query)] |
126 | fn associated_ty_value( | 126 | fn associated_ty_value( |
@@ -134,14 +134,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
134 | fn trait_solve( | 134 | fn trait_solve( |
135 | &self, | 135 | &self, |
136 | krate: CrateId, | 136 | krate: CrateId, |
137 | goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>, | 137 | goal: crate::Canonical<crate::InEnvironment<crate::Goal>>, |
138 | ) -> Option<crate::Solution>; | 138 | ) -> Option<crate::Solution>; |
139 | 139 | ||
140 | #[salsa::invoke(crate::traits::trait_solve_query)] | 140 | #[salsa::invoke(crate::traits::trait_solve_query)] |
141 | fn trait_solve_query( | 141 | fn trait_solve_query( |
142 | &self, | 142 | &self, |
143 | krate: CrateId, | 143 | krate: CrateId, |
144 | goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>, | 144 | goal: crate::Canonical<crate::InEnvironment<crate::Goal>>, |
145 | ) -> Option<crate::Solution>; | 145 | ) -> Option<crate::Solution>; |
146 | 146 | ||
147 | #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)] | 147 | #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)] |
@@ -168,7 +168,7 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> | |||
168 | fn trait_solve_wait( | 168 | fn trait_solve_wait( |
169 | db: &dyn HirDatabase, | 169 | db: &dyn HirDatabase, |
170 | krate: CrateId, | 170 | krate: CrateId, |
171 | goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>, | 171 | goal: crate::Canonical<crate::InEnvironment<crate::Goal>>, |
172 | ) -> Option<crate::Solution> { | 172 | ) -> Option<crate::Solution> { |
173 | let _p = profile::span("trait_solve::wait"); | 173 | let _p = profile::span("trait_solve::wait"); |
174 | db.trait_solve_query(krate, goal) | 174 | db.trait_solve_query(krate, goal) |
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 0ee851a74..f1cebbdb9 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -13,8 +13,6 @@ | |||
13 | //! to certain types. To record this, we use the union-find implementation from | 13 | //! to certain types. To record this, we use the union-find implementation from |
14 | //! the `ena` crate, which is extracted from rustc. | 14 | //! the `ena` crate, which is extracted from rustc. |
15 | 15 | ||
16 | use std::borrow::Cow; | ||
17 | use std::mem; | ||
18 | use std::ops::Index; | 16 | use std::ops::Index; |
19 | use std::sync::Arc; | 17 | use std::sync::Arc; |
20 | 18 | ||
@@ -27,8 +25,8 @@ use hir_def::{ | |||
27 | path::{path, Path}, | 25 | path::{path, Path}, |
28 | resolver::{HasResolver, Resolver, TypeNs}, | 26 | resolver::{HasResolver, Resolver, TypeNs}, |
29 | type_ref::TypeRef, | 27 | type_ref::TypeRef, |
30 | AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, Lookup, TraitId, | 28 | AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup, |
31 | TypeAliasId, VariantId, | 29 | TraitId, TypeAliasId, VariantId, |
32 | }; | 30 | }; |
33 | use hir_expand::{diagnostics::DiagnosticSink, name::name}; | 31 | use hir_expand::{diagnostics::DiagnosticSink, name::name}; |
34 | use la_arena::ArenaMap; | 32 | use la_arena::ArenaMap; |
@@ -36,13 +34,11 @@ use rustc_hash::FxHashMap; | |||
36 | use stdx::impl_from; | 34 | use stdx::impl_from; |
37 | use syntax::SmolStr; | 35 | use syntax::SmolStr; |
38 | 36 | ||
39 | use super::{ | 37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; |
40 | DomainGoal, Guidance, InEnvironment, ProjectionTy, Solution, TraitEnvironment, TraitRef, Ty, | ||
41 | }; | ||
42 | use crate::{ | 38 | use crate::{ |
43 | db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, | 39 | db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, |
44 | lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Canonical, Interner, | 40 | lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, TyBuilder, |
45 | TyBuilder, TyExt, TyKind, | 41 | TyExt, TyKind, |
46 | }; | 42 | }; |
47 | 43 | ||
48 | // This lint has a false positive here. See the link below for details. | 44 | // This lint has a false positive here. See the link below for details. |
@@ -106,6 +102,14 @@ impl Default for BindingMode { | |||
106 | } | 102 | } |
107 | } | 103 | } |
108 | 104 | ||
105 | #[derive(Debug)] | ||
106 | pub(crate) struct InferOk { | ||
107 | goals: Vec<InEnvironment<Goal>>, | ||
108 | } | ||
109 | #[derive(Debug)] | ||
110 | pub(crate) struct TypeError; | ||
111 | pub(crate) type InferResult = Result<InferOk, TypeError>; | ||
112 | |||
109 | /// A mismatch between an expected and an inferred type. | 113 | /// A mismatch between an expected and an inferred type. |
110 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 114 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
111 | pub struct TypeMismatch { | 115 | pub struct TypeMismatch { |
@@ -217,10 +221,8 @@ struct InferenceContext<'a> { | |||
217 | owner: DefWithBodyId, | 221 | owner: DefWithBodyId, |
218 | body: Arc<Body>, | 222 | body: Arc<Body>, |
219 | resolver: Resolver, | 223 | resolver: Resolver, |
220 | table: unify::InferenceTable, | 224 | table: unify::InferenceTable<'a>, |
221 | trait_env: Arc<TraitEnvironment>, | 225 | trait_env: Arc<TraitEnvironment>, |
222 | obligations: Vec<DomainGoal>, | ||
223 | last_obligations_check: Option<u32>, | ||
224 | result: InferenceResult, | 226 | result: InferenceResult, |
225 | /// The return type of the function being inferred, or the closure if we're | 227 | /// The return type of the function being inferred, or the closure if we're |
226 | /// currently within one. | 228 | /// currently within one. |
@@ -252,15 +254,15 @@ fn find_breakable<'c>( | |||
252 | 254 | ||
253 | impl<'a> InferenceContext<'a> { | 255 | impl<'a> InferenceContext<'a> { |
254 | fn new(db: &'a dyn HirDatabase, owner: DefWithBodyId, resolver: Resolver) -> Self { | 256 | fn new(db: &'a dyn HirDatabase, owner: DefWithBodyId, resolver: Resolver) -> Self { |
257 | let krate = owner.module(db.upcast()).krate(); | ||
258 | let trait_env = owner | ||
259 | .as_generic_def_id() | ||
260 | .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d)); | ||
255 | InferenceContext { | 261 | InferenceContext { |
256 | result: InferenceResult::default(), | 262 | result: InferenceResult::default(), |
257 | table: unify::InferenceTable::new(), | 263 | table: unify::InferenceTable::new(db, trait_env.clone()), |
258 | obligations: Vec::default(), | 264 | trait_env, |
259 | last_obligations_check: None, | ||
260 | return_ty: TyKind::Error.intern(&Interner), // set in collect_fn_signature | 265 | return_ty: TyKind::Error.intern(&Interner), // set in collect_fn_signature |
261 | trait_env: owner | ||
262 | .as_generic_def_id() | ||
263 | .map_or_else(Default::default, |d| db.trait_environment(d)), | ||
264 | db, | 266 | db, |
265 | owner, | 267 | owner, |
266 | body: db.body(owner), | 268 | body: db.body(owner), |
@@ -271,19 +273,25 @@ impl<'a> InferenceContext<'a> { | |||
271 | } | 273 | } |
272 | 274 | ||
273 | fn err_ty(&self) -> Ty { | 275 | fn err_ty(&self) -> Ty { |
274 | TyKind::Error.intern(&Interner) | 276 | self.result.standard_types.unknown.clone() |
275 | } | 277 | } |
276 | 278 | ||
277 | fn resolve_all(mut self) -> InferenceResult { | 279 | fn resolve_all(mut self) -> InferenceResult { |
278 | // FIXME resolve obligations as well (use Guidance if necessary) | 280 | // FIXME resolve obligations as well (use Guidance if necessary) |
281 | self.table.resolve_obligations_as_possible(); | ||
282 | |||
283 | // make sure diverging type variables are marked as such | ||
284 | self.table.propagate_diverging_flag(); | ||
279 | let mut result = std::mem::take(&mut self.result); | 285 | let mut result = std::mem::take(&mut self.result); |
280 | for ty in result.type_of_expr.values_mut() { | 286 | for ty in result.type_of_expr.values_mut() { |
281 | let resolved = self.table.resolve_ty_completely(ty.clone()); | 287 | *ty = self.table.resolve_ty_completely(ty.clone()); |
282 | *ty = resolved; | ||
283 | } | 288 | } |
284 | for ty in result.type_of_pat.values_mut() { | 289 | for ty in result.type_of_pat.values_mut() { |
285 | let resolved = self.table.resolve_ty_completely(ty.clone()); | 290 | *ty = self.table.resolve_ty_completely(ty.clone()); |
286 | *ty = resolved; | 291 | } |
292 | for mismatch in result.type_mismatches.values_mut() { | ||
293 | mismatch.expected = self.table.resolve_ty_completely(mismatch.expected.clone()); | ||
294 | mismatch.actual = self.table.resolve_ty_completely(mismatch.actual.clone()); | ||
287 | } | 295 | } |
288 | result | 296 | result |
289 | } | 297 | } |
@@ -337,6 +345,14 @@ impl<'a> InferenceContext<'a> { | |||
337 | fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { | 345 | fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { |
338 | match ty.kind(&Interner) { | 346 | match ty.kind(&Interner) { |
339 | TyKind::Error => self.table.new_type_var(), | 347 | TyKind::Error => self.table.new_type_var(), |
348 | TyKind::InferenceVar(..) => { | ||
349 | let ty_resolved = self.resolve_ty_shallow(&ty); | ||
350 | if ty_resolved.is_unknown() { | ||
351 | self.table.new_type_var() | ||
352 | } else { | ||
353 | ty | ||
354 | } | ||
355 | } | ||
340 | _ => ty, | 356 | _ => ty, |
341 | } | 357 | } |
342 | } | 358 | } |
@@ -346,66 +362,19 @@ impl<'a> InferenceContext<'a> { | |||
346 | } | 362 | } |
347 | 363 | ||
348 | fn resolve_obligations_as_possible(&mut self) { | 364 | fn resolve_obligations_as_possible(&mut self) { |
349 | if self.last_obligations_check == Some(self.table.revision) { | 365 | self.table.resolve_obligations_as_possible(); |
350 | // no change | ||
351 | return; | ||
352 | } | ||
353 | let _span = profile::span("resolve_obligations_as_possible"); | ||
354 | |||
355 | self.last_obligations_check = Some(self.table.revision); | ||
356 | let obligations = mem::replace(&mut self.obligations, Vec::new()); | ||
357 | for obligation in obligations { | ||
358 | let in_env = InEnvironment::new(&self.trait_env.env, obligation.clone()); | ||
359 | let canonicalized = self.canonicalizer().canonicalize_obligation(in_env); | ||
360 | let solution = | ||
361 | self.db.trait_solve(self.resolver.krate().unwrap(), canonicalized.value.clone()); | ||
362 | |||
363 | match solution { | ||
364 | Some(Solution::Unique(canonical_subst)) => { | ||
365 | canonicalized.apply_solution( | ||
366 | self, | ||
367 | Canonical { | ||
368 | binders: canonical_subst.binders, | ||
369 | // FIXME: handle constraints | ||
370 | value: canonical_subst.value.subst, | ||
371 | }, | ||
372 | ); | ||
373 | } | ||
374 | Some(Solution::Ambig(Guidance::Definite(substs))) => { | ||
375 | canonicalized.apply_solution(self, substs); | ||
376 | self.obligations.push(obligation); | ||
377 | } | ||
378 | Some(_) => { | ||
379 | // FIXME use this when trying to resolve everything at the end | ||
380 | self.obligations.push(obligation); | ||
381 | } | ||
382 | None => { | ||
383 | // FIXME obligation cannot be fulfilled => diagnostic | ||
384 | } | ||
385 | }; | ||
386 | } | ||
387 | } | 366 | } |
388 | 367 | ||
389 | fn push_obligation(&mut self, o: DomainGoal) { | 368 | fn push_obligation(&mut self, o: DomainGoal) { |
390 | self.obligations.push(o); | 369 | self.table.register_obligation(o.cast(&Interner)); |
391 | self.last_obligations_check = None; | ||
392 | } | 370 | } |
393 | 371 | ||
394 | fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { | 372 | fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { |
395 | self.table.unify(ty1, ty2) | 373 | self.table.unify(ty1, ty2) |
396 | } | 374 | } |
397 | 375 | ||
398 | /// Resolves the type as far as currently possible, replacing type variables | 376 | fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { |
399 | /// by their known types. All types returned by the infer_* functions should | ||
400 | /// be resolved as far as possible, i.e. contain no type variables with | ||
401 | /// known type. | ||
402 | fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty { | ||
403 | self.resolve_obligations_as_possible(); | 377 | self.resolve_obligations_as_possible(); |
404 | |||
405 | self.table.resolve_ty_as_possible(ty) | ||
406 | } | ||
407 | |||
408 | fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { | ||
409 | self.table.resolve_ty_shallow(ty) | 378 | self.table.resolve_ty_shallow(ty) |
410 | } | 379 | } |
411 | 380 | ||
@@ -439,7 +408,7 @@ impl<'a> InferenceContext<'a> { | |||
439 | }; | 408 | }; |
440 | self.push_obligation(trait_ref.cast(&Interner)); | 409 | self.push_obligation(trait_ref.cast(&Interner)); |
441 | self.push_obligation(alias_eq.cast(&Interner)); | 410 | self.push_obligation(alias_eq.cast(&Interner)); |
442 | self.resolve_ty_as_possible(ty) | 411 | ty |
443 | } | 412 | } |
444 | None => self.err_ty(), | 413 | None => self.err_ty(), |
445 | } | 414 | } |
@@ -452,25 +421,7 @@ impl<'a> InferenceContext<'a> { | |||
452 | /// call). `make_ty` handles this already, but e.g. for field types we need | 421 | /// call). `make_ty` handles this already, but e.g. for field types we need |
453 | /// to do it as well. | 422 | /// to do it as well. |
454 | fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { | 423 | fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { |
455 | let ty = self.resolve_ty_as_possible(ty); | 424 | self.table.normalize_associated_types_in(ty) |
456 | fold_tys( | ||
457 | ty, | ||
458 | |ty, _| match ty.kind(&Interner) { | ||
459 | TyKind::Alias(AliasTy::Projection(proj_ty)) => { | ||
460 | self.normalize_projection_ty(proj_ty.clone()) | ||
461 | } | ||
462 | _ => ty, | ||
463 | }, | ||
464 | DebruijnIndex::INNERMOST, | ||
465 | ) | ||
466 | } | ||
467 | |||
468 | fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { | ||
469 | let var = self.table.new_type_var(); | ||
470 | let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; | ||
471 | let obligation = alias_eq.cast(&Interner); | ||
472 | self.push_obligation(obligation); | ||
473 | var | ||
474 | } | 425 | } |
475 | 426 | ||
476 | fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<VariantId>) { | 427 | fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<VariantId>) { |
@@ -720,17 +671,23 @@ impl<'a> InferenceContext<'a> { | |||
720 | /// When inferring an expression, we propagate downward whatever type hint we | 671 | /// When inferring an expression, we propagate downward whatever type hint we |
721 | /// are able in the form of an `Expectation`. | 672 | /// are able in the form of an `Expectation`. |
722 | #[derive(Clone, PartialEq, Eq, Debug)] | 673 | #[derive(Clone, PartialEq, Eq, Debug)] |
723 | struct Expectation { | 674 | enum Expectation { |
724 | ty: Ty, | 675 | None, |
725 | /// See the `rvalue_hint` method. | 676 | HasType(Ty), |
726 | rvalue_hint: bool, | 677 | // Castable(Ty), // rustc has this, we currently just don't propagate an expectation for casts |
678 | RValueLikeUnsized(Ty), | ||
727 | } | 679 | } |
728 | 680 | ||
729 | impl Expectation { | 681 | impl Expectation { |
730 | /// The expectation that the type of the expression needs to equal the given | 682 | /// The expectation that the type of the expression needs to equal the given |
731 | /// type. | 683 | /// type. |
732 | fn has_type(ty: Ty) -> Self { | 684 | fn has_type(ty: Ty) -> Self { |
733 | Expectation { ty, rvalue_hint: false } | 685 | if ty.is_unknown() { |
686 | // FIXME: get rid of this? | ||
687 | Expectation::None | ||
688 | } else { | ||
689 | Expectation::HasType(ty) | ||
690 | } | ||
734 | } | 691 | } |
735 | 692 | ||
736 | /// The following explanation is copied straight from rustc: | 693 | /// The following explanation is copied straight from rustc: |
@@ -754,24 +711,41 @@ impl Expectation { | |||
754 | /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 | 711 | /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 |
755 | /// for examples of where this comes up,. | 712 | /// for examples of where this comes up,. |
756 | fn rvalue_hint(ty: Ty) -> Self { | 713 | fn rvalue_hint(ty: Ty) -> Self { |
757 | Expectation { ty, rvalue_hint: true } | 714 | match ty.strip_references().kind(&Interner) { |
715 | TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => Expectation::RValueLikeUnsized(ty), | ||
716 | _ => Expectation::has_type(ty), | ||
717 | } | ||
758 | } | 718 | } |
759 | 719 | ||
760 | /// This expresses no expectation on the type. | 720 | /// This expresses no expectation on the type. |
761 | fn none() -> Self { | 721 | fn none() -> Self { |
762 | Expectation { | 722 | Expectation::None |
763 | // FIXME | 723 | } |
764 | ty: TyKind::Error.intern(&Interner), | 724 | |
765 | rvalue_hint: false, | 725 | fn resolve(&self, table: &mut unify::InferenceTable) -> Expectation { |
726 | match self { | ||
727 | Expectation::None => Expectation::None, | ||
728 | Expectation::HasType(t) => Expectation::HasType(table.resolve_ty_shallow(t)), | ||
729 | Expectation::RValueLikeUnsized(t) => { | ||
730 | Expectation::RValueLikeUnsized(table.resolve_ty_shallow(t)) | ||
731 | } | ||
766 | } | 732 | } |
767 | } | 733 | } |
768 | 734 | ||
769 | fn coercion_target(&self) -> Ty { | 735 | fn to_option(&self, table: &mut unify::InferenceTable) -> Option<Ty> { |
770 | if self.rvalue_hint { | 736 | match self.resolve(table) { |
771 | // FIXME | 737 | Expectation::None => None, |
772 | TyKind::Error.intern(&Interner) | 738 | Expectation::HasType(t) | |
773 | } else { | 739 | // Expectation::Castable(t) | |
774 | self.ty.clone() | 740 | Expectation::RValueLikeUnsized(t) => Some(t), |
741 | } | ||
742 | } | ||
743 | |||
744 | fn only_has_type(&self, table: &mut unify::InferenceTable) -> Option<Ty> { | ||
745 | match self { | ||
746 | Expectation::HasType(t) => Some(table.resolve_ty_shallow(t)), | ||
747 | // Expectation::Castable(_) | | ||
748 | Expectation::RValueLikeUnsized(_) | Expectation::None => None, | ||
775 | } | 749 | } |
776 | } | 750 | } |
777 | } | 751 | } |
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 1f463a425..765a02b1c 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs | |||
@@ -2,156 +2,414 @@ | |||
2 | //! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions | 2 | //! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions |
3 | //! like going from `&Vec<T>` to `&[T]`. | 3 | //! like going from `&Vec<T>` to `&[T]`. |
4 | //! | 4 | //! |
5 | //! See: https://doc.rust-lang.org/nomicon/coercions.html | 5 | //! See https://doc.rust-lang.org/nomicon/coercions.html and |
6 | //! librustc_typeck/check/coercion.rs. | ||
6 | 7 | ||
7 | use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; | 8 | use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; |
8 | use hir_def::lang_item::LangItemTarget; | 9 | use hir_def::{expr::ExprId, lang_item::LangItemTarget}; |
9 | 10 | ||
10 | use crate::{autoderef, Canonical, Interner, Solution, Ty, TyBuilder, TyExt, TyKind}; | 11 | use crate::{ |
12 | autoderef, infer::TypeMismatch, static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, | ||
13 | Interner, Solution, Substitution, Ty, TyBuilder, TyExt, TyKind, | ||
14 | }; | ||
11 | 15 | ||
12 | use super::{InEnvironment, InferenceContext}; | 16 | use super::{InEnvironment, InferOk, InferResult, InferenceContext, TypeError}; |
13 | 17 | ||
14 | impl<'a> InferenceContext<'a> { | 18 | impl<'a> InferenceContext<'a> { |
15 | /// Unify two types, but may coerce the first one to the second one | 19 | /// Unify two types, but may coerce the first one to the second one |
16 | /// using "implicit coercion rules" if needed. | 20 | /// using "implicit coercion rules" if needed. |
17 | pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { | 21 | pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { |
18 | let from_ty = self.resolve_ty_shallow(from_ty).into_owned(); | 22 | let from_ty = self.resolve_ty_shallow(from_ty); |
19 | let to_ty = self.resolve_ty_shallow(to_ty); | 23 | let to_ty = self.resolve_ty_shallow(to_ty); |
20 | self.coerce_inner(from_ty, &to_ty) | 24 | match self.coerce_inner(from_ty, &to_ty) { |
25 | Ok(result) => { | ||
26 | self.table.register_infer_ok(result); | ||
27 | true | ||
28 | } | ||
29 | Err(_) => { | ||
30 | // FIXME deal with error | ||
31 | false | ||
32 | } | ||
33 | } | ||
21 | } | 34 | } |
22 | 35 | ||
23 | /// Merge two types from different branches, with possible coercion. | 36 | /// Merge two types from different branches, with possible coercion. |
24 | /// | 37 | /// |
25 | /// Mostly this means trying to coerce one to the other, but | 38 | /// Mostly this means trying to coerce one to the other, but |
26 | /// - if we have two function types for different functions, we need to | 39 | /// - if we have two function types for different functions or closures, we need to |
27 | /// coerce both to function pointers; | 40 | /// coerce both to function pointers; |
28 | /// - if we were concerned with lifetime subtyping, we'd need to look for a | 41 | /// - if we were concerned with lifetime subtyping, we'd need to look for a |
29 | /// least upper bound. | 42 | /// least upper bound. |
30 | pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { | 43 | pub(super) fn coerce_merge_branch(&mut self, id: Option<ExprId>, ty1: &Ty, ty2: &Ty) -> Ty { |
31 | if self.coerce(ty1, ty2) { | 44 | let ty1 = self.resolve_ty_shallow(ty1); |
32 | ty2.clone() | 45 | let ty2 = self.resolve_ty_shallow(ty2); |
33 | } else if self.coerce(ty2, ty1) { | 46 | // Special case: two function types. Try to coerce both to |
47 | // pointers to have a chance at getting a match. See | ||
48 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 | ||
49 | let sig = match (ty1.kind(&Interner), ty2.kind(&Interner)) { | ||
50 | (TyKind::FnDef(..), TyKind::FnDef(..)) | ||
51 | | (TyKind::Closure(..), TyKind::FnDef(..)) | ||
52 | | (TyKind::FnDef(..), TyKind::Closure(..)) | ||
53 | | (TyKind::Closure(..), TyKind::Closure(..)) => { | ||
54 | // FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure, | ||
55 | // we should be coercing the closure to a fn pointer of the safety of the FnDef | ||
56 | cov_mark::hit!(coerce_fn_reification); | ||
57 | let sig = ty1.callable_sig(self.db).expect("FnDef without callable sig"); | ||
58 | Some(sig) | ||
59 | } | ||
60 | _ => None, | ||
61 | }; | ||
62 | if let Some(sig) = sig { | ||
63 | let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(&Interner); | ||
64 | let result1 = self.coerce_inner(ty1.clone(), &target_ty); | ||
65 | let result2 = self.coerce_inner(ty2.clone(), &target_ty); | ||
66 | if let (Ok(result1), Ok(result2)) = (result1, result2) { | ||
67 | self.table.register_infer_ok(result1); | ||
68 | self.table.register_infer_ok(result2); | ||
69 | return target_ty; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | // It might not seem like it, but order is important here: ty1 is our | ||
74 | // "previous" type, ty2 is the "new" one being added. If the previous | ||
75 | // type is a type variable and the new one is `!`, trying it the other | ||
76 | // way around first would mean we make the type variable `!`, instead of | ||
77 | // just marking it as possibly diverging. | ||
78 | if self.coerce(&ty2, &ty1) { | ||
34 | ty1.clone() | 79 | ty1.clone() |
80 | } else if self.coerce(&ty1, &ty2) { | ||
81 | ty2.clone() | ||
35 | } else { | 82 | } else { |
36 | if let (TyKind::FnDef(..), TyKind::FnDef(..)) = | 83 | if let Some(id) = id { |
37 | (ty1.kind(&Interner), ty2.kind(&Interner)) | 84 | self.result |
38 | { | 85 | .type_mismatches |
39 | cov_mark::hit!(coerce_fn_reification); | 86 | .insert(id.into(), TypeMismatch { expected: ty1.clone(), actual: ty2.clone() }); |
40 | // Special case: two function types. Try to coerce both to | ||
41 | // pointers to have a chance at getting a match. See | ||
42 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 | ||
43 | let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig"); | ||
44 | let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig"); | ||
45 | let ptr_ty1 = TyBuilder::fn_ptr(sig1); | ||
46 | let ptr_ty2 = TyBuilder::fn_ptr(sig2); | ||
47 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) | ||
48 | } else { | ||
49 | cov_mark::hit!(coerce_merge_fail_fallback); | ||
50 | ty1.clone() | ||
51 | } | 87 | } |
88 | cov_mark::hit!(coerce_merge_fail_fallback); | ||
89 | ty1.clone() | ||
52 | } | 90 | } |
53 | } | 91 | } |
54 | 92 | ||
55 | fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool { | 93 | fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult { |
56 | match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { | 94 | if from_ty.is_never() { |
57 | // Never type will make type variable to fallback to Never Type instead of Unknown. | 95 | // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound |
58 | (TyKind::Never, TyKind::InferenceVar(tv, TyVariableKind::General)) => { | 96 | // type variable, we want `?T` to fallback to `!` if not |
59 | self.table.type_variable_table.set_diverging(*tv, true); | 97 | // otherwise constrained. An example where this arises: |
60 | return true; | 98 | // |
99 | // let _: Option<?T> = Some({ return; }); | ||
100 | // | ||
101 | // here, we would coerce from `!` to `?T`. | ||
102 | match to_ty.kind(&Interner) { | ||
103 | TyKind::InferenceVar(tv, TyVariableKind::General) => { | ||
104 | self.table.set_diverging(*tv, true); | ||
105 | } | ||
106 | _ => {} | ||
61 | } | 107 | } |
62 | (TyKind::Never, _) => return true, | 108 | return Ok(InferOk { goals: Vec::new() }); |
109 | } | ||
63 | 110 | ||
64 | // Trivial cases, this should go after `never` check to | 111 | // Consider coercing the subtype to a DST |
65 | // avoid infer result type to be never | 112 | if let Ok(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { |
66 | _ => { | 113 | return Ok(ret); |
67 | if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) { | 114 | } |
68 | return true; | 115 | |
69 | } | 116 | // Examine the supertype and consider auto-borrowing. |
117 | match to_ty.kind(&Interner) { | ||
118 | TyKind::Raw(mt, _) => { | ||
119 | return self.coerce_ptr(from_ty, to_ty, *mt); | ||
70 | } | 120 | } |
121 | TyKind::Ref(mt, _, _) => { | ||
122 | return self.coerce_ref(from_ty, to_ty, *mt); | ||
123 | } | ||
124 | _ => {} | ||
71 | } | 125 | } |
72 | 126 | ||
73 | // Pointer weakening and function to pointer | 127 | match from_ty.kind(&Interner) { |
74 | match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { | 128 | TyKind::FnDef(..) => { |
75 | // `*mut T` -> `*const T` | 129 | // Function items are coercible to any closure |
76 | (TyKind::Raw(_, inner), TyKind::Raw(m2 @ Mutability::Not, ..)) => { | 130 | // type; function pointers are not (that would |
77 | from_ty = TyKind::Raw(*m2, inner.clone()).intern(&Interner); | 131 | // require double indirection). |
132 | // Additionally, we permit coercion of function | ||
133 | // items to drop the unsafe qualifier. | ||
134 | self.coerce_from_fn_item(from_ty, to_ty) | ||
78 | } | 135 | } |
79 | // `&mut T` -> `&T` | 136 | TyKind::Function(from_fn_ptr) => { |
80 | (TyKind::Ref(_, lt, inner), TyKind::Ref(m2 @ Mutability::Not, ..)) => { | 137 | // We permit coercion of fn pointers to drop the |
81 | from_ty = TyKind::Ref(*m2, lt.clone(), inner.clone()).intern(&Interner); | 138 | // unsafe qualifier. |
139 | self.coerce_from_fn_pointer(from_ty.clone(), from_fn_ptr, to_ty) | ||
82 | } | 140 | } |
83 | // `&T` -> `*const T` | 141 | TyKind::Closure(_, from_substs) => { |
84 | // `&mut T` -> `*mut T`/`*const T` | 142 | // Non-capturing closures are coercible to |
85 | (TyKind::Ref(.., substs), &TyKind::Raw(m2 @ Mutability::Not, ..)) | 143 | // function pointers or unsafe function pointers. |
86 | | (TyKind::Ref(Mutability::Mut, _, substs), &TyKind::Raw(m2, ..)) => { | 144 | // It cannot convert closures that require unsafe. |
87 | from_ty = TyKind::Raw(m2, substs.clone()).intern(&Interner); | 145 | self.coerce_closure_to_fn(from_ty.clone(), from_substs, to_ty) |
88 | } | 146 | } |
147 | _ => { | ||
148 | // Otherwise, just use unification rules. | ||
149 | self.table.try_unify(&from_ty, to_ty) | ||
150 | } | ||
151 | } | ||
152 | } | ||
89 | 153 | ||
90 | // Illegal mutability conversion | 154 | fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult { |
91 | (TyKind::Raw(Mutability::Not, ..), TyKind::Raw(Mutability::Mut, ..)) | 155 | let (_is_ref, from_mt, from_inner) = match from_ty.kind(&Interner) { |
92 | | (TyKind::Ref(Mutability::Not, ..), TyKind::Ref(Mutability::Mut, ..)) => return false, | 156 | TyKind::Ref(mt, _, ty) => (true, mt, ty), |
157 | TyKind::Raw(mt, ty) => (false, mt, ty), | ||
158 | _ => return self.table.try_unify(&from_ty, to_ty), | ||
159 | }; | ||
93 | 160 | ||
94 | // `{function_type}` -> `fn()` | 161 | coerce_mutabilities(*from_mt, to_mt)?; |
95 | (TyKind::FnDef(..), TyKind::Function { .. }) => match from_ty.callable_sig(self.db) { | 162 | |
96 | None => return false, | 163 | // Check that the types which they point at are compatible. |
97 | Some(sig) => { | 164 | let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(&Interner); |
98 | from_ty = TyBuilder::fn_ptr(sig); | 165 | // FIXME: behavior differs based on is_ref once we're computing adjustments |
99 | } | 166 | self.table.try_unify(&from_raw, to_ty) |
167 | } | ||
168 | |||
169 | /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. | ||
170 | /// To match `A` with `B`, autoderef will be performed, | ||
171 | /// calling `deref`/`deref_mut` where necessary. | ||
172 | fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult { | ||
173 | match from_ty.kind(&Interner) { | ||
174 | TyKind::Ref(mt, _, _) => { | ||
175 | coerce_mutabilities(*mt, to_mt)?; | ||
176 | } | ||
177 | _ => return self.table.try_unify(&from_ty, to_ty), | ||
178 | }; | ||
179 | |||
180 | // NOTE: this code is mostly copied and adapted from rustc, and | ||
181 | // currently more complicated than necessary, carrying errors around | ||
182 | // etc.. This complication will become necessary when we actually track | ||
183 | // details of coercion errors though, so I think it's useful to leave | ||
184 | // the structure like it is. | ||
185 | |||
186 | let canonicalized = self.canonicalize(from_ty.clone()); | ||
187 | let autoderef = autoderef::autoderef( | ||
188 | self.db, | ||
189 | self.resolver.krate(), | ||
190 | InEnvironment { | ||
191 | goal: canonicalized.value.clone(), | ||
192 | environment: self.trait_env.env.clone(), | ||
100 | }, | 193 | }, |
194 | ); | ||
195 | let mut first_error = None; | ||
196 | let mut found = None; | ||
101 | 197 | ||
102 | (TyKind::Closure(.., substs), TyKind::Function { .. }) => { | 198 | for (autoderefs, referent_ty) in autoderef.enumerate() { |
103 | from_ty = substs.at(&Interner, 0).assert_ty_ref(&Interner).clone(); | 199 | if autoderefs == 0 { |
200 | // Don't let this pass, otherwise it would cause | ||
201 | // &T to autoref to &&T. | ||
202 | continue; | ||
104 | } | 203 | } |
105 | 204 | ||
106 | _ => {} | 205 | let referent_ty = canonicalized.decanonicalize_ty(referent_ty.value); |
206 | |||
207 | // At this point, we have deref'd `a` to `referent_ty`. So | ||
208 | // imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`. | ||
209 | // In the autoderef loop for `&'a mut Vec<T>`, we would get | ||
210 | // three callbacks: | ||
211 | // | ||
212 | // - `&'a mut Vec<T>` -- 0 derefs, just ignore it | ||
213 | // - `Vec<T>` -- 1 deref | ||
214 | // - `[T]` -- 2 deref | ||
215 | // | ||
216 | // At each point after the first callback, we want to | ||
217 | // check to see whether this would match out target type | ||
218 | // (`&'b mut [T]`) if we autoref'd it. We can't just | ||
219 | // compare the referent types, though, because we still | ||
220 | // have to consider the mutability. E.g., in the case | ||
221 | // we've been considering, we have an `&mut` reference, so | ||
222 | // the `T` in `[T]` needs to be unified with equality. | ||
223 | // | ||
224 | // Therefore, we construct reference types reflecting what | ||
225 | // the types will be after we do the final auto-ref and | ||
226 | // compare those. Note that this means we use the target | ||
227 | // mutability [1], since it may be that we are coercing | ||
228 | // from `&mut T` to `&U`. | ||
229 | let lt = static_lifetime(); // FIXME: handle lifetimes correctly, see rustc | ||
230 | let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(&Interner); | ||
231 | match self.table.try_unify(&derefd_from_ty, to_ty) { | ||
232 | Ok(result) => { | ||
233 | found = Some(result); | ||
234 | break; | ||
235 | } | ||
236 | Err(err) => { | ||
237 | if first_error.is_none() { | ||
238 | first_error = Some(err); | ||
239 | } | ||
240 | } | ||
241 | } | ||
107 | } | 242 | } |
108 | 243 | ||
109 | if let Some(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { | 244 | // Extract type or return an error. We return the first error |
110 | return ret; | 245 | // we got, which should be from relating the "base" type |
246 | // (e.g., in example above, the failure from relating `Vec<T>` | ||
247 | // to the target type), since that should be the least | ||
248 | // confusing. | ||
249 | let result = match found { | ||
250 | Some(d) => d, | ||
251 | None => { | ||
252 | let err = first_error.expect("coerce_borrowed_pointer had no error"); | ||
253 | return Err(err); | ||
254 | } | ||
255 | }; | ||
256 | |||
257 | Ok(result) | ||
258 | } | ||
259 | |||
260 | /// Attempts to coerce from the type of a Rust function item into a function pointer. | ||
261 | fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult { | ||
262 | match to_ty.kind(&Interner) { | ||
263 | TyKind::Function(_) => { | ||
264 | let from_sig = from_ty.callable_sig(self.db).expect("FnDef had no sig"); | ||
265 | |||
266 | // FIXME check ABI: Intrinsics are not coercible to function pointers | ||
267 | // FIXME Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396) | ||
268 | |||
269 | // FIXME rustc normalizes assoc types in the sig here, not sure if necessary | ||
270 | |||
271 | let from_sig = from_sig.to_fn_ptr(); | ||
272 | let from_fn_pointer = TyKind::Function(from_sig.clone()).intern(&Interner); | ||
273 | let ok = self.coerce_from_safe_fn(from_fn_pointer, &from_sig, to_ty)?; | ||
274 | |||
275 | Ok(ok) | ||
276 | } | ||
277 | _ => self.table.try_unify(&from_ty, to_ty), | ||
111 | } | 278 | } |
279 | } | ||
280 | |||
281 | fn coerce_from_fn_pointer( | ||
282 | &mut self, | ||
283 | from_ty: Ty, | ||
284 | from_f: &FnPointer, | ||
285 | to_ty: &Ty, | ||
286 | ) -> InferResult { | ||
287 | self.coerce_from_safe_fn(from_ty, from_f, to_ty) | ||
288 | } | ||
112 | 289 | ||
113 | // Auto Deref if cannot coerce | 290 | fn coerce_from_safe_fn( |
114 | match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { | 291 | &mut self, |
115 | // FIXME: DerefMut | 292 | from_ty: Ty, |
116 | (TyKind::Ref(.., st1), TyKind::Ref(.., st2)) => { | 293 | from_fn_ptr: &FnPointer, |
117 | self.unify_autoderef_behind_ref(st1, st2) | 294 | to_ty: &Ty, |
295 | ) -> InferResult { | ||
296 | if let TyKind::Function(to_fn_ptr) = to_ty.kind(&Interner) { | ||
297 | if let (chalk_ir::Safety::Safe, chalk_ir::Safety::Unsafe) = | ||
298 | (from_fn_ptr.sig.safety, to_fn_ptr.sig.safety) | ||
299 | { | ||
300 | let from_unsafe = | ||
301 | TyKind::Function(safe_to_unsafe_fn_ty(from_fn_ptr.clone())).intern(&Interner); | ||
302 | return self.table.try_unify(&from_unsafe, to_ty); | ||
118 | } | 303 | } |
304 | } | ||
305 | self.table.try_unify(&from_ty, to_ty) | ||
306 | } | ||
119 | 307 | ||
120 | // Otherwise, normal unify | 308 | /// Attempts to coerce from the type of a non-capturing closure into a |
121 | _ => self.unify(&from_ty, to_ty), | 309 | /// function pointer. |
310 | fn coerce_closure_to_fn( | ||
311 | &mut self, | ||
312 | from_ty: Ty, | ||
313 | from_substs: &Substitution, | ||
314 | to_ty: &Ty, | ||
315 | ) -> InferResult { | ||
316 | match to_ty.kind(&Interner) { | ||
317 | TyKind::Function(fn_ty) /* if from_substs is non-capturing (FIXME) */ => { | ||
318 | // We coerce the closure, which has fn type | ||
319 | // `extern "rust-call" fn((arg0,arg1,...)) -> _` | ||
320 | // to | ||
321 | // `fn(arg0,arg1,...) -> _` | ||
322 | // or | ||
323 | // `unsafe fn(arg0,arg1,...) -> _` | ||
324 | let safety = fn_ty.sig.safety; | ||
325 | let pointer_ty = coerce_closure_fn_ty(from_substs, safety); | ||
326 | self.table.try_unify(&pointer_ty, to_ty) | ||
327 | } | ||
328 | _ => self.table.try_unify(&from_ty, to_ty), | ||
122 | } | 329 | } |
123 | } | 330 | } |
124 | 331 | ||
125 | /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>` | 332 | /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>` |
126 | /// | 333 | /// |
127 | /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html | 334 | /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html |
128 | fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> Option<bool> { | 335 | fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult { |
336 | // These 'if' statements require some explanation. | ||
337 | // The `CoerceUnsized` trait is special - it is only | ||
338 | // possible to write `impl CoerceUnsized<B> for A` where | ||
339 | // A and B have 'matching' fields. This rules out the following | ||
340 | // two types of blanket impls: | ||
341 | // | ||
342 | // `impl<T> CoerceUnsized<T> for SomeType` | ||
343 | // `impl<T> CoerceUnsized<SomeType> for T` | ||
344 | // | ||
345 | // Both of these trigger a special `CoerceUnsized`-related error (E0376) | ||
346 | // | ||
347 | // We can take advantage of this fact to avoid performing unecessary work. | ||
348 | // If either `source` or `target` is a type variable, then any applicable impl | ||
349 | // would need to be generic over the self-type (`impl<T> CoerceUnsized<SomeType> for T`) | ||
350 | // or generic over the `CoerceUnsized` type parameter (`impl<T> CoerceUnsized<T> for | ||
351 | // SomeType`). | ||
352 | // | ||
353 | // However, these are exactly the kinds of impls which are forbidden by | ||
354 | // the compiler! Therefore, we can be sure that coercion will always fail | ||
355 | // when either the source or target type is a type variable. This allows us | ||
356 | // to skip performing any trait selection, and immediately bail out. | ||
357 | if from_ty.is_ty_var() { | ||
358 | return Err(TypeError); | ||
359 | } | ||
360 | if to_ty.is_ty_var() { | ||
361 | return Err(TypeError); | ||
362 | } | ||
363 | |||
364 | // Handle reborrows before trying to solve `Source: CoerceUnsized<Target>`. | ||
365 | let coerce_from = match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { | ||
366 | (TyKind::Ref(from_mt, _, from_inner), TyKind::Ref(to_mt, _, _)) => { | ||
367 | coerce_mutabilities(*from_mt, *to_mt)?; | ||
368 | |||
369 | let lt = static_lifetime(); | ||
370 | TyKind::Ref(*to_mt, lt, from_inner.clone()).intern(&Interner) | ||
371 | } | ||
372 | (TyKind::Ref(from_mt, _, from_inner), TyKind::Raw(to_mt, _)) => { | ||
373 | coerce_mutabilities(*from_mt, *to_mt)?; | ||
374 | |||
375 | TyKind::Raw(*to_mt, from_inner.clone()).intern(&Interner) | ||
376 | } | ||
377 | _ => from_ty.clone(), | ||
378 | }; | ||
379 | |||
129 | let krate = self.resolver.krate().unwrap(); | 380 | let krate = self.resolver.krate().unwrap(); |
130 | let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) { | 381 | let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) { |
131 | Some(LangItemTarget::TraitId(trait_)) => trait_, | 382 | Some(LangItemTarget::TraitId(trait_)) => trait_, |
132 | _ => return None, | 383 | _ => return Err(TypeError), |
133 | }; | 384 | }; |
134 | 385 | ||
135 | let trait_ref = { | 386 | let trait_ref = { |
136 | let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait); | 387 | let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait); |
137 | if b.remaining() != 2 { | 388 | if b.remaining() != 2 { |
138 | // The CoerceUnsized trait should have two generic params: Self and T. | 389 | // The CoerceUnsized trait should have two generic params: Self and T. |
139 | return None; | 390 | return Err(TypeError); |
140 | } | 391 | } |
141 | b.push(from_ty.clone()).push(to_ty.clone()).build() | 392 | b.push(coerce_from.clone()).push(to_ty.clone()).build() |
142 | }; | 393 | }; |
143 | 394 | ||
144 | let goal = InEnvironment::new(&self.trait_env.env, trait_ref.cast(&Interner)); | 395 | let goal: InEnvironment<DomainGoal> = |
396 | InEnvironment::new(&self.trait_env.env, trait_ref.cast(&Interner)); | ||
145 | 397 | ||
146 | let canonicalizer = self.canonicalizer(); | 398 | let canonicalized = self.canonicalize(goal); |
147 | let canonicalized = canonicalizer.canonicalize_obligation(goal); | ||
148 | 399 | ||
149 | let solution = self.db.trait_solve(krate, canonicalized.value.clone())?; | 400 | // FIXME: rustc's coerce_unsized is more specialized -- it only tries to |
401 | // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the | ||
402 | // rest for later. Also, there's some logic about sized type variables. | ||
403 | // Need to find out in what cases this is necessary | ||
404 | let solution = self | ||
405 | .db | ||
406 | .trait_solve(krate, canonicalized.value.clone().cast(&Interner)) | ||
407 | .ok_or(TypeError)?; | ||
150 | 408 | ||
151 | match solution { | 409 | match solution { |
152 | Solution::Unique(v) => { | 410 | Solution::Unique(v) => { |
153 | canonicalized.apply_solution( | 411 | canonicalized.apply_solution( |
154 | self, | 412 | &mut self.table, |
155 | Canonical { | 413 | Canonical { |
156 | binders: v.binders, | 414 | binders: v.binders, |
157 | // FIXME handle constraints | 415 | // FIXME handle constraints |
@@ -159,38 +417,40 @@ impl<'a> InferenceContext<'a> { | |||
159 | }, | 417 | }, |
160 | ); | 418 | ); |
161 | } | 419 | } |
162 | _ => return None, | 420 | // FIXME: should we accept ambiguous results here? |
421 | _ => return Err(TypeError), | ||
163 | }; | 422 | }; |
164 | 423 | ||
165 | Some(true) | 424 | Ok(InferOk { goals: Vec::new() }) |
166 | } | 425 | } |
426 | } | ||
167 | 427 | ||
168 | /// Unify `from_ty` to `to_ty` with optional auto Deref | 428 | fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty { |
169 | /// | 429 | let closure_sig = closure_substs.at(&Interner, 0).assert_ty_ref(&Interner).clone(); |
170 | /// Note that the parameters are already stripped the outer reference. | 430 | match closure_sig.kind(&Interner) { |
171 | fn unify_autoderef_behind_ref(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { | 431 | TyKind::Function(fn_ty) => TyKind::Function(FnPointer { |
172 | let canonicalized = self.canonicalizer().canonicalize_ty(from_ty.clone()); | 432 | num_binders: fn_ty.num_binders, |
173 | let to_ty = self.resolve_ty_shallow(&to_ty); | 433 | sig: FnSig { safety, ..fn_ty.sig }, |
174 | // FIXME: Auto DerefMut | 434 | substitution: fn_ty.substitution.clone(), |
175 | for derefed_ty in autoderef::autoderef( | 435 | }) |
176 | self.db, | 436 | .intern(&Interner), |
177 | self.resolver.krate(), | 437 | _ => TyKind::Error.intern(&Interner), |
178 | InEnvironment { | 438 | } |
179 | goal: canonicalized.value.clone(), | 439 | } |
180 | environment: self.trait_env.env.clone(), | 440 | |
181 | }, | 441 | fn safe_to_unsafe_fn_ty(fn_ty: FnPointer) -> FnPointer { |
182 | ) { | 442 | FnPointer { |
183 | let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value); | 443 | num_binders: fn_ty.num_binders, |
184 | let from_ty = self.resolve_ty_shallow(&derefed_ty); | 444 | sig: FnSig { safety: chalk_ir::Safety::Unsafe, ..fn_ty.sig }, |
185 | // Stop when constructor matches. | 445 | substitution: fn_ty.substitution, |
186 | if from_ty.equals_ctor(&to_ty) { | 446 | } |
187 | // It will not recurse to `coerce`. | 447 | } |
188 | return self.table.unify(&from_ty, &to_ty); | ||
189 | } else if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) { | ||
190 | return true; | ||
191 | } | ||
192 | } | ||
193 | 448 | ||
194 | false | 449 | fn coerce_mutabilities(from: Mutability, to: Mutability) -> Result<(), TypeError> { |
450 | match (from, to) { | ||
451 | (Mutability::Mut, Mutability::Mut) | ||
452 | | (Mutability::Mut, Mutability::Not) | ||
453 | | (Mutability::Not, Mutability::Not) => Ok(()), | ||
454 | (Mutability::Not, Mutability::Mut) => Err(TypeError), | ||
195 | } | 455 | } |
196 | } | 456 | } |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 7278faeec..08c05c67c 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -35,39 +35,43 @@ use super::{ | |||
35 | impl<'a> InferenceContext<'a> { | 35 | impl<'a> InferenceContext<'a> { |
36 | pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { | 36 | pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { |
37 | let ty = self.infer_expr_inner(tgt_expr, expected); | 37 | let ty = self.infer_expr_inner(tgt_expr, expected); |
38 | if ty.is_never() { | 38 | if self.resolve_ty_shallow(&ty).is_never() { |
39 | // Any expression that produces a value of type `!` must have diverged | 39 | // Any expression that produces a value of type `!` must have diverged |
40 | self.diverges = Diverges::Always; | 40 | self.diverges = Diverges::Always; |
41 | } | 41 | } |
42 | let could_unify = self.unify(&ty, &expected.ty); | 42 | if let Some(expected_ty) = expected.only_has_type(&mut self.table) { |
43 | if !could_unify { | 43 | let could_unify = self.unify(&ty, &expected_ty); |
44 | self.result.type_mismatches.insert( | 44 | if !could_unify { |
45 | tgt_expr.into(), | 45 | self.result.type_mismatches.insert( |
46 | TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() }, | 46 | tgt_expr.into(), |
47 | ); | 47 | TypeMismatch { expected: expected_ty.clone(), actual: ty.clone() }, |
48 | ); | ||
49 | } | ||
48 | } | 50 | } |
49 | self.resolve_ty_as_possible(ty) | 51 | ty |
50 | } | 52 | } |
51 | 53 | ||
52 | /// Infer type of expression with possibly implicit coerce to the expected type. | 54 | /// Infer type of expression with possibly implicit coerce to the expected type. |
53 | /// Return the type after possible coercion. | 55 | /// Return the type after possible coercion. |
54 | pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { | 56 | pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { |
55 | let ty = self.infer_expr_inner(expr, &expected); | 57 | let ty = self.infer_expr_inner(expr, &expected); |
56 | let ty = if !self.coerce(&ty, &expected.coercion_target()) { | 58 | let ty = if let Some(target) = expected.only_has_type(&mut self.table) { |
57 | self.result.type_mismatches.insert( | 59 | if !self.coerce(&ty, &target) { |
58 | expr.into(), | 60 | self.result.type_mismatches.insert( |
59 | TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() }, | 61 | expr.into(), |
60 | ); | 62 | TypeMismatch { expected: target.clone(), actual: ty.clone() }, |
61 | // Return actual type when type mismatch. | 63 | ); |
62 | // This is needed for diagnostic when return type mismatch. | 64 | // Return actual type when type mismatch. |
63 | ty | 65 | // This is needed for diagnostic when return type mismatch. |
64 | } else if expected.coercion_target().is_unknown() { | 66 | ty |
65 | ty | 67 | } else { |
68 | target.clone() | ||
69 | } | ||
66 | } else { | 70 | } else { |
67 | expected.ty.clone() | 71 | ty |
68 | }; | 72 | }; |
69 | 73 | ||
70 | self.resolve_ty_as_possible(ty) | 74 | ty |
71 | } | 75 | } |
72 | 76 | ||
73 | fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> { | 77 | fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> { |
@@ -98,10 +102,10 @@ impl<'a> InferenceContext<'a> { | |||
98 | goal: projection.trait_ref(self.db).cast(&Interner), | 102 | goal: projection.trait_ref(self.db).cast(&Interner), |
99 | environment: trait_env, | 103 | environment: trait_env, |
100 | }; | 104 | }; |
101 | let canonical = self.canonicalizer().canonicalize_obligation(obligation.clone()); | 105 | let canonical = self.canonicalize(obligation.clone()); |
102 | if self.db.trait_solve(krate, canonical.value).is_some() { | 106 | if self.db.trait_solve(krate, canonical.value.cast(&Interner)).is_some() { |
103 | self.push_obligation(obligation.goal); | 107 | self.push_obligation(obligation.goal); |
104 | let return_ty = self.normalize_projection_ty(projection); | 108 | let return_ty = self.table.normalize_projection_ty(projection); |
105 | Some((arg_tys, return_ty)) | 109 | Some((arg_tys, return_ty)) |
106 | } else { | 110 | } else { |
107 | None | 111 | None |
@@ -131,17 +135,21 @@ impl<'a> InferenceContext<'a> { | |||
131 | let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); | 135 | let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); |
132 | let mut both_arms_diverge = Diverges::Always; | 136 | let mut both_arms_diverge = Diverges::Always; |
133 | 137 | ||
138 | let mut result_ty = self.table.new_type_var(); | ||
134 | let then_ty = self.infer_expr_inner(*then_branch, &expected); | 139 | let then_ty = self.infer_expr_inner(*then_branch, &expected); |
135 | both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); | 140 | both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); |
141 | result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty); | ||
136 | let else_ty = match else_branch { | 142 | let else_ty = match else_branch { |
137 | Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), | 143 | Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), |
138 | None => TyBuilder::unit(), | 144 | None => TyBuilder::unit(), |
139 | }; | 145 | }; |
140 | both_arms_diverge &= self.diverges; | 146 | both_arms_diverge &= self.diverges; |
147 | // FIXME: create a synthetic `else {}` so we have something to refer to here instead of None? | ||
148 | result_ty = self.coerce_merge_branch(*else_branch, &result_ty, &else_ty); | ||
141 | 149 | ||
142 | self.diverges = condition_diverges | both_arms_diverge; | 150 | self.diverges = condition_diverges | both_arms_diverge; |
143 | 151 | ||
144 | self.coerce_merge_branch(&then_ty, &else_ty) | 152 | result_ty |
145 | } | 153 | } |
146 | Expr::Block { statements, tail, label, id: _ } => { | 154 | Expr::Block { statements, tail, label, id: _ } => { |
147 | let old_resolver = mem::replace( | 155 | let old_resolver = mem::replace( |
@@ -277,12 +285,13 @@ impl<'a> InferenceContext<'a> { | |||
277 | // Eagerly try to relate the closure type with the expected | 285 | // Eagerly try to relate the closure type with the expected |
278 | // type, otherwise we often won't have enough information to | 286 | // type, otherwise we often won't have enough information to |
279 | // infer the body. | 287 | // infer the body. |
280 | self.coerce(&closure_ty, &expected.ty); | 288 | if let Some(t) = expected.only_has_type(&mut self.table) { |
289 | self.coerce(&closure_ty, &t); | ||
290 | } | ||
281 | 291 | ||
282 | // Now go through the argument patterns | 292 | // Now go through the argument patterns |
283 | for (arg_pat, arg_ty) in args.iter().zip(sig_tys) { | 293 | for (arg_pat, arg_ty) in args.iter().zip(sig_tys) { |
284 | let resolved = self.resolve_ty_as_possible(arg_ty); | 294 | self.infer_pat(*arg_pat, &arg_ty, BindingMode::default()); |
285 | self.infer_pat(*arg_pat, &resolved, BindingMode::default()); | ||
286 | } | 295 | } |
287 | 296 | ||
288 | let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); | 297 | let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); |
@@ -297,13 +306,13 @@ impl<'a> InferenceContext<'a> { | |||
297 | } | 306 | } |
298 | Expr::Call { callee, args } => { | 307 | Expr::Call { callee, args } => { |
299 | let callee_ty = self.infer_expr(*callee, &Expectation::none()); | 308 | let callee_ty = self.infer_expr(*callee, &Expectation::none()); |
300 | let canonicalized = self.canonicalizer().canonicalize_ty(callee_ty.clone()); | 309 | let canonicalized = self.canonicalize(callee_ty.clone()); |
301 | let mut derefs = autoderef( | 310 | let mut derefs = autoderef( |
302 | self.db, | 311 | self.db, |
303 | self.resolver.krate(), | 312 | self.resolver.krate(), |
304 | InEnvironment { | 313 | InEnvironment { |
305 | goal: canonicalized.value.clone(), | 314 | goal: canonicalized.value.clone(), |
306 | environment: self.trait_env.env.clone(), | 315 | environment: self.table.trait_env.env.clone(), |
307 | }, | 316 | }, |
308 | ); | 317 | ); |
309 | let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs | 318 | let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs |
@@ -350,7 +359,7 @@ impl<'a> InferenceContext<'a> { | |||
350 | 359 | ||
351 | let arm_ty = self.infer_expr_inner(arm.expr, &expected); | 360 | let arm_ty = self.infer_expr_inner(arm.expr, &expected); |
352 | all_arms_diverge &= self.diverges; | 361 | all_arms_diverge &= self.diverges; |
353 | result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); | 362 | result_ty = self.coerce_merge_branch(Some(arm.expr), &result_ty, &arm_ty); |
354 | } | 363 | } |
355 | 364 | ||
356 | self.diverges = matchee_diverges | all_arms_diverge; | 365 | self.diverges = matchee_diverges | all_arms_diverge; |
@@ -364,12 +373,6 @@ impl<'a> InferenceContext<'a> { | |||
364 | } | 373 | } |
365 | Expr::Continue { .. } => TyKind::Never.intern(&Interner), | 374 | Expr::Continue { .. } => TyKind::Never.intern(&Interner), |
366 | Expr::Break { expr, label } => { | 375 | Expr::Break { expr, label } => { |
367 | let val_ty = if let Some(expr) = expr { | ||
368 | self.infer_expr(*expr, &Expectation::none()) | ||
369 | } else { | ||
370 | TyBuilder::unit() | ||
371 | }; | ||
372 | |||
373 | let last_ty = | 376 | let last_ty = |
374 | if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) { | 377 | if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) { |
375 | ctxt.break_ty.clone() | 378 | ctxt.break_ty.clone() |
@@ -377,7 +380,14 @@ impl<'a> InferenceContext<'a> { | |||
377 | self.err_ty() | 380 | self.err_ty() |
378 | }; | 381 | }; |
379 | 382 | ||
380 | let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); | 383 | let val_ty = if let Some(expr) = expr { |
384 | self.infer_expr(*expr, &Expectation::none()) | ||
385 | } else { | ||
386 | TyBuilder::unit() | ||
387 | }; | ||
388 | |||
389 | // FIXME: create a synthetic `()` during lowering so we have something to refer to here? | ||
390 | let merged_type = self.coerce_merge_branch(*expr, &last_ty, &val_ty); | ||
381 | 391 | ||
382 | if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) { | 392 | if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) { |
383 | ctxt.break_ty = merged_type; | 393 | ctxt.break_ty = merged_type; |
@@ -411,7 +421,9 @@ impl<'a> InferenceContext<'a> { | |||
411 | self.write_variant_resolution(tgt_expr.into(), variant); | 421 | self.write_variant_resolution(tgt_expr.into(), variant); |
412 | } | 422 | } |
413 | 423 | ||
414 | self.unify(&ty, &expected.ty); | 424 | if let Some(t) = expected.only_has_type(&mut self.table) { |
425 | self.unify(&ty, &t); | ||
426 | } | ||
415 | 427 | ||
416 | let substs = ty | 428 | let substs = ty |
417 | .as_adt() | 429 | .as_adt() |
@@ -442,7 +454,7 @@ impl<'a> InferenceContext<'a> { | |||
442 | } | 454 | } |
443 | Expr::Field { expr, name } => { | 455 | Expr::Field { expr, name } => { |
444 | let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none()); | 456 | let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none()); |
445 | let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty); | 457 | let canonicalized = self.canonicalize(receiver_ty); |
446 | let ty = autoderef::autoderef( | 458 | let ty = autoderef::autoderef( |
447 | self.db, | 459 | self.db, |
448 | self.resolver.krate(), | 460 | self.resolver.krate(), |
@@ -514,6 +526,7 @@ impl<'a> InferenceContext<'a> { | |||
514 | self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok()) | 526 | self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok()) |
515 | } | 527 | } |
516 | Expr::Cast { expr, type_ref } => { | 528 | Expr::Cast { expr, type_ref } => { |
529 | // FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary) | ||
517 | let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); | 530 | let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); |
518 | let cast_ty = self.make_ty(type_ref); | 531 | let cast_ty = self.make_ty(type_ref); |
519 | // FIXME check the cast... | 532 | // FIXME check the cast... |
@@ -521,15 +534,17 @@ impl<'a> InferenceContext<'a> { | |||
521 | } | 534 | } |
522 | Expr::Ref { expr, rawness, mutability } => { | 535 | Expr::Ref { expr, rawness, mutability } => { |
523 | let mutability = lower_to_chalk_mutability(*mutability); | 536 | let mutability = lower_to_chalk_mutability(*mutability); |
524 | let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = | 537 | let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = expected |
525 | &expected.ty.as_reference_or_ptr() | 538 | .only_has_type(&mut self.table) |
539 | .as_ref() | ||
540 | .and_then(|t| t.as_reference_or_ptr()) | ||
526 | { | 541 | { |
527 | if *exp_mutability == Mutability::Mut && mutability == Mutability::Not { | 542 | if exp_mutability == Mutability::Mut && mutability == Mutability::Not { |
528 | // FIXME: throw type error - expected mut reference but found shared ref, | 543 | // FIXME: record type error - expected mut reference but found shared ref, |
529 | // which cannot be coerced | 544 | // which cannot be coerced |
530 | } | 545 | } |
531 | if *exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr { | 546 | if exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr { |
532 | // FIXME: throw type error - expected reference but found ptr, | 547 | // FIXME: record type error - expected reference but found ptr, |
533 | // which cannot be coerced | 548 | // which cannot be coerced |
534 | } | 549 | } |
535 | Expectation::rvalue_hint(Ty::clone(exp_inner)) | 550 | Expectation::rvalue_hint(Ty::clone(exp_inner)) |
@@ -556,10 +571,11 @@ impl<'a> InferenceContext<'a> { | |||
556 | } | 571 | } |
557 | Expr::UnaryOp { expr, op } => { | 572 | Expr::UnaryOp { expr, op } => { |
558 | let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); | 573 | let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); |
574 | let inner_ty = self.resolve_ty_shallow(&inner_ty); | ||
559 | match op { | 575 | match op { |
560 | UnaryOp::Deref => match self.resolver.krate() { | 576 | UnaryOp::Deref => match self.resolver.krate() { |
561 | Some(krate) => { | 577 | Some(krate) => { |
562 | let canonicalized = self.canonicalizer().canonicalize_ty(inner_ty); | 578 | let canonicalized = self.canonicalize(inner_ty); |
563 | match autoderef::deref( | 579 | match autoderef::deref( |
564 | self.db, | 580 | self.db, |
565 | krate, | 581 | krate, |
@@ -612,8 +628,10 @@ impl<'a> InferenceContext<'a> { | |||
612 | _ => Expectation::none(), | 628 | _ => Expectation::none(), |
613 | }; | 629 | }; |
614 | let lhs_ty = self.infer_expr(*lhs, &lhs_expectation); | 630 | let lhs_ty = self.infer_expr(*lhs, &lhs_expectation); |
631 | let lhs_ty = self.resolve_ty_shallow(&lhs_ty); | ||
615 | let rhs_expectation = op::binary_op_rhs_expectation(*op, lhs_ty.clone()); | 632 | let rhs_expectation = op::binary_op_rhs_expectation(*op, lhs_ty.clone()); |
616 | let rhs_ty = self.infer_expr(*rhs, &Expectation::has_type(rhs_expectation)); | 633 | let rhs_ty = self.infer_expr(*rhs, &Expectation::has_type(rhs_expectation)); |
634 | let rhs_ty = self.resolve_ty_shallow(&rhs_ty); | ||
617 | 635 | ||
618 | let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone()); | 636 | let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone()); |
619 | 637 | ||
@@ -676,7 +694,7 @@ impl<'a> InferenceContext<'a> { | |||
676 | if let (Some(index_trait), Some(krate)) = | 694 | if let (Some(index_trait), Some(krate)) = |
677 | (self.resolve_ops_index(), self.resolver.krate()) | 695 | (self.resolve_ops_index(), self.resolver.krate()) |
678 | { | 696 | { |
679 | let canonicalized = self.canonicalizer().canonicalize_ty(base_ty); | 697 | let canonicalized = self.canonicalize(base_ty); |
680 | let self_ty = method_resolution::resolve_indexing_op( | 698 | let self_ty = method_resolution::resolve_indexing_op( |
681 | self.db, | 699 | self.db, |
682 | &canonicalized.value, | 700 | &canonicalized.value, |
@@ -696,8 +714,12 @@ impl<'a> InferenceContext<'a> { | |||
696 | } | 714 | } |
697 | } | 715 | } |
698 | Expr::Tuple { exprs } => { | 716 | Expr::Tuple { exprs } => { |
699 | let mut tys = match expected.ty.kind(&Interner) { | 717 | let mut tys = match expected |
700 | TyKind::Tuple(_, substs) => substs | 718 | .only_has_type(&mut self.table) |
719 | .as_ref() | ||
720 | .map(|t| t.kind(&Interner)) | ||
721 | { | ||
722 | Some(TyKind::Tuple(_, substs)) => substs | ||
701 | .iter(&Interner) | 723 | .iter(&Interner) |
702 | .map(|a| a.assert_ty_ref(&Interner).clone()) | 724 | .map(|a| a.assert_ty_ref(&Interner).clone()) |
703 | .chain(repeat_with(|| self.table.new_type_var())) | 725 | .chain(repeat_with(|| self.table.new_type_var())) |
@@ -713,14 +735,16 @@ impl<'a> InferenceContext<'a> { | |||
713 | TyKind::Tuple(tys.len(), Substitution::from_iter(&Interner, tys)).intern(&Interner) | 735 | TyKind::Tuple(tys.len(), Substitution::from_iter(&Interner, tys)).intern(&Interner) |
714 | } | 736 | } |
715 | Expr::Array(array) => { | 737 | Expr::Array(array) => { |
716 | let elem_ty = match expected.ty.kind(&Interner) { | 738 | let elem_ty = |
717 | TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(), | 739 | match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(&Interner)) { |
718 | _ => self.table.new_type_var(), | 740 | Some(TyKind::Array(st, _)) | Some(TyKind::Slice(st)) => st.clone(), |
719 | }; | 741 | _ => self.table.new_type_var(), |
742 | }; | ||
720 | 743 | ||
721 | let len = match array { | 744 | let len = match array { |
722 | Array::ElementList(items) => { | 745 | Array::ElementList(items) => { |
723 | for expr in items.iter() { | 746 | for expr in items.iter() { |
747 | // FIXME: use CoerceMany (coerce_merge_branch) | ||
724 | self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone())); | 748 | self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone())); |
725 | } | 749 | } |
726 | Some(items.len() as u64) | 750 | Some(items.len() as u64) |
@@ -785,7 +809,6 @@ impl<'a> InferenceContext<'a> { | |||
785 | }; | 809 | }; |
786 | // use a new type variable if we got unknown here | 810 | // use a new type variable if we got unknown here |
787 | let ty = self.insert_type_vars_shallow(ty); | 811 | let ty = self.insert_type_vars_shallow(ty); |
788 | let ty = self.resolve_ty_as_possible(ty); | ||
789 | self.write_expr_ty(tgt_expr, ty.clone()); | 812 | self.write_expr_ty(tgt_expr, ty.clone()); |
790 | ty | 813 | ty |
791 | } | 814 | } |
@@ -813,7 +836,6 @@ impl<'a> InferenceContext<'a> { | |||
813 | } | 836 | } |
814 | } | 837 | } |
815 | 838 | ||
816 | let ty = self.resolve_ty_as_possible(ty); | ||
817 | self.infer_pat(*pat, &ty, BindingMode::default()); | 839 | self.infer_pat(*pat, &ty, BindingMode::default()); |
818 | } | 840 | } |
819 | Statement::Expr { expr, .. } => { | 841 | Statement::Expr { expr, .. } => { |
@@ -836,7 +858,9 @@ impl<'a> InferenceContext<'a> { | |||
836 | // we don't even make an attempt at coercion | 858 | // we don't even make an attempt at coercion |
837 | self.table.new_maybe_never_var() | 859 | self.table.new_maybe_never_var() |
838 | } else { | 860 | } else { |
839 | self.coerce(&TyBuilder::unit(), &expected.coercion_target()); | 861 | if let Some(t) = expected.only_has_type(&mut self.table) { |
862 | self.coerce(&TyBuilder::unit(), &t); | ||
863 | } | ||
840 | TyBuilder::unit() | 864 | TyBuilder::unit() |
841 | } | 865 | } |
842 | }; | 866 | }; |
@@ -852,7 +876,7 @@ impl<'a> InferenceContext<'a> { | |||
852 | generic_args: Option<&GenericArgs>, | 876 | generic_args: Option<&GenericArgs>, |
853 | ) -> Ty { | 877 | ) -> Ty { |
854 | let receiver_ty = self.infer_expr(receiver, &Expectation::none()); | 878 | let receiver_ty = self.infer_expr(receiver, &Expectation::none()); |
855 | let canonicalized_receiver = self.canonicalizer().canonicalize_ty(receiver_ty.clone()); | 879 | let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); |
856 | 880 | ||
857 | let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); | 881 | let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); |
858 | 882 | ||
@@ -891,7 +915,8 @@ impl<'a> InferenceContext<'a> { | |||
891 | }; | 915 | }; |
892 | // Apply autoref so the below unification works correctly | 916 | // Apply autoref so the below unification works correctly |
893 | // FIXME: return correct autorefs from lookup_method | 917 | // FIXME: return correct autorefs from lookup_method |
894 | let actual_receiver_ty = match expected_receiver_ty.as_reference() { | 918 | let actual_receiver_ty = match self.resolve_ty_shallow(&expected_receiver_ty).as_reference() |
919 | { | ||
895 | Some((_, lifetime, mutability)) => { | 920 | Some((_, lifetime, mutability)) => { |
896 | TyKind::Ref(mutability, lifetime, derefed_receiver_ty).intern(&Interner) | 921 | TyKind::Ref(mutability, lifetime, derefed_receiver_ty).intern(&Interner) |
897 | } | 922 | } |
@@ -971,6 +996,7 @@ impl<'a> InferenceContext<'a> { | |||
971 | } | 996 | } |
972 | 997 | ||
973 | fn register_obligations_for_call(&mut self, callable_ty: &Ty) { | 998 | fn register_obligations_for_call(&mut self, callable_ty: &Ty) { |
999 | let callable_ty = self.resolve_ty_shallow(&callable_ty); | ||
974 | if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) { | 1000 | if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) { |
975 | let def: CallableDefId = from_chalk(self.db, *fn_def); | 1001 | let def: CallableDefId = from_chalk(self.db, *fn_def); |
976 | let generic_predicates = self.db.generic_predicates(def.into()); | 1002 | let generic_predicates = self.db.generic_predicates(def.into()); |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index b15f4977d..9c8e3b6ae 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -94,14 +94,15 @@ impl<'a> InferenceContext<'a> { | |||
94 | pub(super) fn infer_pat( | 94 | pub(super) fn infer_pat( |
95 | &mut self, | 95 | &mut self, |
96 | pat: PatId, | 96 | pat: PatId, |
97 | mut expected: &Ty, | 97 | expected: &Ty, |
98 | mut default_bm: BindingMode, | 98 | mut default_bm: BindingMode, |
99 | ) -> Ty { | 99 | ) -> Ty { |
100 | let body = Arc::clone(&self.body); // avoid borrow checker problem | 100 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
101 | let mut expected = self.resolve_ty_shallow(expected); | ||
101 | 102 | ||
102 | if is_non_ref_pat(&body, pat) { | 103 | if is_non_ref_pat(&body, pat) { |
103 | while let Some((inner, _lifetime, mutability)) = expected.as_reference() { | 104 | while let Some((inner, _lifetime, mutability)) = expected.as_reference() { |
104 | expected = inner; | 105 | expected = self.resolve_ty_shallow(inner); |
105 | default_bm = match default_bm { | 106 | default_bm = match default_bm { |
106 | BindingMode::Move => BindingMode::Ref(mutability), | 107 | BindingMode::Move => BindingMode::Ref(mutability), |
107 | BindingMode::Ref(Mutability::Not) => BindingMode::Ref(Mutability::Not), | 108 | BindingMode::Ref(Mutability::Not) => BindingMode::Ref(Mutability::Not), |
@@ -147,9 +148,9 @@ impl<'a> InferenceContext<'a> { | |||
147 | } | 148 | } |
148 | Pat::Or(ref pats) => { | 149 | Pat::Or(ref pats) => { |
149 | if let Some((first_pat, rest)) = pats.split_first() { | 150 | if let Some((first_pat, rest)) = pats.split_first() { |
150 | let ty = self.infer_pat(*first_pat, expected, default_bm); | 151 | let ty = self.infer_pat(*first_pat, &expected, default_bm); |
151 | for pat in rest { | 152 | for pat in rest { |
152 | self.infer_pat(*pat, expected, default_bm); | 153 | self.infer_pat(*pat, &expected, default_bm); |
153 | } | 154 | } |
154 | ty | 155 | ty |
155 | } else { | 156 | } else { |
@@ -173,13 +174,13 @@ impl<'a> InferenceContext<'a> { | |||
173 | Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat( | 174 | Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat( |
174 | p.as_deref(), | 175 | p.as_deref(), |
175 | subpats, | 176 | subpats, |
176 | expected, | 177 | &expected, |
177 | default_bm, | 178 | default_bm, |
178 | pat, | 179 | pat, |
179 | *ellipsis, | 180 | *ellipsis, |
180 | ), | 181 | ), |
181 | Pat::Record { path: p, args: fields, ellipsis: _ } => { | 182 | Pat::Record { path: p, args: fields, ellipsis: _ } => { |
182 | self.infer_record_pat(p.as_deref(), fields, expected, default_bm, pat) | 183 | self.infer_record_pat(p.as_deref(), fields, &expected, default_bm, pat) |
183 | } | 184 | } |
184 | Pat::Path(path) => { | 185 | Pat::Path(path) => { |
185 | // FIXME use correct resolver for the surrounding expression | 186 | // FIXME use correct resolver for the surrounding expression |
@@ -193,7 +194,7 @@ impl<'a> InferenceContext<'a> { | |||
193 | BindingMode::convert(*mode) | 194 | BindingMode::convert(*mode) |
194 | }; | 195 | }; |
195 | let inner_ty = if let Some(subpat) = subpat { | 196 | let inner_ty = if let Some(subpat) = subpat { |
196 | self.infer_pat(*subpat, expected, default_bm) | 197 | self.infer_pat(*subpat, &expected, default_bm) |
197 | } else { | 198 | } else { |
198 | expected.clone() | 199 | expected.clone() |
199 | }; | 200 | }; |
@@ -206,7 +207,6 @@ impl<'a> InferenceContext<'a> { | |||
206 | } | 207 | } |
207 | BindingMode::Move => inner_ty.clone(), | 208 | BindingMode::Move => inner_ty.clone(), |
208 | }; | 209 | }; |
209 | let bound_ty = self.resolve_ty_as_possible(bound_ty); | ||
210 | self.write_pat_ty(pat, bound_ty); | 210 | self.write_pat_ty(pat, bound_ty); |
211 | return inner_ty; | 211 | return inner_ty; |
212 | } | 212 | } |
@@ -265,13 +265,12 @@ impl<'a> InferenceContext<'a> { | |||
265 | }; | 265 | }; |
266 | // use a new type variable if we got error type here | 266 | // use a new type variable if we got error type here |
267 | let ty = self.insert_type_vars_shallow(ty); | 267 | let ty = self.insert_type_vars_shallow(ty); |
268 | if !self.unify(&ty, expected) { | 268 | if !self.unify(&ty, &expected) { |
269 | self.result.type_mismatches.insert( | 269 | self.result.type_mismatches.insert( |
270 | pat.into(), | 270 | pat.into(), |
271 | TypeMismatch { expected: expected.clone(), actual: ty.clone() }, | 271 | TypeMismatch { expected: expected.clone(), actual: ty.clone() }, |
272 | ); | 272 | ); |
273 | } | 273 | } |
274 | let ty = self.resolve_ty_as_possible(ty); | ||
275 | self.write_pat_ty(pat, ty.clone()); | 274 | self.write_pat_ty(pat, ty.clone()); |
276 | ty | 275 | ty |
277 | } | 276 | } |
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index 495282eba..14c99eafd 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs | |||
@@ -65,7 +65,6 @@ impl<'a> InferenceContext<'a> { | |||
65 | let typable: ValueTyDefId = match value { | 65 | let typable: ValueTyDefId = match value { |
66 | ValueNs::LocalBinding(pat) => { | 66 | ValueNs::LocalBinding(pat) => { |
67 | let ty = self.result.type_of_pat.get(pat)?.clone(); | 67 | let ty = self.result.type_of_pat.get(pat)?.clone(); |
68 | let ty = self.resolve_ty_as_possible(ty); | ||
69 | return Some(ty); | 68 | return Some(ty); |
70 | } | 69 | } |
71 | ValueNs::FunctionId(it) => it.into(), | 70 | ValueNs::FunctionId(it) => it.into(), |
@@ -218,14 +217,14 @@ impl<'a> InferenceContext<'a> { | |||
218 | return Some(result); | 217 | return Some(result); |
219 | } | 218 | } |
220 | 219 | ||
221 | let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); | 220 | let canonical_ty = self.canonicalize(ty.clone()); |
222 | let krate = self.resolver.krate()?; | 221 | let krate = self.resolver.krate()?; |
223 | let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); | 222 | let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); |
224 | 223 | ||
225 | method_resolution::iterate_method_candidates( | 224 | method_resolution::iterate_method_candidates( |
226 | &canonical_ty.value, | 225 | &canonical_ty.value, |
227 | self.db, | 226 | self.db, |
228 | self.trait_env.clone(), | 227 | self.table.trait_env.clone(), |
229 | krate, | 228 | krate, |
230 | &traits_in_scope, | 229 | &traits_in_scope, |
231 | None, | 230 | None, |
@@ -275,6 +274,7 @@ impl<'a> InferenceContext<'a> { | |||
275 | name: &Name, | 274 | name: &Name, |
276 | id: ExprOrPatId, | 275 | id: ExprOrPatId, |
277 | ) -> Option<(ValueNs, Option<Substitution>)> { | 276 | ) -> Option<(ValueNs, Option<Substitution>)> { |
277 | let ty = self.resolve_ty_shallow(ty); | ||
278 | let (enum_id, subst) = match ty.as_adt() { | 278 | let (enum_id, subst) = match ty.as_adt() { |
279 | Some((AdtId::EnumId(e), subst)) => (e, subst), | 279 | Some((AdtId::EnumId(e), subst)) => (e, subst), |
280 | _ => return None, | 280 | _ => return None, |
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index d8e0b4320..21d3fb54e 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs | |||
@@ -1,177 +1,95 @@ | |||
1 | //! Unification and canonicalization logic. | 1 | //! Unification and canonicalization logic. |
2 | 2 | ||
3 | use std::borrow::Cow; | 3 | use std::{fmt, mem, sync::Arc}; |
4 | 4 | ||
5 | use chalk_ir::{ | 5 | use chalk_ir::{ |
6 | cast::Cast, fold::Fold, interner::HasInterner, FloatTy, IntTy, TyVariableKind, UniverseIndex, | 6 | cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, TyVariableKind, |
7 | VariableKind, | 7 | UniverseIndex, |
8 | }; | 8 | }; |
9 | use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; | 9 | use chalk_solve::infer::ParameterEnaVariableExt; |
10 | use ena::unify::UnifyKey; | ||
10 | 11 | ||
11 | use super::{DomainGoal, InferenceContext}; | 12 | use super::{InferOk, InferResult, InferenceContext, TypeError}; |
12 | use crate::{ | 13 | use crate::{ |
13 | fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, | 14 | db::HirDatabase, fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, |
14 | DebruijnIndex, FnPointer, FnSubst, InEnvironment, InferenceVar, Interner, Scalar, Substitution, | 15 | DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar, Interner, ProjectionTy, |
15 | Ty, TyExt, TyKind, WhereClause, | 16 | Scalar, Solution, Substitution, TraitEnvironment, Ty, TyKind, VariableKind, |
16 | }; | 17 | }; |
17 | 18 | ||
18 | impl<'a> InferenceContext<'a> { | 19 | impl<'a> InferenceContext<'a> { |
19 | pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b> | 20 | pub(super) fn canonicalize<T: Fold<Interner> + HasInterner<Interner = Interner>>( |
21 | &mut self, | ||
22 | t: T, | ||
23 | ) -> Canonicalized<T::Result> | ||
20 | where | 24 | where |
21 | 'a: 'b, | 25 | T::Result: HasInterner<Interner = Interner>, |
22 | { | 26 | { |
23 | Canonicalizer { ctx: self, free_vars: Vec::new(), var_stack: Vec::new() } | 27 | // try to resolve obligations before canonicalizing, since this might |
28 | // result in new knowledge about variables | ||
29 | self.resolve_obligations_as_possible(); | ||
30 | self.table.canonicalize(t) | ||
24 | } | 31 | } |
25 | } | 32 | } |
26 | 33 | ||
27 | pub(super) struct Canonicalizer<'a, 'b> | 34 | #[derive(Debug, Clone)] |
28 | where | ||
29 | 'a: 'b, | ||
30 | { | ||
31 | ctx: &'b mut InferenceContext<'a>, | ||
32 | free_vars: Vec<(InferenceVar, TyVariableKind)>, | ||
33 | /// A stack of type variables that is used to detect recursive types (which | ||
34 | /// are an error, but we need to protect against them to avoid stack | ||
35 | /// overflows). | ||
36 | var_stack: Vec<TypeVarId>, | ||
37 | } | ||
38 | |||
39 | #[derive(Debug)] | ||
40 | pub(super) struct Canonicalized<T> | 35 | pub(super) struct Canonicalized<T> |
41 | where | 36 | where |
42 | T: HasInterner<Interner = Interner>, | 37 | T: HasInterner<Interner = Interner>, |
43 | { | 38 | { |
44 | pub(super) value: Canonical<T>, | 39 | pub(super) value: Canonical<T>, |
45 | free_vars: Vec<(InferenceVar, TyVariableKind)>, | 40 | free_vars: Vec<GenericArg>, |
46 | } | ||
47 | |||
48 | impl<'a, 'b> Canonicalizer<'a, 'b> { | ||
49 | fn add(&mut self, free_var: InferenceVar, kind: TyVariableKind) -> usize { | ||
50 | self.free_vars.iter().position(|&(v, _)| v == free_var).unwrap_or_else(|| { | ||
51 | let next_index = self.free_vars.len(); | ||
52 | self.free_vars.push((free_var, kind)); | ||
53 | next_index | ||
54 | }) | ||
55 | } | ||
56 | |||
57 | fn do_canonicalize<T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>>( | ||
58 | &mut self, | ||
59 | t: T, | ||
60 | binders: DebruijnIndex, | ||
61 | ) -> T { | ||
62 | fold_tys( | ||
63 | t, | ||
64 | |ty, binders| match ty.kind(&Interner) { | ||
65 | &TyKind::InferenceVar(var, kind) => { | ||
66 | let inner = from_inference_var(var); | ||
67 | if self.var_stack.contains(&inner) { | ||
68 | // recursive type | ||
69 | return self.ctx.table.type_variable_table.fallback_value(var, kind); | ||
70 | } | ||
71 | if let Some(known_ty) = | ||
72 | self.ctx.table.var_unification_table.inlined_probe_value(inner).known() | ||
73 | { | ||
74 | self.var_stack.push(inner); | ||
75 | let result = self.do_canonicalize(known_ty.clone(), binders); | ||
76 | self.var_stack.pop(); | ||
77 | result | ||
78 | } else { | ||
79 | let root = self.ctx.table.var_unification_table.find(inner); | ||
80 | let position = self.add(to_inference_var(root), kind); | ||
81 | TyKind::BoundVar(BoundVar::new(binders, position)).intern(&Interner) | ||
82 | } | ||
83 | } | ||
84 | _ => ty, | ||
85 | }, | ||
86 | binders, | ||
87 | ) | ||
88 | } | ||
89 | |||
90 | fn into_canonicalized<T: HasInterner<Interner = Interner>>( | ||
91 | self, | ||
92 | result: T, | ||
93 | ) -> Canonicalized<T> { | ||
94 | let kinds = self | ||
95 | .free_vars | ||
96 | .iter() | ||
97 | .map(|&(_, k)| chalk_ir::WithKind::new(VariableKind::Ty(k), UniverseIndex::ROOT)); | ||
98 | Canonicalized { | ||
99 | value: Canonical { | ||
100 | value: result, | ||
101 | binders: CanonicalVarKinds::from_iter(&Interner, kinds), | ||
102 | }, | ||
103 | free_vars: self.free_vars, | ||
104 | } | ||
105 | } | ||
106 | |||
107 | pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> { | ||
108 | let result = self.do_canonicalize(ty, DebruijnIndex::INNERMOST); | ||
109 | self.into_canonicalized(result) | ||
110 | } | ||
111 | |||
112 | pub(crate) fn canonicalize_obligation( | ||
113 | mut self, | ||
114 | obligation: InEnvironment<DomainGoal>, | ||
115 | ) -> Canonicalized<InEnvironment<DomainGoal>> { | ||
116 | let result = match obligation.goal { | ||
117 | DomainGoal::Holds(wc) => { | ||
118 | DomainGoal::Holds(self.do_canonicalize(wc, DebruijnIndex::INNERMOST)) | ||
119 | } | ||
120 | _ => unimplemented!(), | ||
121 | }; | ||
122 | self.into_canonicalized(InEnvironment { goal: result, environment: obligation.environment }) | ||
123 | } | ||
124 | } | 41 | } |
125 | 42 | ||
126 | impl<T: HasInterner<Interner = Interner>> Canonicalized<T> { | 43 | impl<T: HasInterner<Interner = Interner>> Canonicalized<T> { |
127 | pub(super) fn decanonicalize_ty(&self, ty: Ty) -> Ty { | 44 | pub(super) fn decanonicalize_ty(&self, ty: Ty) -> Ty { |
128 | crate::fold_free_vars(ty, |bound, _binders| { | 45 | chalk_ir::Substitute::apply(&self.free_vars, ty, &Interner) |
129 | let (v, k) = self.free_vars[bound.index]; | ||
130 | TyKind::InferenceVar(v, k).intern(&Interner) | ||
131 | }) | ||
132 | } | 46 | } |
133 | 47 | ||
134 | pub(super) fn apply_solution( | 48 | pub(super) fn apply_solution( |
135 | &self, | 49 | &self, |
136 | ctx: &mut InferenceContext<'_>, | 50 | ctx: &mut InferenceTable, |
137 | solution: Canonical<Substitution>, | 51 | solution: Canonical<Substitution>, |
138 | ) { | 52 | ) { |
139 | // the solution may contain new variables, which we need to convert to new inference vars | 53 | // the solution may contain new variables, which we need to convert to new inference vars |
140 | let new_vars = Substitution::from_iter( | 54 | let new_vars = Substitution::from_iter( |
141 | &Interner, | 55 | &Interner, |
142 | solution.binders.iter(&Interner).map(|k| match k.kind { | 56 | solution.binders.iter(&Interner).map(|k| match k.kind { |
143 | VariableKind::Ty(TyVariableKind::General) => { | 57 | VariableKind::Ty(TyVariableKind::General) => ctx.new_type_var().cast(&Interner), |
144 | ctx.table.new_type_var().cast(&Interner) | 58 | VariableKind::Ty(TyVariableKind::Integer) => ctx.new_integer_var().cast(&Interner), |
145 | } | 59 | VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(&Interner), |
146 | VariableKind::Ty(TyVariableKind::Integer) => { | ||
147 | ctx.table.new_integer_var().cast(&Interner) | ||
148 | } | ||
149 | VariableKind::Ty(TyVariableKind::Float) => { | ||
150 | ctx.table.new_float_var().cast(&Interner) | ||
151 | } | ||
152 | // Chalk can sometimes return new lifetime variables. We just use the static lifetime everywhere | 60 | // Chalk can sometimes return new lifetime variables. We just use the static lifetime everywhere |
153 | VariableKind::Lifetime => static_lifetime().cast(&Interner), | 61 | VariableKind::Lifetime => static_lifetime().cast(&Interner), |
154 | _ => panic!("const variable in solution"), | 62 | _ => panic!("const variable in solution"), |
155 | }), | 63 | }), |
156 | ); | 64 | ); |
157 | for (i, ty) in solution.value.iter(&Interner).enumerate() { | 65 | for (i, v) in solution.value.iter(&Interner).enumerate() { |
158 | let (v, k) = self.free_vars[i]; | 66 | let var = self.free_vars[i].clone(); |
159 | // eagerly replace projections in the type; we may be getting types | 67 | if let Some(ty) = v.ty(&Interner) { |
160 | // e.g. from where clauses where this hasn't happened yet | 68 | // eagerly replace projections in the type; we may be getting types |
161 | let ty = ctx.normalize_associated_types_in( | 69 | // e.g. from where clauses where this hasn't happened yet |
162 | new_vars.apply(ty.assert_ty_ref(&Interner).clone(), &Interner), | 70 | let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), &Interner)); |
163 | ); | 71 | ctx.unify(var.assert_ty_ref(&Interner), &ty); |
164 | ctx.table.unify(&TyKind::InferenceVar(v, k).intern(&Interner), &ty); | 72 | } else { |
73 | let _ = ctx.try_unify(&var, &new_vars.apply(v.clone(), &Interner)); | ||
74 | } | ||
165 | } | 75 | } |
166 | } | 76 | } |
167 | } | 77 | } |
168 | 78 | ||
169 | pub fn could_unify(t1: &Ty, t2: &Ty) -> bool { | 79 | pub fn could_unify( |
170 | InferenceTable::new().unify(t1, t2) | 80 | db: &dyn HirDatabase, |
81 | env: Arc<TraitEnvironment>, | ||
82 | tys: &Canonical<(Ty, Ty)>, | ||
83 | ) -> bool { | ||
84 | unify(db, env, tys).is_some() | ||
171 | } | 85 | } |
172 | 86 | ||
173 | pub(crate) fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substitution> { | 87 | pub(crate) fn unify( |
174 | let mut table = InferenceTable::new(); | 88 | db: &dyn HirDatabase, |
89 | env: Arc<TraitEnvironment>, | ||
90 | tys: &Canonical<(Ty, Ty)>, | ||
91 | ) -> Option<Substitution> { | ||
92 | let mut table = InferenceTable::new(db, env); | ||
175 | let vars = Substitution::from_iter( | 93 | let vars = Substitution::from_iter( |
176 | &Interner, | 94 | &Interner, |
177 | tys.binders | 95 | tys.binders |
@@ -187,77 +105,151 @@ pub(crate) fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substitution> { | |||
187 | } | 105 | } |
188 | // default any type vars that weren't unified back to their original bound vars | 106 | // default any type vars that weren't unified back to their original bound vars |
189 | // (kind of hacky) | 107 | // (kind of hacky) |
190 | for (i, var) in vars.iter(&Interner).enumerate() { | 108 | let find_var = |iv| { |
191 | let var = var.assert_ty_ref(&Interner); | 109 | vars.iter(&Interner).position(|v| match v.interned() { |
192 | if &*table.resolve_ty_shallow(var) == var { | 110 | chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(&Interner), |
193 | table.unify( | 111 | chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(&Interner), |
194 | var, | 112 | chalk_ir::GenericArgData::Const(c) => c.inference_var(&Interner), |
195 | &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i)).intern(&Interner), | 113 | } == Some(iv)) |
196 | ); | 114 | }; |
197 | } | 115 | let fallback = |iv, kind, default, binder| match kind { |
198 | } | 116 | chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv) |
117 | .map_or(default, |i| BoundVar::new(binder, i).to_ty(&Interner).cast(&Interner)), | ||
118 | chalk_ir::VariableKind::Lifetime => find_var(iv) | ||
119 | .map_or(default, |i| BoundVar::new(binder, i).to_lifetime(&Interner).cast(&Interner)), | ||
120 | chalk_ir::VariableKind::Const(ty) => find_var(iv) | ||
121 | .map_or(default, |i| BoundVar::new(binder, i).to_const(&Interner, ty).cast(&Interner)), | ||
122 | }; | ||
199 | Some(Substitution::from_iter( | 123 | Some(Substitution::from_iter( |
200 | &Interner, | 124 | &Interner, |
201 | vars.iter(&Interner) | 125 | vars.iter(&Interner) |
202 | .map(|v| table.resolve_ty_completely(v.assert_ty_ref(&Interner).clone())), | 126 | .map(|v| table.resolve_with_fallback(v.assert_ty_ref(&Interner).clone(), fallback)), |
203 | )) | 127 | )) |
204 | } | 128 | } |
205 | 129 | ||
206 | #[derive(Clone, Debug)] | 130 | #[derive(Copy, Clone, Debug)] |
207 | pub(super) struct TypeVariableTable { | 131 | pub(crate) struct TypeVariableData { |
208 | inner: Vec<TypeVariableData>, | 132 | diverging: bool, |
209 | } | 133 | } |
210 | 134 | ||
211 | impl TypeVariableTable { | 135 | type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>; |
212 | fn push(&mut self, data: TypeVariableData) { | 136 | |
213 | self.inner.push(data); | 137 | #[derive(Clone)] |
138 | pub(crate) struct InferenceTable<'a> { | ||
139 | pub(crate) db: &'a dyn HirDatabase, | ||
140 | pub(crate) trait_env: Arc<TraitEnvironment>, | ||
141 | var_unification_table: ChalkInferenceTable, | ||
142 | type_variable_table: Vec<TypeVariableData>, | ||
143 | pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>, | ||
144 | } | ||
145 | |||
146 | impl<'a> InferenceTable<'a> { | ||
147 | pub(crate) fn new(db: &'a dyn HirDatabase, trait_env: Arc<TraitEnvironment>) -> Self { | ||
148 | InferenceTable { | ||
149 | db, | ||
150 | trait_env, | ||
151 | var_unification_table: ChalkInferenceTable::new(), | ||
152 | type_variable_table: Vec::new(), | ||
153 | pending_obligations: Vec::new(), | ||
154 | } | ||
214 | } | 155 | } |
215 | 156 | ||
216 | pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { | 157 | /// Chalk doesn't know about the `diverging` flag, so when it unifies two |
217 | self.inner[from_inference_var(iv).0 as usize].diverging = diverging; | 158 | /// type variables of which one is diverging, the chosen root might not be |
159 | /// diverging and we have no way of marking it as such at that time. This | ||
160 | /// function goes through all type variables and make sure their root is | ||
161 | /// marked as diverging if necessary, so that resolving them gives the right | ||
162 | /// result. | ||
163 | pub(super) fn propagate_diverging_flag(&mut self) { | ||
164 | for i in 0..self.type_variable_table.len() { | ||
165 | if !self.type_variable_table[i].diverging { | ||
166 | continue; | ||
167 | } | ||
168 | let v = InferenceVar::from(i as u32); | ||
169 | let root = self.var_unification_table.inference_var_root(v); | ||
170 | if let Some(data) = self.type_variable_table.get_mut(root.index() as usize) { | ||
171 | data.diverging = true; | ||
172 | } | ||
173 | } | ||
218 | } | 174 | } |
219 | 175 | ||
220 | fn is_diverging(&mut self, iv: InferenceVar) -> bool { | 176 | pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { |
221 | self.inner[from_inference_var(iv).0 as usize].diverging | 177 | self.type_variable_table[iv.index() as usize].diverging = diverging; |
222 | } | 178 | } |
223 | 179 | ||
224 | fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { | 180 | fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { |
225 | match kind { | 181 | match kind { |
226 | _ if self.inner[from_inference_var(iv).0 as usize].diverging => TyKind::Never, | 182 | _ if self |
183 | .type_variable_table | ||
184 | .get(iv.index() as usize) | ||
185 | .map_or(false, |data| data.diverging) => | ||
186 | { | ||
187 | TyKind::Never | ||
188 | } | ||
227 | TyVariableKind::General => TyKind::Error, | 189 | TyVariableKind::General => TyKind::Error, |
228 | TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)), | 190 | TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)), |
229 | TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)), | 191 | TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)), |
230 | } | 192 | } |
231 | .intern(&Interner) | 193 | .intern(&Interner) |
232 | } | 194 | } |
233 | } | ||
234 | 195 | ||
235 | #[derive(Copy, Clone, Debug)] | 196 | pub(super) fn canonicalize<T: Fold<Interner> + HasInterner<Interner = Interner>>( |
236 | pub(crate) struct TypeVariableData { | 197 | &mut self, |
237 | diverging: bool, | 198 | t: T, |
238 | } | 199 | ) -> Canonicalized<T::Result> |
200 | where | ||
201 | T::Result: HasInterner<Interner = Interner>, | ||
202 | { | ||
203 | let result = self.var_unification_table.canonicalize(&Interner, t); | ||
204 | let free_vars = result | ||
205 | .free_vars | ||
206 | .into_iter() | ||
207 | .map(|free_var| free_var.to_generic_arg(&Interner)) | ||
208 | .collect(); | ||
209 | Canonicalized { value: result.quantified, free_vars } | ||
210 | } | ||
211 | |||
212 | /// Recurses through the given type, normalizing associated types mentioned | ||
213 | /// in it by replacing them by type variables and registering obligations to | ||
214 | /// resolve later. This should be done once for every type we get from some | ||
215 | /// type annotation (e.g. from a let type annotation, field type or function | ||
216 | /// call). `make_ty` handles this already, but e.g. for field types we need | ||
217 | /// to do it as well. | ||
218 | pub(super) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { | ||
219 | fold_tys( | ||
220 | ty, | ||
221 | |ty, _| match ty.kind(&Interner) { | ||
222 | TyKind::Alias(AliasTy::Projection(proj_ty)) => { | ||
223 | self.normalize_projection_ty(proj_ty.clone()) | ||
224 | } | ||
225 | _ => ty, | ||
226 | }, | ||
227 | DebruijnIndex::INNERMOST, | ||
228 | ) | ||
229 | } | ||
239 | 230 | ||
240 | #[derive(Clone, Debug)] | 231 | pub(super) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { |
241 | pub(crate) struct InferenceTable { | 232 | let var = self.new_type_var(); |
242 | pub(super) var_unification_table: InPlaceUnificationTable<TypeVarId>, | 233 | let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; |
243 | pub(super) type_variable_table: TypeVariableTable, | 234 | let obligation = alias_eq.cast(&Interner); |
244 | pub(super) revision: u32, | 235 | self.register_obligation(obligation); |
245 | } | 236 | var |
237 | } | ||
246 | 238 | ||
247 | impl InferenceTable { | 239 | fn extend_type_variable_table(&mut self, to_index: usize) { |
248 | pub(crate) fn new() -> Self { | 240 | self.type_variable_table.extend( |
249 | InferenceTable { | 241 | (0..1 + to_index - self.type_variable_table.len()) |
250 | var_unification_table: InPlaceUnificationTable::new(), | 242 | .map(|_| TypeVariableData { diverging: false }), |
251 | type_variable_table: TypeVariableTable { inner: Vec::new() }, | 243 | ); |
252 | revision: 0, | ||
253 | } | ||
254 | } | 244 | } |
255 | 245 | ||
256 | fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { | 246 | fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { |
257 | self.type_variable_table.push(TypeVariableData { diverging }); | 247 | let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); |
258 | let key = self.var_unification_table.new_key(TypeVarValue::Unknown); | 248 | // Chalk might have created some type variables for its own purposes that we don't know about... |
259 | assert_eq!(key.0 as usize, self.type_variable_table.inner.len() - 1); | 249 | self.extend_type_variable_table(var.index() as usize); |
260 | TyKind::InferenceVar(to_inference_var(key), kind).intern(&Interner) | 250 | assert_eq!(var.index() as usize, self.type_variable_table.len() - 1); |
251 | self.type_variable_table[var.index() as usize].diverging = diverging; | ||
252 | var.to_ty_with_kind(&Interner, kind) | ||
261 | } | 253 | } |
262 | 254 | ||
263 | pub(crate) fn new_type_var(&mut self) -> Ty { | 255 | pub(crate) fn new_type_var(&mut self) -> Ty { |
@@ -276,350 +268,261 @@ impl InferenceTable { | |||
276 | self.new_var(TyVariableKind::General, true) | 268 | self.new_var(TyVariableKind::General, true) |
277 | } | 269 | } |
278 | 270 | ||
279 | pub(crate) fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { | 271 | pub(crate) fn resolve_with_fallback<T>( |
280 | self.resolve_ty_completely_inner(&mut Vec::new(), ty) | 272 | &mut self, |
273 | t: T, | ||
274 | fallback: impl Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, | ||
275 | ) -> T::Result | ||
276 | where | ||
277 | T: HasInterner<Interner = Interner> + Fold<Interner>, | ||
278 | { | ||
279 | self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback) | ||
281 | } | 280 | } |
282 | 281 | ||
283 | pub(crate) fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty { | 282 | fn resolve_with_fallback_inner<T>( |
284 | self.resolve_ty_as_possible_inner(&mut Vec::new(), ty) | 283 | &mut self, |
284 | var_stack: &mut Vec<InferenceVar>, | ||
285 | t: T, | ||
286 | fallback: &impl Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, | ||
287 | ) -> T::Result | ||
288 | where | ||
289 | T: HasInterner<Interner = Interner> + Fold<Interner>, | ||
290 | { | ||
291 | t.fold_with( | ||
292 | &mut resolve::Resolver { table: self, var_stack, fallback }, | ||
293 | DebruijnIndex::INNERMOST, | ||
294 | ) | ||
295 | .expect("fold failed unexpectedly") | ||
285 | } | 296 | } |
286 | 297 | ||
287 | pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { | 298 | pub(crate) fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { |
288 | self.unify_inner(ty1, ty2, 0) | 299 | self.resolve_with_fallback(ty, |_, _, d, _| d) |
289 | } | 300 | } |
290 | 301 | ||
291 | pub(crate) fn unify_substs( | 302 | /// Unify two types and register new trait goals that arise from that. |
292 | &mut self, | 303 | pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { |
293 | substs1: &Substitution, | 304 | let result = if let Ok(r) = self.try_unify(ty1, ty2) { |
294 | substs2: &Substitution, | 305 | r |
295 | depth: usize, | 306 | } else { |
296 | ) -> bool { | 307 | return false; |
297 | substs1.iter(&Interner).zip(substs2.iter(&Interner)).all(|(t1, t2)| { | 308 | }; |
298 | self.unify_inner(t1.assert_ty_ref(&Interner), t2.assert_ty_ref(&Interner), depth) | 309 | self.register_infer_ok(result); |
299 | }) | 310 | true |
300 | } | 311 | } |
301 | 312 | ||
302 | fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool { | 313 | /// Unify two types and return new trait goals arising from it, so the |
303 | if depth > 1000 { | 314 | /// caller needs to deal with them. |
304 | // prevent stackoverflows | 315 | pub(crate) fn try_unify<T: Zip<Interner>>(&mut self, t1: &T, t2: &T) -> InferResult { |
305 | panic!("infinite recursion in unification"); | 316 | match self.var_unification_table.relate( |
306 | } | 317 | &Interner, |
307 | if ty1 == ty2 { | 318 | &self.db, |
308 | return true; | 319 | &self.trait_env.env, |
309 | } | 320 | chalk_ir::Variance::Invariant, |
310 | // try to resolve type vars first | 321 | t1, |
311 | let ty1 = self.resolve_ty_shallow(ty1); | 322 | t2, |
312 | let ty2 = self.resolve_ty_shallow(ty2); | 323 | ) { |
313 | if ty1.equals_ctor(&ty2) { | 324 | Ok(result) => Ok(InferOk { goals: result.goals }), |
314 | match (ty1.kind(&Interner), ty2.kind(&Interner)) { | 325 | Err(chalk_ir::NoSolution) => Err(TypeError), |
315 | (TyKind::Adt(_, substs1), TyKind::Adt(_, substs2)) | ||
316 | | (TyKind::FnDef(_, substs1), TyKind::FnDef(_, substs2)) | ||
317 | | ( | ||
318 | TyKind::Function(FnPointer { substitution: FnSubst(substs1), .. }), | ||
319 | TyKind::Function(FnPointer { substitution: FnSubst(substs2), .. }), | ||
320 | ) | ||
321 | | (TyKind::Tuple(_, substs1), TyKind::Tuple(_, substs2)) | ||
322 | | (TyKind::OpaqueType(_, substs1), TyKind::OpaqueType(_, substs2)) | ||
323 | | (TyKind::AssociatedType(_, substs1), TyKind::AssociatedType(_, substs2)) | ||
324 | | (TyKind::Closure(.., substs1), TyKind::Closure(.., substs2)) => { | ||
325 | self.unify_substs(substs1, substs2, depth + 1) | ||
326 | } | ||
327 | (TyKind::Array(ty1, c1), TyKind::Array(ty2, c2)) if c1 == c2 => { | ||
328 | self.unify_inner(ty1, ty2, depth + 1) | ||
329 | } | ||
330 | (TyKind::Ref(_, _, ty1), TyKind::Ref(_, _, ty2)) | ||
331 | | (TyKind::Raw(_, ty1), TyKind::Raw(_, ty2)) | ||
332 | | (TyKind::Slice(ty1), TyKind::Slice(ty2)) => self.unify_inner(ty1, ty2, depth + 1), | ||
333 | _ => true, /* we checked equals_ctor already */ | ||
334 | } | ||
335 | } else if let (TyKind::Closure(.., substs1), TyKind::Closure(.., substs2)) = | ||
336 | (ty1.kind(&Interner), ty2.kind(&Interner)) | ||
337 | { | ||
338 | self.unify_substs(substs1, substs2, depth + 1) | ||
339 | } else { | ||
340 | self.unify_inner_trivial(&ty1, &ty2, depth) | ||
341 | } | 326 | } |
342 | } | 327 | } |
343 | 328 | ||
344 | pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool { | 329 | /// If `ty` is a type variable with known type, returns that type; |
345 | match (ty1.kind(&Interner), ty2.kind(&Interner)) { | 330 | /// otherwise, return ty. |
346 | (TyKind::Error, _) | (_, TyKind::Error) => true, | 331 | pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { |
332 | self.var_unification_table.normalize_ty_shallow(&Interner, ty).unwrap_or_else(|| ty.clone()) | ||
333 | } | ||
347 | 334 | ||
348 | (TyKind::Placeholder(p1), TyKind::Placeholder(p2)) if *p1 == *p2 => true, | 335 | pub(crate) fn register_obligation(&mut self, goal: Goal) { |
336 | let in_env = InEnvironment::new(&self.trait_env.env, goal); | ||
337 | self.register_obligation_in_env(in_env) | ||
338 | } | ||
349 | 339 | ||
350 | (TyKind::Dyn(dyn1), TyKind::Dyn(dyn2)) | 340 | fn register_obligation_in_env(&mut self, goal: InEnvironment<Goal>) { |
351 | if dyn1.bounds.skip_binders().interned().len() | 341 | let canonicalized = self.canonicalize(goal); |
352 | == dyn2.bounds.skip_binders().interned().len() => | 342 | if !self.try_resolve_obligation(&canonicalized) { |
353 | { | 343 | self.pending_obligations.push(canonicalized); |
354 | for (pred1, pred2) in dyn1 | 344 | } |
355 | .bounds | 345 | } |
356 | .skip_binders() | ||
357 | .interned() | ||
358 | .iter() | ||
359 | .zip(dyn2.bounds.skip_binders().interned().iter()) | ||
360 | { | ||
361 | if !self.unify_preds(pred1.skip_binders(), pred2.skip_binders(), depth + 1) { | ||
362 | return false; | ||
363 | } | ||
364 | } | ||
365 | true | ||
366 | } | ||
367 | 346 | ||
368 | ( | 347 | pub(crate) fn register_infer_ok(&mut self, infer_ok: InferOk) { |
369 | TyKind::InferenceVar(tv1, TyVariableKind::General), | 348 | infer_ok.goals.into_iter().for_each(|goal| self.register_obligation_in_env(goal)); |
370 | TyKind::InferenceVar(tv2, TyVariableKind::General), | 349 | } |
371 | ) | ||
372 | | ( | ||
373 | TyKind::InferenceVar(tv1, TyVariableKind::Integer), | ||
374 | TyKind::InferenceVar(tv2, TyVariableKind::Integer), | ||
375 | ) | ||
376 | | ( | ||
377 | TyKind::InferenceVar(tv1, TyVariableKind::Float), | ||
378 | TyKind::InferenceVar(tv2, TyVariableKind::Float), | ||
379 | ) if self.type_variable_table.is_diverging(*tv1) | ||
380 | == self.type_variable_table.is_diverging(*tv2) => | ||
381 | { | ||
382 | // both type vars are unknown since we tried to resolve them | ||
383 | if !self | ||
384 | .var_unification_table | ||
385 | .unioned(from_inference_var(*tv1), from_inference_var(*tv2)) | ||
386 | { | ||
387 | self.var_unification_table | ||
388 | .union(from_inference_var(*tv1), from_inference_var(*tv2)); | ||
389 | self.revision += 1; | ||
390 | } | ||
391 | true | ||
392 | } | ||
393 | 350 | ||
394 | // The order of MaybeNeverTypeVar matters here. | 351 | pub(crate) fn resolve_obligations_as_possible(&mut self) { |
395 | // Unifying MaybeNeverTypeVar and TypeVar will let the latter become MaybeNeverTypeVar. | 352 | let _span = profile::span("resolve_obligations_as_possible"); |
396 | // Unifying MaybeNeverTypeVar and other concrete type will let the former become it. | 353 | let mut changed = true; |
397 | (TyKind::InferenceVar(tv, TyVariableKind::General), other) | 354 | let mut obligations = Vec::new(); |
398 | | (other, TyKind::InferenceVar(tv, TyVariableKind::General)) | 355 | while changed { |
399 | | ( | 356 | changed = false; |
400 | TyKind::InferenceVar(tv, TyVariableKind::Integer), | 357 | mem::swap(&mut self.pending_obligations, &mut obligations); |
401 | other @ TyKind::Scalar(Scalar::Int(_)), | 358 | for canonicalized in obligations.drain(..) { |
402 | ) | 359 | if !self.check_changed(&canonicalized) { |
403 | | ( | 360 | self.pending_obligations.push(canonicalized); |
404 | other @ TyKind::Scalar(Scalar::Int(_)), | 361 | continue; |
405 | TyKind::InferenceVar(tv, TyVariableKind::Integer), | 362 | } |
406 | ) | 363 | changed = true; |
407 | | ( | 364 | let uncanonical = chalk_ir::Substitute::apply( |
408 | TyKind::InferenceVar(tv, TyVariableKind::Integer), | 365 | &canonicalized.free_vars, |
409 | other @ TyKind::Scalar(Scalar::Uint(_)), | 366 | canonicalized.value.value, |
410 | ) | 367 | &Interner, |
411 | | ( | ||
412 | other @ TyKind::Scalar(Scalar::Uint(_)), | ||
413 | TyKind::InferenceVar(tv, TyVariableKind::Integer), | ||
414 | ) | ||
415 | | ( | ||
416 | TyKind::InferenceVar(tv, TyVariableKind::Float), | ||
417 | other @ TyKind::Scalar(Scalar::Float(_)), | ||
418 | ) | ||
419 | | ( | ||
420 | other @ TyKind::Scalar(Scalar::Float(_)), | ||
421 | TyKind::InferenceVar(tv, TyVariableKind::Float), | ||
422 | ) => { | ||
423 | // the type var is unknown since we tried to resolve it | ||
424 | self.var_unification_table.union_value( | ||
425 | from_inference_var(*tv), | ||
426 | TypeVarValue::Known(other.clone().intern(&Interner)), | ||
427 | ); | 368 | ); |
428 | self.revision += 1; | 369 | self.register_obligation_in_env(uncanonical); |
429 | true | ||
430 | } | 370 | } |
431 | |||
432 | _ => false, | ||
433 | } | 371 | } |
434 | } | 372 | } |
435 | 373 | ||
436 | fn unify_preds(&mut self, pred1: &WhereClause, pred2: &WhereClause, depth: usize) -> bool { | 374 | /// This checks whether any of the free variables in the `canonicalized` |
437 | match (pred1, pred2) { | 375 | /// have changed (either been unified with another variable, or with a |
438 | (WhereClause::Implemented(tr1), WhereClause::Implemented(tr2)) | 376 | /// value). If this is not the case, we don't need to try to solve the goal |
439 | if tr1.trait_id == tr2.trait_id => | 377 | /// again -- it'll give the same result as last time. |
440 | { | 378 | fn check_changed(&mut self, canonicalized: &Canonicalized<InEnvironment<Goal>>) -> bool { |
441 | self.unify_substs(&tr1.substitution, &tr2.substitution, depth + 1) | 379 | canonicalized.free_vars.iter().any(|var| { |
380 | let iv = match var.data(&Interner) { | ||
381 | chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(&Interner), | ||
382 | chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(&Interner), | ||
383 | chalk_ir::GenericArgData::Const(c) => c.inference_var(&Interner), | ||
442 | } | 384 | } |
443 | ( | 385 | .expect("free var is not inference var"); |
444 | WhereClause::AliasEq(AliasEq { alias: alias1, ty: ty1 }), | 386 | if self.var_unification_table.probe_var(iv).is_some() { |
445 | WhereClause::AliasEq(AliasEq { alias: alias2, ty: ty2 }), | 387 | return true; |
446 | ) => { | ||
447 | let (substitution1, substitution2) = match (alias1, alias2) { | ||
448 | (AliasTy::Projection(projection_ty1), AliasTy::Projection(projection_ty2)) | ||
449 | if projection_ty1.associated_ty_id == projection_ty2.associated_ty_id => | ||
450 | { | ||
451 | (&projection_ty1.substitution, &projection_ty2.substitution) | ||
452 | } | ||
453 | (AliasTy::Opaque(opaque1), AliasTy::Opaque(opaque2)) | ||
454 | if opaque1.opaque_ty_id == opaque2.opaque_ty_id => | ||
455 | { | ||
456 | (&opaque1.substitution, &opaque2.substitution) | ||
457 | } | ||
458 | _ => return false, | ||
459 | }; | ||
460 | self.unify_substs(&substitution1, &substitution2, depth + 1) | ||
461 | && self.unify_inner(&ty1, &ty2, depth + 1) | ||
462 | } | 388 | } |
463 | _ => false, | 389 | let root = self.var_unification_table.inference_var_root(iv); |
464 | } | 390 | iv != root |
391 | }) | ||
465 | } | 392 | } |
466 | 393 | ||
467 | /// If `ty` is a type variable with known type, returns that type; | 394 | fn try_resolve_obligation( |
468 | /// otherwise, return ty. | 395 | &mut self, |
469 | pub(crate) fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { | 396 | canonicalized: &Canonicalized<InEnvironment<Goal>>, |
470 | let mut ty = Cow::Borrowed(ty); | 397 | ) -> bool { |
471 | // The type variable could resolve to a int/float variable. Hence try | 398 | let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value.clone()); |
472 | // resolving up to three times; each type of variable shouldn't occur | 399 | |
473 | // more than once | 400 | match solution { |
474 | for i in 0..3 { | 401 | Some(Solution::Unique(canonical_subst)) => { |
475 | if i > 0 { | 402 | canonicalized.apply_solution( |
476 | cov_mark::hit!(type_var_resolves_to_int_var); | 403 | self, |
404 | Canonical { | ||
405 | binders: canonical_subst.binders, | ||
406 | // FIXME: handle constraints | ||
407 | value: canonical_subst.value.subst, | ||
408 | }, | ||
409 | ); | ||
410 | true | ||
477 | } | 411 | } |
478 | match ty.kind(&Interner) { | 412 | Some(Solution::Ambig(Guidance::Definite(substs))) => { |
479 | TyKind::InferenceVar(tv, _) => { | 413 | canonicalized.apply_solution(self, substs); |
480 | let inner = from_inference_var(*tv); | 414 | false |
481 | match self.var_unification_table.inlined_probe_value(inner).known() { | 415 | } |
482 | Some(known_ty) => { | 416 | Some(_) => { |
483 | // The known_ty can't be a type var itself | 417 | // FIXME use this when trying to resolve everything at the end |
484 | ty = Cow::Owned(known_ty.clone()); | 418 | false |
485 | } | 419 | } |
486 | _ => return ty, | 420 | None => { |
487 | } | 421 | // FIXME obligation cannot be fulfilled => diagnostic |
488 | } | 422 | true |
489 | _ => return ty, | ||
490 | } | 423 | } |
491 | } | 424 | } |
492 | log::error!("Inference variable still not resolved: {:?}", ty); | ||
493 | ty | ||
494 | } | ||
495 | |||
496 | /// Resolves the type as far as currently possible, replacing type variables | ||
497 | /// by their known types. All types returned by the infer_* functions should | ||
498 | /// be resolved as far as possible, i.e. contain no type variables with | ||
499 | /// known type. | ||
500 | fn resolve_ty_as_possible_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty { | ||
501 | fold_tys( | ||
502 | ty, | ||
503 | |ty, _| match ty.kind(&Interner) { | ||
504 | &TyKind::InferenceVar(tv, kind) => { | ||
505 | let inner = from_inference_var(tv); | ||
506 | if tv_stack.contains(&inner) { | ||
507 | cov_mark::hit!(type_var_cycles_resolve_as_possible); | ||
508 | // recursive type | ||
509 | return self.type_variable_table.fallback_value(tv, kind); | ||
510 | } | ||
511 | if let Some(known_ty) = | ||
512 | self.var_unification_table.inlined_probe_value(inner).known() | ||
513 | { | ||
514 | // known_ty may contain other variables that are known by now | ||
515 | tv_stack.push(inner); | ||
516 | let result = self.resolve_ty_as_possible_inner(tv_stack, known_ty.clone()); | ||
517 | tv_stack.pop(); | ||
518 | result | ||
519 | } else { | ||
520 | ty | ||
521 | } | ||
522 | } | ||
523 | _ => ty, | ||
524 | }, | ||
525 | DebruijnIndex::INNERMOST, | ||
526 | ) | ||
527 | } | ||
528 | |||
529 | /// Resolves the type completely; type variables without known type are | ||
530 | /// replaced by TyKind::Unknown. | ||
531 | fn resolve_ty_completely_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty { | ||
532 | fold_tys( | ||
533 | ty, | ||
534 | |ty, _| match ty.kind(&Interner) { | ||
535 | &TyKind::InferenceVar(tv, kind) => { | ||
536 | let inner = from_inference_var(tv); | ||
537 | if tv_stack.contains(&inner) { | ||
538 | cov_mark::hit!(type_var_cycles_resolve_completely); | ||
539 | // recursive type | ||
540 | return self.type_variable_table.fallback_value(tv, kind); | ||
541 | } | ||
542 | if let Some(known_ty) = | ||
543 | self.var_unification_table.inlined_probe_value(inner).known() | ||
544 | { | ||
545 | // known_ty may contain other variables that are known by now | ||
546 | tv_stack.push(inner); | ||
547 | let result = self.resolve_ty_completely_inner(tv_stack, known_ty.clone()); | ||
548 | tv_stack.pop(); | ||
549 | result | ||
550 | } else { | ||
551 | self.type_variable_table.fallback_value(tv, kind) | ||
552 | } | ||
553 | } | ||
554 | _ => ty, | ||
555 | }, | ||
556 | DebruijnIndex::INNERMOST, | ||
557 | ) | ||
558 | } | 425 | } |
559 | } | 426 | } |
560 | 427 | ||
561 | /// The ID of a type variable. | 428 | impl<'a> fmt::Debug for InferenceTable<'a> { |
562 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | 429 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
563 | pub(super) struct TypeVarId(pub(super) u32); | 430 | f.debug_struct("InferenceTable").field("num_vars", &self.type_variable_table.len()).finish() |
564 | |||
565 | impl UnifyKey for TypeVarId { | ||
566 | type Value = TypeVarValue; | ||
567 | |||
568 | fn index(&self) -> u32 { | ||
569 | self.0 | ||
570 | } | ||
571 | |||
572 | fn from_index(i: u32) -> Self { | ||
573 | TypeVarId(i) | ||
574 | } | 431 | } |
575 | |||
576 | fn tag() -> &'static str { | ||
577 | "TypeVarId" | ||
578 | } | ||
579 | } | ||
580 | |||
581 | fn from_inference_var(var: InferenceVar) -> TypeVarId { | ||
582 | TypeVarId(var.index()) | ||
583 | } | ||
584 | |||
585 | fn to_inference_var(TypeVarId(index): TypeVarId) -> InferenceVar { | ||
586 | index.into() | ||
587 | } | ||
588 | |||
589 | /// The value of a type variable: either we already know the type, or we don't | ||
590 | /// know it yet. | ||
591 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
592 | pub(super) enum TypeVarValue { | ||
593 | Known(Ty), | ||
594 | Unknown, | ||
595 | } | 432 | } |
596 | 433 | ||
597 | impl TypeVarValue { | 434 | mod resolve { |
598 | fn known(&self) -> Option<&Ty> { | 435 | use super::InferenceTable; |
599 | match self { | 436 | use crate::{ |
600 | TypeVarValue::Known(ty) => Some(ty), | 437 | ConcreteConst, Const, ConstData, ConstValue, DebruijnIndex, GenericArg, InferenceVar, |
601 | TypeVarValue::Unknown => None, | 438 | Interner, Ty, TyVariableKind, VariableKind, |
439 | }; | ||
440 | use chalk_ir::{ | ||
441 | cast::Cast, | ||
442 | fold::{Fold, Folder}, | ||
443 | Fallible, | ||
444 | }; | ||
445 | use hir_def::type_ref::ConstScalar; | ||
446 | |||
447 | pub(super) struct Resolver<'a, 'b, F> { | ||
448 | pub(super) table: &'a mut InferenceTable<'b>, | ||
449 | pub(super) var_stack: &'a mut Vec<InferenceVar>, | ||
450 | pub(super) fallback: F, | ||
451 | } | ||
452 | impl<'a, 'b, 'i, F> Folder<'i, Interner> for Resolver<'a, 'b, F> | ||
453 | where | ||
454 | F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg + 'i, | ||
455 | { | ||
456 | fn as_dyn(&mut self) -> &mut dyn Folder<'i, Interner> { | ||
457 | self | ||
602 | } | 458 | } |
603 | } | ||
604 | } | ||
605 | |||
606 | impl UnifyValue for TypeVarValue { | ||
607 | type Error = NoError; | ||
608 | 459 | ||
609 | fn unify_values(value1: &Self, value2: &Self) -> Result<Self, NoError> { | 460 | fn interner(&self) -> &'i Interner { |
610 | match (value1, value2) { | 461 | &Interner |
611 | // We should never equate two type variables, both of which have | 462 | } |
612 | // known types. Instead, we recursively equate those types. | ||
613 | (TypeVarValue::Known(t1), TypeVarValue::Known(t2)) => panic!( | ||
614 | "equating two type variables, both of which have known types: {:?} and {:?}", | ||
615 | t1, t2 | ||
616 | ), | ||
617 | 463 | ||
618 | // If one side is known, prefer that one. | 464 | fn fold_inference_ty( |
619 | (TypeVarValue::Known(..), TypeVarValue::Unknown) => Ok(value1.clone()), | 465 | &mut self, |
620 | (TypeVarValue::Unknown, TypeVarValue::Known(..)) => Ok(value2.clone()), | 466 | var: InferenceVar, |
467 | kind: TyVariableKind, | ||
468 | outer_binder: DebruijnIndex, | ||
469 | ) -> Fallible<Ty> { | ||
470 | let var = self.table.var_unification_table.inference_var_root(var); | ||
471 | if self.var_stack.contains(&var) { | ||
472 | // recursive type | ||
473 | let default = self.table.fallback_value(var, kind).cast(&Interner); | ||
474 | return Ok((self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) | ||
475 | .assert_ty_ref(&Interner) | ||
476 | .clone()); | ||
477 | } | ||
478 | let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) { | ||
479 | // known_ty may contain other variables that are known by now | ||
480 | self.var_stack.push(var); | ||
481 | let result = | ||
482 | known_ty.fold_with(self, outer_binder).expect("fold failed unexpectedly"); | ||
483 | self.var_stack.pop(); | ||
484 | result.assert_ty_ref(&Interner).clone() | ||
485 | } else { | ||
486 | let default = self.table.fallback_value(var, kind).cast(&Interner); | ||
487 | (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) | ||
488 | .assert_ty_ref(&Interner) | ||
489 | .clone() | ||
490 | }; | ||
491 | Ok(result) | ||
492 | } | ||
621 | 493 | ||
622 | (TypeVarValue::Unknown, TypeVarValue::Unknown) => Ok(TypeVarValue::Unknown), | 494 | fn fold_inference_const( |
495 | &mut self, | ||
496 | ty: Ty, | ||
497 | var: InferenceVar, | ||
498 | outer_binder: DebruijnIndex, | ||
499 | ) -> Fallible<Const> { | ||
500 | let var = self.table.var_unification_table.inference_var_root(var); | ||
501 | let default = ConstData { | ||
502 | ty: ty.clone(), | ||
503 | value: ConstValue::Concrete(ConcreteConst { interned: ConstScalar::Unknown }), | ||
504 | } | ||
505 | .intern(&Interner) | ||
506 | .cast(&Interner); | ||
507 | if self.var_stack.contains(&var) { | ||
508 | // recursive | ||
509 | return Ok((self.fallback)(var, VariableKind::Const(ty), default, outer_binder) | ||
510 | .assert_const_ref(&Interner) | ||
511 | .clone()); | ||
512 | } | ||
513 | let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) { | ||
514 | // known_ty may contain other variables that are known by now | ||
515 | self.var_stack.push(var); | ||
516 | let result = | ||
517 | known_ty.fold_with(self, outer_binder).expect("fold failed unexpectedly"); | ||
518 | self.var_stack.pop(); | ||
519 | result.assert_const_ref(&Interner).clone() | ||
520 | } else { | ||
521 | (self.fallback)(var, VariableKind::Const(ty), default, outer_binder) | ||
522 | .assert_const_ref(&Interner) | ||
523 | .clone() | ||
524 | }; | ||
525 | Ok(result) | ||
623 | } | 526 | } |
624 | } | 527 | } |
625 | } | 528 | } |
diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs index 7b4119747..29ffdd9b7 100644 --- a/crates/hir_ty/src/interner.rs +++ b/crates/hir_ty/src/interner.rs | |||
@@ -15,9 +15,15 @@ use std::{fmt, sync::Arc}; | |||
15 | #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] | 15 | #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] |
16 | pub struct Interner; | 16 | pub struct Interner; |
17 | 17 | ||
18 | #[derive(PartialEq, Eq, Hash, Debug)] | 18 | #[derive(PartialEq, Eq, Hash)] |
19 | pub struct InternedWrapper<T>(T); | 19 | pub struct InternedWrapper<T>(T); |
20 | 20 | ||
21 | impl<T: fmt::Debug> fmt::Debug for InternedWrapper<T> { | ||
22 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
23 | fmt::Debug::fmt(&self.0, f) | ||
24 | } | ||
25 | } | ||
26 | |||
21 | impl<T> std::ops::Deref for InternedWrapper<T> { | 27 | impl<T> std::ops::Deref for InternedWrapper<T> { |
22 | type Target = T; | 28 | type Target = T; |
23 | 29 | ||
@@ -101,66 +107,65 @@ impl chalk_ir::interner::Interner for Interner { | |||
101 | opaque_ty: &chalk_ir::OpaqueTy<Interner>, | 107 | opaque_ty: &chalk_ir::OpaqueTy<Interner>, |
102 | fmt: &mut fmt::Formatter<'_>, | 108 | fmt: &mut fmt::Formatter<'_>, |
103 | ) -> Option<fmt::Result> { | 109 | ) -> Option<fmt::Result> { |
104 | tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt))) | 110 | Some(write!(fmt, "{:?}", opaque_ty.opaque_ty_id)) |
105 | } | 111 | } |
106 | 112 | ||
107 | fn debug_opaque_ty_id( | 113 | fn debug_opaque_ty_id( |
108 | opaque_ty_id: chalk_ir::OpaqueTyId<Self>, | 114 | opaque_ty_id: chalk_ir::OpaqueTyId<Self>, |
109 | fmt: &mut fmt::Formatter<'_>, | 115 | fmt: &mut fmt::Formatter<'_>, |
110 | ) -> Option<fmt::Result> { | 116 | ) -> Option<fmt::Result> { |
111 | tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt))) | 117 | Some(fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish()) |
112 | } | 118 | } |
113 | 119 | ||
114 | fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { | 120 | fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { |
115 | tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt))) | 121 | Some(write!(fmt, "{:?}", ty.data(&Interner))) |
116 | } | 122 | } |
117 | 123 | ||
118 | fn debug_lifetime( | 124 | fn debug_lifetime( |
119 | lifetime: &chalk_ir::Lifetime<Interner>, | 125 | lifetime: &chalk_ir::Lifetime<Interner>, |
120 | fmt: &mut fmt::Formatter<'_>, | 126 | fmt: &mut fmt::Formatter<'_>, |
121 | ) -> Option<fmt::Result> { | 127 | ) -> Option<fmt::Result> { |
122 | tls::with_current_program(|prog| Some(prog?.debug_lifetime(lifetime, fmt))) | 128 | Some(write!(fmt, "{:?}", lifetime.data(&Interner))) |
123 | } | 129 | } |
124 | 130 | ||
125 | fn debug_generic_arg( | 131 | fn debug_generic_arg( |
126 | parameter: &GenericArg, | 132 | parameter: &GenericArg, |
127 | fmt: &mut fmt::Formatter<'_>, | 133 | fmt: &mut fmt::Formatter<'_>, |
128 | ) -> Option<fmt::Result> { | 134 | ) -> Option<fmt::Result> { |
129 | tls::with_current_program(|prog| Some(prog?.debug_generic_arg(parameter, fmt))) | 135 | Some(write!(fmt, "{:?}", parameter.data(&Interner).inner_debug())) |
130 | } | 136 | } |
131 | 137 | ||
132 | fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { | 138 | fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { |
133 | tls::with_current_program(|prog| Some(prog?.debug_goal(goal, fmt))) | 139 | let goal_data = goal.data(&Interner); |
140 | Some(write!(fmt, "{:?}", goal_data)) | ||
134 | } | 141 | } |
135 | 142 | ||
136 | fn debug_goals( | 143 | fn debug_goals( |
137 | goals: &chalk_ir::Goals<Interner>, | 144 | goals: &chalk_ir::Goals<Interner>, |
138 | fmt: &mut fmt::Formatter<'_>, | 145 | fmt: &mut fmt::Formatter<'_>, |
139 | ) -> Option<fmt::Result> { | 146 | ) -> Option<fmt::Result> { |
140 | tls::with_current_program(|prog| Some(prog?.debug_goals(goals, fmt))) | 147 | Some(write!(fmt, "{:?}", goals.debug(&Interner))) |
141 | } | 148 | } |
142 | 149 | ||
143 | fn debug_program_clause_implication( | 150 | fn debug_program_clause_implication( |
144 | pci: &chalk_ir::ProgramClauseImplication<Interner>, | 151 | pci: &chalk_ir::ProgramClauseImplication<Interner>, |
145 | fmt: &mut fmt::Formatter<'_>, | 152 | fmt: &mut fmt::Formatter<'_>, |
146 | ) -> Option<fmt::Result> { | 153 | ) -> Option<fmt::Result> { |
147 | tls::with_current_program(|prog| Some(prog?.debug_program_clause_implication(pci, fmt))) | 154 | Some(write!(fmt, "{:?}", pci.debug(&Interner))) |
148 | } | 155 | } |
149 | 156 | ||
150 | fn debug_substitution( | 157 | fn debug_substitution( |
151 | substitution: &chalk_ir::Substitution<Interner>, | 158 | substitution: &chalk_ir::Substitution<Interner>, |
152 | fmt: &mut fmt::Formatter<'_>, | 159 | fmt: &mut fmt::Formatter<'_>, |
153 | ) -> Option<fmt::Result> { | 160 | ) -> Option<fmt::Result> { |
154 | tls::with_current_program(|prog| Some(prog?.debug_substitution(substitution, fmt))) | 161 | Some(write!(fmt, "{:?}", substitution.debug(&Interner))) |
155 | } | 162 | } |
156 | 163 | ||
157 | fn debug_separator_trait_ref( | 164 | fn debug_separator_trait_ref( |
158 | separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>, | 165 | separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>, |
159 | fmt: &mut fmt::Formatter<'_>, | 166 | fmt: &mut fmt::Formatter<'_>, |
160 | ) -> Option<fmt::Result> { | 167 | ) -> Option<fmt::Result> { |
161 | tls::with_current_program(|prog| { | 168 | Some(write!(fmt, "{:?}", separator_trait_ref.debug(&Interner))) |
162 | Some(prog?.debug_separator_trait_ref(separator_trait_ref, fmt)) | ||
163 | }) | ||
164 | } | 169 | } |
165 | 170 | ||
166 | fn debug_fn_def_id( | 171 | fn debug_fn_def_id( |
@@ -173,47 +178,43 @@ impl chalk_ir::interner::Interner for Interner { | |||
173 | constant: &chalk_ir::Const<Self>, | 178 | constant: &chalk_ir::Const<Self>, |
174 | fmt: &mut fmt::Formatter<'_>, | 179 | fmt: &mut fmt::Formatter<'_>, |
175 | ) -> Option<fmt::Result> { | 180 | ) -> Option<fmt::Result> { |
176 | tls::with_current_program(|prog| Some(prog?.debug_const(constant, fmt))) | 181 | Some(write!(fmt, "{:?}", constant.data(&Interner))) |
177 | } | 182 | } |
178 | fn debug_variable_kinds( | 183 | fn debug_variable_kinds( |
179 | variable_kinds: &chalk_ir::VariableKinds<Self>, | 184 | variable_kinds: &chalk_ir::VariableKinds<Self>, |
180 | fmt: &mut fmt::Formatter<'_>, | 185 | fmt: &mut fmt::Formatter<'_>, |
181 | ) -> Option<fmt::Result> { | 186 | ) -> Option<fmt::Result> { |
182 | tls::with_current_program(|prog| Some(prog?.debug_variable_kinds(variable_kinds, fmt))) | 187 | Some(write!(fmt, "{:?}", variable_kinds.as_slice(&Interner))) |
183 | } | 188 | } |
184 | fn debug_variable_kinds_with_angles( | 189 | fn debug_variable_kinds_with_angles( |
185 | variable_kinds: &chalk_ir::VariableKinds<Self>, | 190 | variable_kinds: &chalk_ir::VariableKinds<Self>, |
186 | fmt: &mut fmt::Formatter<'_>, | 191 | fmt: &mut fmt::Formatter<'_>, |
187 | ) -> Option<fmt::Result> { | 192 | ) -> Option<fmt::Result> { |
188 | tls::with_current_program(|prog| { | 193 | Some(write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner))) |
189 | Some(prog?.debug_variable_kinds_with_angles(variable_kinds, fmt)) | ||
190 | }) | ||
191 | } | 194 | } |
192 | fn debug_canonical_var_kinds( | 195 | fn debug_canonical_var_kinds( |
193 | canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Self>, | 196 | canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Self>, |
194 | fmt: &mut fmt::Formatter<'_>, | 197 | fmt: &mut fmt::Formatter<'_>, |
195 | ) -> Option<fmt::Result> { | 198 | ) -> Option<fmt::Result> { |
196 | tls::with_current_program(|prog| { | 199 | Some(write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner))) |
197 | Some(prog?.debug_canonical_var_kinds(canonical_var_kinds, fmt)) | ||
198 | }) | ||
199 | } | 200 | } |
200 | fn debug_program_clause( | 201 | fn debug_program_clause( |
201 | clause: &chalk_ir::ProgramClause<Self>, | 202 | clause: &chalk_ir::ProgramClause<Self>, |
202 | fmt: &mut fmt::Formatter<'_>, | 203 | fmt: &mut fmt::Formatter<'_>, |
203 | ) -> Option<fmt::Result> { | 204 | ) -> Option<fmt::Result> { |
204 | tls::with_current_program(|prog| Some(prog?.debug_program_clause(clause, fmt))) | 205 | Some(write!(fmt, "{:?}", clause.data(&Interner))) |
205 | } | 206 | } |
206 | fn debug_program_clauses( | 207 | fn debug_program_clauses( |
207 | clauses: &chalk_ir::ProgramClauses<Self>, | 208 | clauses: &chalk_ir::ProgramClauses<Self>, |
208 | fmt: &mut fmt::Formatter<'_>, | 209 | fmt: &mut fmt::Formatter<'_>, |
209 | ) -> Option<fmt::Result> { | 210 | ) -> Option<fmt::Result> { |
210 | tls::with_current_program(|prog| Some(prog?.debug_program_clauses(clauses, fmt))) | 211 | Some(write!(fmt, "{:?}", clauses.as_slice(&Interner))) |
211 | } | 212 | } |
212 | fn debug_quantified_where_clauses( | 213 | fn debug_quantified_where_clauses( |
213 | clauses: &chalk_ir::QuantifiedWhereClauses<Self>, | 214 | clauses: &chalk_ir::QuantifiedWhereClauses<Self>, |
214 | fmt: &mut fmt::Formatter<'_>, | 215 | fmt: &mut fmt::Formatter<'_>, |
215 | ) -> Option<fmt::Result> { | 216 | ) -> Option<fmt::Result> { |
216 | tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt))) | 217 | Some(write!(fmt, "{:?}", clauses.as_slice(&Interner))) |
217 | } | 218 | } |
218 | 219 | ||
219 | fn intern_ty(&self, kind: chalk_ir::TyKind<Self>) -> Self::InternedType { | 220 | fn intern_ty(&self, kind: chalk_ir::TyKind<Self>) -> Self::InternedType { |
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 15b61bedc..72093d75a 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -43,8 +43,9 @@ use hir_def::{ | |||
43 | type_ref::{ConstScalar, Rawness}, | 43 | type_ref::{ConstScalar, Rawness}, |
44 | TypeParamId, | 44 | TypeParamId, |
45 | }; | 45 | }; |
46 | use stdx::always; | ||
46 | 47 | ||
47 | use crate::{db::HirDatabase, display::HirDisplay, utils::generics}; | 48 | use crate::{db::HirDatabase, utils::generics}; |
48 | 49 | ||
49 | pub use autoderef::autoderef; | 50 | pub use autoderef::autoderef; |
50 | pub use builder::TyBuilder; | 51 | pub use builder::TyBuilder; |
@@ -113,6 +114,7 @@ pub type FnSig = chalk_ir::FnSig<Interner>; | |||
113 | 114 | ||
114 | pub type InEnvironment<T> = chalk_ir::InEnvironment<T>; | 115 | pub type InEnvironment<T> = chalk_ir::InEnvironment<T>; |
115 | pub type DomainGoal = chalk_ir::DomainGoal<Interner>; | 116 | pub type DomainGoal = chalk_ir::DomainGoal<Interner>; |
117 | pub type Goal = chalk_ir::Goal<Interner>; | ||
116 | pub type AliasEq = chalk_ir::AliasEq<Interner>; | 118 | pub type AliasEq = chalk_ir::AliasEq<Interner>; |
117 | pub type Solution = chalk_solve::Solution<Interner>; | 119 | pub type Solution = chalk_solve::Solution<Interner>; |
118 | pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>; | 120 | pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>; |
@@ -167,6 +169,7 @@ pub fn make_canonical<T: HasInterner<Interner = Interner>>( | |||
167 | Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) } | 169 | Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) } |
168 | } | 170 | } |
169 | 171 | ||
172 | // FIXME: get rid of this, just replace it by FnPointer | ||
170 | /// A function signature as seen by type inference: Several parameter types and | 173 | /// A function signature as seen by type inference: Several parameter types and |
171 | /// one return type. | 174 | /// one return type. |
172 | #[derive(Clone, PartialEq, Eq, Debug)] | 175 | #[derive(Clone, PartialEq, Eq, Debug)] |
@@ -203,6 +206,17 @@ impl CallableSig { | |||
203 | } | 206 | } |
204 | } | 207 | } |
205 | 208 | ||
209 | pub fn to_fn_ptr(&self) -> FnPointer { | ||
210 | FnPointer { | ||
211 | num_binders: 0, | ||
212 | sig: FnSig { abi: (), safety: Safety::Safe, variadic: self.is_varargs }, | ||
213 | substitution: FnSubst(Substitution::from_iter( | ||
214 | &Interner, | ||
215 | self.params_and_return.iter().cloned(), | ||
216 | )), | ||
217 | } | ||
218 | } | ||
219 | |||
206 | pub fn params(&self) -> &[Ty] { | 220 | pub fn params(&self) -> &[Ty] { |
207 | &self.params_and_return[0..self.params_and_return.len() - 1] | 221 | &self.params_and_return[0..self.params_and_return.len() - 1] |
208 | } | 222 | } |
@@ -314,3 +328,58 @@ pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + Fold<Interner>>( | |||
314 | } | 328 | } |
315 | t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly") | 329 | t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly") |
316 | } | 330 | } |
331 | |||
332 | pub fn replace_errors_with_variables<T>(t: T) -> Canonical<T::Result> | ||
333 | where | ||
334 | T: HasInterner<Interner = Interner> + Fold<Interner>, | ||
335 | T::Result: HasInterner<Interner = Interner>, | ||
336 | { | ||
337 | use chalk_ir::{ | ||
338 | fold::{Folder, SuperFold}, | ||
339 | Fallible, | ||
340 | }; | ||
341 | struct ErrorReplacer { | ||
342 | vars: usize, | ||
343 | } | ||
344 | impl<'i> Folder<'i, Interner> for ErrorReplacer { | ||
345 | fn as_dyn(&mut self) -> &mut dyn Folder<'i, Interner> { | ||
346 | self | ||
347 | } | ||
348 | |||
349 | fn interner(&self) -> &'i Interner { | ||
350 | &Interner | ||
351 | } | ||
352 | |||
353 | fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> { | ||
354 | if let TyKind::Error = ty.kind(&Interner) { | ||
355 | let index = self.vars; | ||
356 | self.vars += 1; | ||
357 | Ok(TyKind::BoundVar(BoundVar::new(outer_binder, index)).intern(&Interner)) | ||
358 | } else { | ||
359 | let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?; | ||
360 | Ok(ty) | ||
361 | } | ||
362 | } | ||
363 | |||
364 | fn fold_inference_ty( | ||
365 | &mut self, | ||
366 | var: InferenceVar, | ||
367 | kind: TyVariableKind, | ||
368 | _outer_binder: DebruijnIndex, | ||
369 | ) -> Fallible<Ty> { | ||
370 | always!(false); | ||
371 | Ok(TyKind::InferenceVar(var, kind).intern(&Interner)) | ||
372 | } | ||
373 | } | ||
374 | let mut error_replacer = ErrorReplacer { vars: 0 }; | ||
375 | let value = t | ||
376 | .fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) | ||
377 | .expect("fold failed unexpectedly"); | ||
378 | let kinds = (0..error_replacer.vars).map(|_| { | ||
379 | chalk_ir::CanonicalVarKind::new( | ||
380 | chalk_ir::VariableKind::Ty(TyVariableKind::General), | ||
381 | chalk_ir::UniverseIndex::ROOT, | ||
382 | ) | ||
383 | }); | ||
384 | Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) } | ||
385 | } | ||
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index bd8bb6028..8a375b973 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -1035,9 +1035,11 @@ pub(crate) fn trait_environment_query( | |||
1035 | clauses.push(program_clause.into_from_env_clause(&Interner)); | 1035 | clauses.push(program_clause.into_from_env_clause(&Interner)); |
1036 | } | 1036 | } |
1037 | 1037 | ||
1038 | let krate = def.module(db.upcast()).krate(); | ||
1039 | |||
1038 | let env = chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses); | 1040 | let env = chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses); |
1039 | 1041 | ||
1040 | Arc::new(TraitEnvironment { traits_from_clauses: traits_in_scope, env }) | 1042 | Arc::new(TraitEnvironment { krate, traits_from_clauses: traits_in_scope, env }) |
1041 | } | 1043 | } |
1042 | 1044 | ||
1043 | /// Resolve the where clause(s) of an item with generics. | 1045 | /// Resolve the where clause(s) of an item with generics. |
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 48bbcfd9f..08e385a42 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -577,6 +577,7 @@ fn iterate_method_candidates_by_receiver( | |||
577 | if iterate_inherent_methods( | 577 | if iterate_inherent_methods( |
578 | self_ty, | 578 | self_ty, |
579 | db, | 579 | db, |
580 | env.clone(), | ||
580 | name, | 581 | name, |
581 | Some(receiver_ty), | 582 | Some(receiver_ty), |
582 | krate, | 583 | krate, |
@@ -613,8 +614,16 @@ fn iterate_method_candidates_for_self_ty( | |||
613 | name: Option<&Name>, | 614 | name: Option<&Name>, |
614 | mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, | 615 | mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, |
615 | ) -> bool { | 616 | ) -> bool { |
616 | if iterate_inherent_methods(self_ty, db, name, None, krate, visible_from_module, &mut callback) | 617 | if iterate_inherent_methods( |
617 | { | 618 | self_ty, |
619 | db, | ||
620 | env.clone(), | ||
621 | name, | ||
622 | None, | ||
623 | krate, | ||
624 | visible_from_module, | ||
625 | &mut callback, | ||
626 | ) { | ||
618 | return true; | 627 | return true; |
619 | } | 628 | } |
620 | iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback) | 629 | iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback) |
@@ -653,12 +662,12 @@ fn iterate_trait_method_candidates( | |||
653 | for (_name, item) in data.items.iter() { | 662 | for (_name, item) in data.items.iter() { |
654 | // Don't pass a `visible_from_module` down to `is_valid_candidate`, | 663 | // Don't pass a `visible_from_module` down to `is_valid_candidate`, |
655 | // since only inherent methods should be included into visibility checking. | 664 | // since only inherent methods should be included into visibility checking. |
656 | if !is_valid_candidate(db, name, receiver_ty, *item, self_ty, None) { | 665 | if !is_valid_candidate(db, env.clone(), name, receiver_ty, *item, self_ty, None) { |
657 | continue; | 666 | continue; |
658 | } | 667 | } |
659 | if !known_implemented { | 668 | if !known_implemented { |
660 | let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone()); | 669 | let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone()); |
661 | if db.trait_solve(krate, goal).is_none() { | 670 | if db.trait_solve(krate, goal.cast(&Interner)).is_none() { |
662 | continue 'traits; | 671 | continue 'traits; |
663 | } | 672 | } |
664 | } | 673 | } |
@@ -675,6 +684,7 @@ fn iterate_trait_method_candidates( | |||
675 | fn iterate_inherent_methods( | 684 | fn iterate_inherent_methods( |
676 | self_ty: &Canonical<Ty>, | 685 | self_ty: &Canonical<Ty>, |
677 | db: &dyn HirDatabase, | 686 | db: &dyn HirDatabase, |
687 | env: Arc<TraitEnvironment>, | ||
678 | name: Option<&Name>, | 688 | name: Option<&Name>, |
679 | receiver_ty: Option<&Canonical<Ty>>, | 689 | receiver_ty: Option<&Canonical<Ty>>, |
680 | krate: CrateId, | 690 | krate: CrateId, |
@@ -690,14 +700,24 @@ fn iterate_inherent_methods( | |||
690 | 700 | ||
691 | for &impl_def in impls.for_self_ty(&self_ty.value) { | 701 | for &impl_def in impls.for_self_ty(&self_ty.value) { |
692 | for &item in db.impl_data(impl_def).items.iter() { | 702 | for &item in db.impl_data(impl_def).items.iter() { |
693 | if !is_valid_candidate(db, name, receiver_ty, item, self_ty, visible_from_module) { | 703 | if !is_valid_candidate( |
704 | db, | ||
705 | env.clone(), | ||
706 | name, | ||
707 | receiver_ty, | ||
708 | item, | ||
709 | self_ty, | ||
710 | visible_from_module, | ||
711 | ) { | ||
694 | continue; | 712 | continue; |
695 | } | 713 | } |
696 | // we have to check whether the self type unifies with the type | 714 | // we have to check whether the self type unifies with the type |
697 | // that the impl is for. If we have a receiver type, this | 715 | // that the impl is for. If we have a receiver type, this |
698 | // already happens in `is_valid_candidate` above; if not, we | 716 | // already happens in `is_valid_candidate` above; if not, we |
699 | // check it here | 717 | // check it here |
700 | if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { | 718 | if receiver_ty.is_none() |
719 | && inherent_impl_substs(db, env.clone(), impl_def, self_ty).is_none() | ||
720 | { | ||
701 | cov_mark::hit!(impl_self_type_match_without_receiver); | 721 | cov_mark::hit!(impl_self_type_match_without_receiver); |
702 | continue; | 722 | continue; |
703 | } | 723 | } |
@@ -722,7 +742,7 @@ pub fn resolve_indexing_op( | |||
722 | let deref_chain = autoderef_method_receiver(db, krate, ty); | 742 | let deref_chain = autoderef_method_receiver(db, krate, ty); |
723 | for ty in deref_chain { | 743 | for ty in deref_chain { |
724 | let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone()); | 744 | let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone()); |
725 | if db.trait_solve(krate, goal).is_some() { | 745 | if db.trait_solve(krate, goal.cast(&Interner)).is_some() { |
726 | return Some(ty); | 746 | return Some(ty); |
727 | } | 747 | } |
728 | } | 748 | } |
@@ -731,6 +751,7 @@ pub fn resolve_indexing_op( | |||
731 | 751 | ||
732 | fn is_valid_candidate( | 752 | fn is_valid_candidate( |
733 | db: &dyn HirDatabase, | 753 | db: &dyn HirDatabase, |
754 | env: Arc<TraitEnvironment>, | ||
734 | name: Option<&Name>, | 755 | name: Option<&Name>, |
735 | receiver_ty: Option<&Canonical<Ty>>, | 756 | receiver_ty: Option<&Canonical<Ty>>, |
736 | item: AssocItemId, | 757 | item: AssocItemId, |
@@ -749,7 +770,7 @@ fn is_valid_candidate( | |||
749 | if !data.has_self_param() { | 770 | if !data.has_self_param() { |
750 | return false; | 771 | return false; |
751 | } | 772 | } |
752 | let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) { | 773 | let transformed_receiver_ty = match transform_receiver_ty(db, env, m, self_ty) { |
753 | Some(ty) => ty, | 774 | Some(ty) => ty, |
754 | None => return false, | 775 | None => return false, |
755 | }; | 776 | }; |
@@ -776,6 +797,7 @@ fn is_valid_candidate( | |||
776 | 797 | ||
777 | pub(crate) fn inherent_impl_substs( | 798 | pub(crate) fn inherent_impl_substs( |
778 | db: &dyn HirDatabase, | 799 | db: &dyn HirDatabase, |
800 | env: Arc<TraitEnvironment>, | ||
779 | impl_id: ImplId, | 801 | impl_id: ImplId, |
780 | self_ty: &Canonical<Ty>, | 802 | self_ty: &Canonical<Ty>, |
781 | ) -> Option<Substitution> { | 803 | ) -> Option<Substitution> { |
@@ -798,7 +820,7 @@ pub(crate) fn inherent_impl_substs( | |||
798 | binders: CanonicalVarKinds::from_iter(&Interner, kinds), | 820 | binders: CanonicalVarKinds::from_iter(&Interner, kinds), |
799 | value: (self_ty_with_vars, self_ty.value.clone()), | 821 | value: (self_ty_with_vars, self_ty.value.clone()), |
800 | }; | 822 | }; |
801 | let substs = super::infer::unify(&tys)?; | 823 | let substs = super::infer::unify(db, env, &tys)?; |
802 | // We only want the substs for the vars we added, not the ones from self_ty. | 824 | // We only want the substs for the vars we added, not the ones from self_ty. |
803 | // Also, if any of the vars we added are still in there, we replace them by | 825 | // Also, if any of the vars we added are still in there, we replace them by |
804 | // Unknown. I think this can only really happen if self_ty contained | 826 | // Unknown. I think this can only really happen if self_ty contained |
@@ -823,6 +845,7 @@ fn fallback_bound_vars(s: Substitution, num_vars_to_keep: usize) -> Substitution | |||
823 | 845 | ||
824 | fn transform_receiver_ty( | 846 | fn transform_receiver_ty( |
825 | db: &dyn HirDatabase, | 847 | db: &dyn HirDatabase, |
848 | env: Arc<TraitEnvironment>, | ||
826 | function_id: FunctionId, | 849 | function_id: FunctionId, |
827 | self_ty: &Canonical<Ty>, | 850 | self_ty: &Canonical<Ty>, |
828 | ) -> Option<Ty> { | 851 | ) -> Option<Ty> { |
@@ -832,7 +855,7 @@ fn transform_receiver_ty( | |||
832 | .fill_with_unknown() | 855 | .fill_with_unknown() |
833 | .build(), | 856 | .build(), |
834 | AssocContainerId::ImplId(impl_id) => { | 857 | AssocContainerId::ImplId(impl_id) => { |
835 | let impl_substs = inherent_impl_substs(db, impl_id, &self_ty)?; | 858 | let impl_substs = inherent_impl_substs(db, env, impl_id, &self_ty)?; |
836 | TyBuilder::subst_for_def(db, function_id) | 859 | TyBuilder::subst_for_def(db, function_id) |
837 | .use_parent_substs(&impl_substs) | 860 | .use_parent_substs(&impl_substs) |
838 | .fill_with_unknown() | 861 | .fill_with_unknown() |
@@ -852,7 +875,7 @@ pub fn implements_trait( | |||
852 | trait_: TraitId, | 875 | trait_: TraitId, |
853 | ) -> bool { | 876 | ) -> bool { |
854 | let goal = generic_implements_goal(db, env, trait_, ty.clone()); | 877 | let goal = generic_implements_goal(db, env, trait_, ty.clone()); |
855 | let solution = db.trait_solve(krate, goal); | 878 | let solution = db.trait_solve(krate, goal.cast(&Interner)); |
856 | 879 | ||
857 | solution.is_some() | 880 | solution.is_some() |
858 | } | 881 | } |
@@ -865,7 +888,7 @@ pub fn implements_trait_unique( | |||
865 | trait_: TraitId, | 888 | trait_: TraitId, |
866 | ) -> bool { | 889 | ) -> bool { |
867 | let goal = generic_implements_goal(db, env, trait_, ty.clone()); | 890 | let goal = generic_implements_goal(db, env, trait_, ty.clone()); |
868 | let solution = db.trait_solve(krate, goal); | 891 | let solution = db.trait_solve(krate, goal.cast(&Interner)); |
869 | 892 | ||
870 | matches!(solution, Some(crate::Solution::Unique(_))) | 893 | matches!(solution, Some(crate::Solution::Unique(_))) |
871 | } | 894 | } |
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index 190471069..bb568ea37 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | 2 | ||
3 | use super::{check_infer, check_infer_with_mismatches}; | 3 | use super::{check_infer, check_infer_with_mismatches, check_types}; |
4 | 4 | ||
5 | #[test] | 5 | #[test] |
6 | fn infer_block_expr_type_mismatch() { | 6 | fn infer_block_expr_type_mismatch() { |
@@ -858,3 +858,57 @@ fn coerce_unsize_generic() { | |||
858 | "]], | 858 | "]], |
859 | ); | 859 | ); |
860 | } | 860 | } |
861 | |||
862 | #[test] | ||
863 | fn infer_two_closures_lub() { | ||
864 | check_types( | ||
865 | r#" | ||
866 | fn foo(c: i32) { | ||
867 | let add = |a: i32, b: i32| a + b; | ||
868 | let sub = |a, b| a - b; | ||
869 | //^ |i32, i32| -> i32 | ||
870 | if c > 42 { add } else { sub }; | ||
871 | //^ fn(i32, i32) -> i32 | ||
872 | } | ||
873 | "#, | ||
874 | ) | ||
875 | } | ||
876 | |||
877 | #[test] | ||
878 | fn infer_match_diverging_branch_1() { | ||
879 | check_types( | ||
880 | r#" | ||
881 | enum Result<T> { Ok(T), Err } | ||
882 | fn parse<T>() -> T { loop {} } | ||
883 | |||
884 | fn test() -> i32 { | ||
885 | let a = match parse() { | ||
886 | Ok(val) => val, | ||
887 | Err => return 0, | ||
888 | }; | ||
889 | a | ||
890 | //^ i32 | ||
891 | } | ||
892 | "#, | ||
893 | ) | ||
894 | } | ||
895 | |||
896 | #[test] | ||
897 | fn infer_match_diverging_branch_2() { | ||
898 | // same as 1 except for order of branches | ||
899 | check_types( | ||
900 | r#" | ||
901 | enum Result<T> { Ok(T), Err } | ||
902 | fn parse<T>() -> T { loop {} } | ||
903 | |||
904 | fn test() -> i32 { | ||
905 | let a = match parse() { | ||
906 | Err => return 0, | ||
907 | Ok(val) => val, | ||
908 | }; | ||
909 | a | ||
910 | //^ i32 | ||
911 | } | ||
912 | "#, | ||
913 | ) | ||
914 | } | ||
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index ddbadbe40..cd08b5c7a 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs | |||
@@ -747,7 +747,7 @@ fn foo(tuple: (u8, i16, f32)) { | |||
747 | 209..210 '_': (u8, i16, f32) | 747 | 209..210 '_': (u8, i16, f32) |
748 | 214..216 '{}': () | 748 | 214..216 '{}': () |
749 | 136..142: expected (u8, i16, f32), got (u8, i16) | 749 | 136..142: expected (u8, i16, f32), got (u8, i16) |
750 | 170..182: expected (u8, i16, f32), got (u8, i16, f32, _) | 750 | 170..182: expected (u8, i16, f32), got (u8, i16, f32, {unknown}) |
751 | "#]], | 751 | "#]], |
752 | ); | 752 | ); |
753 | } | 753 | } |
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index 431861712..ad9edf11c 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs | |||
@@ -86,8 +86,6 @@ fn bug_651() { | |||
86 | 86 | ||
87 | #[test] | 87 | #[test] |
88 | fn recursive_vars() { | 88 | fn recursive_vars() { |
89 | cov_mark::check!(type_var_cycles_resolve_completely); | ||
90 | cov_mark::check!(type_var_cycles_resolve_as_possible); | ||
91 | check_infer( | 89 | check_infer( |
92 | r#" | 90 | r#" |
93 | fn test() { | 91 | fn test() { |
@@ -97,12 +95,12 @@ fn recursive_vars() { | |||
97 | "#, | 95 | "#, |
98 | expect![[r#" | 96 | expect![[r#" |
99 | 10..47 '{ ...&y]; }': () | 97 | 10..47 '{ ...&y]; }': () |
100 | 20..21 'y': &{unknown} | 98 | 20..21 'y': {unknown} |
101 | 24..31 'unknown': &{unknown} | 99 | 24..31 'unknown': {unknown} |
102 | 37..44 '[y, &y]': [&&{unknown}; 2] | 100 | 37..44 '[y, &y]': [{unknown}; 2] |
103 | 38..39 'y': &{unknown} | 101 | 38..39 'y': {unknown} |
104 | 41..43 '&y': &&{unknown} | 102 | 41..43 '&y': &{unknown} |
105 | 42..43 'y': &{unknown} | 103 | 42..43 'y': {unknown} |
106 | "#]], | 104 | "#]], |
107 | ); | 105 | ); |
108 | } | 106 | } |
@@ -119,19 +117,19 @@ fn recursive_vars_2() { | |||
119 | "#, | 117 | "#, |
120 | expect![[r#" | 118 | expect![[r#" |
121 | 10..79 '{ ...x)]; }': () | 119 | 10..79 '{ ...x)]; }': () |
122 | 20..21 'x': &&{unknown} | 120 | 20..21 'x': &{unknown} |
123 | 24..31 'unknown': &&{unknown} | 121 | 24..31 'unknown': &{unknown} |
124 | 41..42 'y': &&{unknown} | 122 | 41..42 'y': {unknown} |
125 | 45..52 'unknown': &&{unknown} | 123 | 45..52 'unknown': {unknown} |
126 | 58..76 '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown}); 2] | 124 | 58..76 '[(x, y..., &x)]': [(&{unknown}, {unknown}); 2] |
127 | 59..65 '(x, y)': (&&&{unknown}, &&&{unknown}) | 125 | 59..65 '(x, y)': (&{unknown}, {unknown}) |
128 | 60..61 'x': &&{unknown} | 126 | 60..61 'x': &{unknown} |
129 | 63..64 'y': &&{unknown} | 127 | 63..64 'y': {unknown} |
130 | 67..75 '(&y, &x)': (&&&{unknown}, &&&{unknown}) | 128 | 67..75 '(&y, &x)': (&{unknown}, {unknown}) |
131 | 68..70 '&y': &&&{unknown} | 129 | 68..70 '&y': &{unknown} |
132 | 69..70 'y': &&{unknown} | 130 | 69..70 'y': {unknown} |
133 | 72..74 '&x': &&&{unknown} | 131 | 72..74 '&x': &&{unknown} |
134 | 73..74 'x': &&{unknown} | 132 | 73..74 'x': &{unknown} |
135 | "#]], | 133 | "#]], |
136 | ); | 134 | ); |
137 | } | 135 | } |
@@ -165,7 +163,6 @@ fn infer_std_crash_1() { | |||
165 | 163 | ||
166 | #[test] | 164 | #[test] |
167 | fn infer_std_crash_2() { | 165 | fn infer_std_crash_2() { |
168 | cov_mark::check!(type_var_resolves_to_int_var); | ||
169 | // caused "equating two type variables, ...", taken from std | 166 | // caused "equating two type variables, ...", taken from std |
170 | check_infer( | 167 | check_infer( |
171 | r#" | 168 | r#" |
@@ -257,27 +254,27 @@ fn infer_std_crash_5() { | |||
257 | expect![[r#" | 254 | expect![[r#" |
258 | 26..322 '{ ... } }': () | 255 | 26..322 '{ ... } }': () |
259 | 32..320 'for co... }': () | 256 | 32..320 'for co... }': () |
260 | 36..43 'content': &{unknown} | 257 | 36..43 'content': {unknown} |
261 | 47..60 'doesnt_matter': {unknown} | 258 | 47..60 'doesnt_matter': {unknown} |
262 | 61..320 '{ ... }': () | 259 | 61..320 '{ ... }': () |
263 | 75..79 'name': &&{unknown} | 260 | 75..79 'name': &{unknown} |
264 | 82..166 'if doe... }': &&{unknown} | 261 | 82..166 'if doe... }': &{unknown} |
265 | 85..98 'doesnt_matter': bool | 262 | 85..98 'doesnt_matter': bool |
266 | 99..128 '{ ... }': &&{unknown} | 263 | 99..128 '{ ... }': &{unknown} |
267 | 113..118 'first': &&{unknown} | 264 | 113..118 'first': &{unknown} |
268 | 134..166 '{ ... }': &&{unknown} | 265 | 134..166 '{ ... }': &{unknown} |
269 | 148..156 '&content': &&{unknown} | 266 | 148..156 '&content': &{unknown} |
270 | 149..156 'content': &{unknown} | 267 | 149..156 'content': {unknown} |
271 | 181..188 'content': &{unknown} | 268 | 181..188 'content': &{unknown} |
272 | 191..313 'if ICE... }': &{unknown} | 269 | 191..313 'if ICE... }': &{unknown} |
273 | 194..231 'ICE_RE..._VALUE': {unknown} | 270 | 194..231 'ICE_RE..._VALUE': {unknown} |
274 | 194..247 'ICE_RE...&name)': bool | 271 | 194..247 'ICE_RE...&name)': bool |
275 | 241..246 '&name': &&&{unknown} | 272 | 241..246 '&name': &&{unknown} |
276 | 242..246 'name': &&{unknown} | 273 | 242..246 'name': &{unknown} |
277 | 248..276 '{ ... }': &&{unknown} | 274 | 248..276 '{ ... }': &{unknown} |
278 | 262..266 'name': &&{unknown} | 275 | 262..266 'name': &{unknown} |
279 | 282..313 '{ ... }': &{unknown} | 276 | 282..313 '{ ... }': {unknown} |
280 | 296..303 'content': &{unknown} | 277 | 296..303 'content': {unknown} |
281 | "#]], | 278 | "#]], |
282 | ); | 279 | ); |
283 | } | 280 | } |
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index a9cd42186..5c70a1fc0 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs | |||
@@ -1040,42 +1040,6 @@ fn infer_in_elseif() { | |||
1040 | } | 1040 | } |
1041 | 1041 | ||
1042 | #[test] | 1042 | #[test] |
1043 | fn infer_closure_unify() { | ||
1044 | check_infer( | ||
1045 | r#" | ||
1046 | fn foo(f: bool) { | ||
1047 | let a = |x| x; | ||
1048 | let b = |x| x; | ||
1049 | let id = if f { a } else { b }; | ||
1050 | id(123); | ||
1051 | } | ||
1052 | "#, | ||
1053 | expect![[r#" | ||
1054 | 7..8 'f': bool | ||
1055 | 16..106 '{ ...23); }': () | ||
1056 | 26..27 'a': |i32| -> i32 | ||
1057 | 30..35 '|x| x': |i32| -> i32 | ||
1058 | 31..32 'x': i32 | ||
1059 | 34..35 'x': i32 | ||
1060 | 45..46 'b': |i32| -> i32 | ||
1061 | 49..54 '|x| x': |i32| -> i32 | ||
1062 | 50..51 'x': i32 | ||
1063 | 53..54 'x': i32 | ||
1064 | 64..66 'id': |i32| -> i32 | ||
1065 | 69..90 'if f {... { b }': |i32| -> i32 | ||
1066 | 72..73 'f': bool | ||
1067 | 74..79 '{ a }': |i32| -> i32 | ||
1068 | 76..77 'a': |i32| -> i32 | ||
1069 | 85..90 '{ b }': |i32| -> i32 | ||
1070 | 87..88 'b': |i32| -> i32 | ||
1071 | 96..98 'id': |i32| -> i32 | ||
1072 | 96..103 'id(123)': i32 | ||
1073 | 99..102 '123': i32 | ||
1074 | "#]], | ||
1075 | ) | ||
1076 | } | ||
1077 | |||
1078 | #[test] | ||
1079 | fn infer_if_match_with_return() { | 1043 | fn infer_if_match_with_return() { |
1080 | check_infer( | 1044 | check_infer( |
1081 | r#" | 1045 | r#" |
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index f80cf9879..a5a2df54c 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs | |||
@@ -3104,7 +3104,7 @@ fn foo() { | |||
3104 | 568..573 'f(&s)': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)> | 3104 | 568..573 'f(&s)': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)> |
3105 | 570..572 '&s': &Option<i32> | 3105 | 570..572 '&s': &Option<i32> |
3106 | 571..572 's': Option<i32> | 3106 | 571..572 's': Option<i32> |
3107 | 549..562: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|_| -> ()> | 3107 | 549..562: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|{unknown}| -> ()> |
3108 | "#]], | 3108 | "#]], |
3109 | ); | 3109 | ); |
3110 | } | 3110 | } |
diff --git a/crates/hir_ty/src/tls.rs b/crates/hir_ty/src/tls.rs index 87c671a42..708797c47 100644 --- a/crates/hir_ty/src/tls.rs +++ b/crates/hir_ty/src/tls.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Implementation of Chalk debug helper functions using TLS. | 1 | //! Implementation of Chalk debug helper functions using TLS. |
2 | use std::fmt; | 2 | use std::fmt::{self, Debug}; |
3 | 3 | ||
4 | use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication}; | 4 | use chalk_ir::AliasTy; |
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
@@ -53,14 +53,6 @@ impl DebugContext<'_> { | |||
53 | write!(fmt, "{}::{}", trait_data.name, type_alias_data.name) | 53 | write!(fmt, "{}::{}", trait_data.name, type_alias_data.name) |
54 | } | 54 | } |
55 | 55 | ||
56 | pub(crate) fn debug_opaque_ty_id( | ||
57 | &self, | ||
58 | opaque_ty_id: chalk_ir::OpaqueTyId<Interner>, | ||
59 | fmt: &mut fmt::Formatter<'_>, | ||
60 | ) -> Result<(), fmt::Error> { | ||
61 | fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish() | ||
62 | } | ||
63 | |||
64 | pub(crate) fn debug_alias( | 56 | pub(crate) fn debug_alias( |
65 | &self, | 57 | &self, |
66 | alias_ty: &AliasTy<Interner>, | 58 | alias_ty: &AliasTy<Interner>, |
@@ -68,7 +60,7 @@ impl DebugContext<'_> { | |||
68 | ) -> Result<(), fmt::Error> { | 60 | ) -> Result<(), fmt::Error> { |
69 | match alias_ty { | 61 | match alias_ty { |
70 | AliasTy::Projection(projection_ty) => self.debug_projection_ty(projection_ty, fmt), | 62 | AliasTy::Projection(projection_ty) => self.debug_projection_ty(projection_ty, fmt), |
71 | AliasTy::Opaque(opaque_ty) => self.debug_opaque_ty(opaque_ty, fmt), | 63 | AliasTy::Opaque(opaque_ty) => opaque_ty.fmt(fmt), |
72 | } | 64 | } |
73 | } | 65 | } |
74 | 66 | ||
@@ -96,79 +88,6 @@ impl DebugContext<'_> { | |||
96 | write!(fmt, ">::{}", type_alias_data.name) | 88 | write!(fmt, ">::{}", type_alias_data.name) |
97 | } | 89 | } |
98 | 90 | ||
99 | pub(crate) fn debug_opaque_ty( | ||
100 | &self, | ||
101 | opaque_ty: &chalk_ir::OpaqueTy<Interner>, | ||
102 | fmt: &mut fmt::Formatter<'_>, | ||
103 | ) -> Result<(), fmt::Error> { | ||
104 | write!(fmt, "{:?}", opaque_ty.opaque_ty_id) | ||
105 | } | ||
106 | |||
107 | pub(crate) fn debug_ty( | ||
108 | &self, | ||
109 | ty: &chalk_ir::Ty<Interner>, | ||
110 | fmt: &mut fmt::Formatter<'_>, | ||
111 | ) -> Result<(), fmt::Error> { | ||
112 | write!(fmt, "{:?}", ty.data(&Interner)) | ||
113 | } | ||
114 | |||
115 | pub(crate) fn debug_lifetime( | ||
116 | &self, | ||
117 | lifetime: &Lifetime<Interner>, | ||
118 | fmt: &mut fmt::Formatter<'_>, | ||
119 | ) -> Result<(), fmt::Error> { | ||
120 | write!(fmt, "{:?}", lifetime.data(&Interner)) | ||
121 | } | ||
122 | |||
123 | pub(crate) fn debug_generic_arg( | ||
124 | &self, | ||
125 | parameter: &GenericArg<Interner>, | ||
126 | fmt: &mut fmt::Formatter<'_>, | ||
127 | ) -> Result<(), fmt::Error> { | ||
128 | write!(fmt, "{:?}", parameter.data(&Interner).inner_debug()) | ||
129 | } | ||
130 | |||
131 | pub(crate) fn debug_goal( | ||
132 | &self, | ||
133 | goal: &Goal<Interner>, | ||
134 | fmt: &mut fmt::Formatter<'_>, | ||
135 | ) -> Result<(), fmt::Error> { | ||
136 | let goal_data = goal.data(&Interner); | ||
137 | write!(fmt, "{:?}", goal_data) | ||
138 | } | ||
139 | |||
140 | pub(crate) fn debug_goals( | ||
141 | &self, | ||
142 | goals: &Goals<Interner>, | ||
143 | fmt: &mut fmt::Formatter<'_>, | ||
144 | ) -> Result<(), fmt::Error> { | ||
145 | write!(fmt, "{:?}", goals.debug(&Interner)) | ||
146 | } | ||
147 | |||
148 | pub(crate) fn debug_program_clause_implication( | ||
149 | &self, | ||
150 | pci: &ProgramClauseImplication<Interner>, | ||
151 | fmt: &mut fmt::Formatter<'_>, | ||
152 | ) -> Result<(), fmt::Error> { | ||
153 | write!(fmt, "{:?}", pci.debug(&Interner)) | ||
154 | } | ||
155 | |||
156 | pub(crate) fn debug_substitution( | ||
157 | &self, | ||
158 | substitution: &chalk_ir::Substitution<Interner>, | ||
159 | fmt: &mut fmt::Formatter<'_>, | ||
160 | ) -> Result<(), fmt::Error> { | ||
161 | write!(fmt, "{:?}", substitution.debug(&Interner)) | ||
162 | } | ||
163 | |||
164 | pub(crate) fn debug_separator_trait_ref( | ||
165 | &self, | ||
166 | separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>, | ||
167 | fmt: &mut fmt::Formatter<'_>, | ||
168 | ) -> Result<(), fmt::Error> { | ||
169 | write!(fmt, "{:?}", separator_trait_ref.debug(&Interner)) | ||
170 | } | ||
171 | |||
172 | pub(crate) fn debug_fn_def_id( | 91 | pub(crate) fn debug_fn_def_id( |
173 | &self, | 92 | &self, |
174 | fn_def_id: chalk_ir::FnDefId<Interner>, | 93 | fn_def_id: chalk_ir::FnDefId<Interner>, |
@@ -190,57 +109,6 @@ impl DebugContext<'_> { | |||
190 | } | 109 | } |
191 | } | 110 | } |
192 | } | 111 | } |
193 | |||
194 | pub(crate) fn debug_const( | ||
195 | &self, | ||
196 | _constant: &chalk_ir::Const<Interner>, | ||
197 | fmt: &mut fmt::Formatter<'_>, | ||
198 | ) -> fmt::Result { | ||
199 | write!(fmt, "const") | ||
200 | } | ||
201 | |||
202 | pub(crate) fn debug_variable_kinds( | ||
203 | &self, | ||
204 | variable_kinds: &chalk_ir::VariableKinds<Interner>, | ||
205 | fmt: &mut fmt::Formatter<'_>, | ||
206 | ) -> fmt::Result { | ||
207 | write!(fmt, "{:?}", variable_kinds.as_slice(&Interner)) | ||
208 | } | ||
209 | pub(crate) fn debug_variable_kinds_with_angles( | ||
210 | &self, | ||
211 | variable_kinds: &chalk_ir::VariableKinds<Interner>, | ||
212 | fmt: &mut fmt::Formatter<'_>, | ||
213 | ) -> fmt::Result { | ||
214 | write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner)) | ||
215 | } | ||
216 | pub(crate) fn debug_canonical_var_kinds( | ||
217 | &self, | ||
218 | canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Interner>, | ||
219 | fmt: &mut fmt::Formatter<'_>, | ||
220 | ) -> fmt::Result { | ||
221 | write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner)) | ||
222 | } | ||
223 | pub(crate) fn debug_program_clause( | ||
224 | &self, | ||
225 | clause: &chalk_ir::ProgramClause<Interner>, | ||
226 | fmt: &mut fmt::Formatter<'_>, | ||
227 | ) -> fmt::Result { | ||
228 | write!(fmt, "{:?}", clause.data(&Interner)) | ||
229 | } | ||
230 | pub(crate) fn debug_program_clauses( | ||
231 | &self, | ||
232 | clauses: &chalk_ir::ProgramClauses<Interner>, | ||
233 | fmt: &mut fmt::Formatter<'_>, | ||
234 | ) -> fmt::Result { | ||
235 | write!(fmt, "{:?}", clauses.as_slice(&Interner)) | ||
236 | } | ||
237 | pub(crate) fn debug_quantified_where_clauses( | ||
238 | &self, | ||
239 | clauses: &chalk_ir::QuantifiedWhereClauses<Interner>, | ||
240 | fmt: &mut fmt::Formatter<'_>, | ||
241 | ) -> fmt::Result { | ||
242 | write!(fmt, "{:?}", clauses.as_slice(&Interner)) | ||
243 | } | ||
244 | } | 112 | } |
245 | 113 | ||
246 | mod unsafe_tls { | 114 | mod unsafe_tls { |
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index 9936d0803..294cb531c 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::env::var; | 3 | use std::env::var; |
4 | 4 | ||
5 | use chalk_ir::cast::Cast; | 5 | use chalk_ir::GoalData; |
6 | use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver}; | 6 | use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver}; |
7 | 7 | ||
8 | use base_db::CrateId; | 8 | use base_db::CrateId; |
@@ -10,7 +10,7 @@ use hir_def::{lang_item::LangItemTarget, TraitId}; | |||
10 | use stdx::panic_context; | 10 | use stdx::panic_context; |
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
13 | db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Guidance, HirDisplay, InEnvironment, | 13 | db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, |
14 | Interner, Solution, TraitRefExt, Ty, TyKind, WhereClause, | 14 | Interner, Solution, TraitRefExt, Ty, TyKind, WhereClause, |
15 | }; | 15 | }; |
16 | 16 | ||
@@ -38,6 +38,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> { | |||
38 | /// we assume that `T: Default`. | 38 | /// we assume that `T: Default`. |
39 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 39 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
40 | pub struct TraitEnvironment { | 40 | pub struct TraitEnvironment { |
41 | pub krate: CrateId, | ||
41 | // When we're using Chalk's Ty we can make this a BTreeMap since it's Ord, | 42 | // When we're using Chalk's Ty we can make this a BTreeMap since it's Ord, |
42 | // but for now it's too annoying... | 43 | // but for now it's too annoying... |
43 | pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, | 44 | pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, |
@@ -45,6 +46,14 @@ pub struct TraitEnvironment { | |||
45 | } | 46 | } |
46 | 47 | ||
47 | impl TraitEnvironment { | 48 | impl TraitEnvironment { |
49 | pub fn empty(krate: CrateId) -> Self { | ||
50 | TraitEnvironment { | ||
51 | krate, | ||
52 | traits_from_clauses: Vec::new(), | ||
53 | env: chalk_ir::Environment::new(&Interner), | ||
54 | } | ||
55 | } | ||
56 | |||
48 | pub(crate) fn traits_in_scope_from_clauses<'a>( | 57 | pub(crate) fn traits_in_scope_from_clauses<'a>( |
49 | &'a self, | 58 | &'a self, |
50 | ty: &'a Ty, | 59 | ty: &'a Ty, |
@@ -59,34 +68,25 @@ impl TraitEnvironment { | |||
59 | } | 68 | } |
60 | } | 69 | } |
61 | 70 | ||
62 | impl Default for TraitEnvironment { | ||
63 | fn default() -> Self { | ||
64 | TraitEnvironment { | ||
65 | traits_from_clauses: Vec::new(), | ||
66 | env: chalk_ir::Environment::new(&Interner), | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | /// Solve a trait goal using Chalk. | 71 | /// Solve a trait goal using Chalk. |
72 | pub(crate) fn trait_solve_query( | 72 | pub(crate) fn trait_solve_query( |
73 | db: &dyn HirDatabase, | 73 | db: &dyn HirDatabase, |
74 | krate: CrateId, | 74 | krate: CrateId, |
75 | goal: Canonical<InEnvironment<DomainGoal>>, | 75 | goal: Canonical<InEnvironment<Goal>>, |
76 | ) -> Option<Solution> { | 76 | ) -> Option<Solution> { |
77 | let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal { | 77 | let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal.data(&Interner) { |
78 | DomainGoal::Holds(WhereClause::Implemented(it)) => { | 78 | GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => { |
79 | db.trait_data(it.hir_trait_id()).name.to_string() | 79 | db.trait_data(it.hir_trait_id()).name.to_string() |
80 | } | 80 | } |
81 | DomainGoal::Holds(WhereClause::AliasEq(_)) => "alias_eq".to_string(), | 81 | GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_string(), |
82 | _ => "??".to_string(), | 82 | _ => "??".to_string(), |
83 | }); | 83 | }); |
84 | log::info!("trait_solve_query({})", goal.value.goal.display(db)); | 84 | log::info!("trait_solve_query({:?})", goal.value.goal); |
85 | 85 | ||
86 | if let DomainGoal::Holds(WhereClause::AliasEq(AliasEq { | 86 | if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq { |
87 | alias: AliasTy::Projection(projection_ty), | 87 | alias: AliasTy::Projection(projection_ty), |
88 | .. | 88 | .. |
89 | })) = &goal.value.goal | 89 | }))) = &goal.value.goal.data(&Interner) |
90 | { | 90 | { |
91 | if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(&Interner).kind(&Interner) { | 91 | if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(&Interner).kind(&Interner) { |
92 | // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible | 92 | // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible |
@@ -94,11 +94,9 @@ pub(crate) fn trait_solve_query( | |||
94 | } | 94 | } |
95 | } | 95 | } |
96 | 96 | ||
97 | let canonical = goal.cast(&Interner); | ||
98 | |||
99 | // We currently don't deal with universes (I think / hope they're not yet | 97 | // We currently don't deal with universes (I think / hope they're not yet |
100 | // relevant for our use cases?) | 98 | // relevant for our use cases?) |
101 | let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 }; | 99 | let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 }; |
102 | solve(db, krate, &u_canonical) | 100 | solve(db, krate, &u_canonical) |
103 | } | 101 | } |
104 | 102 | ||
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index f4b90db3a..ff2a54117 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -50,6 +50,7 @@ mod typing; | |||
50 | mod markdown_remove; | 50 | mod markdown_remove; |
51 | mod doc_links; | 51 | mod doc_links; |
52 | mod view_crate_graph; | 52 | mod view_crate_graph; |
53 | mod view_item_tree; | ||
53 | 54 | ||
54 | use std::sync::Arc; | 55 | use std::sync::Arc; |
55 | 56 | ||
@@ -288,6 +289,10 @@ impl Analysis { | |||
288 | self.with_db(|db| view_hir::view_hir(&db, position)) | 289 | self.with_db(|db| view_hir::view_hir(&db, position)) |
289 | } | 290 | } |
290 | 291 | ||
292 | pub fn view_item_tree(&self, file_id: FileId) -> Cancelable<String> { | ||
293 | self.with_db(|db| view_item_tree::view_item_tree(&db, file_id)) | ||
294 | } | ||
295 | |||
291 | /// Renders the crate graph to GraphViz "dot" syntax. | 296 | /// Renders the crate graph to GraphViz "dot" syntax. |
292 | pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> { | 297 | pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> { |
293 | self.with_db(|db| view_crate_graph::view_crate_graph(&db)) | 298 | self.with_db(|db| view_crate_graph::view_crate_graph(&db)) |
diff --git a/crates/ide/src/view_item_tree.rs b/crates/ide/src/view_item_tree.rs new file mode 100644 index 000000000..3dc03085d --- /dev/null +++ b/crates/ide/src/view_item_tree.rs | |||
@@ -0,0 +1,16 @@ | |||
1 | use hir::db::DefDatabase; | ||
2 | use ide_db::base_db::FileId; | ||
3 | use ide_db::RootDatabase; | ||
4 | |||
5 | // Feature: Debug ItemTree | ||
6 | // | ||
7 | // Displays the ItemTree of the currently open file, for debugging. | ||
8 | // | ||
9 | // |=== | ||
10 | // | Editor | Action Name | ||
11 | // | ||
12 | // | VS Code | **Rust Analyzer: Debug ItemTree** | ||
13 | // |=== | ||
14 | pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String { | ||
15 | db.file_item_tree(file_id.into()).pretty_print() | ||
16 | } | ||
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index dda5a6631..d4748ef3a 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs | |||
@@ -33,20 +33,19 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; | |||
33 | // use super::AssistContext; | 33 | // use super::AssistContext; |
34 | // ``` | 34 | // ``` |
35 | // | 35 | // |
36 | // .Merge Behavior | 36 | // .Import Granularity |
37 | // | 37 | // |
38 | // It is possible to configure how use-trees are merged with the `importMergeBehavior` setting. | 38 | // It is possible to configure how use-trees are merged with the `importGranularity` setting. |
39 | // It has the following configurations: | 39 | // It has the following configurations: |
40 | // | 40 | // |
41 | // - `full`: This setting will cause auto-import to always completely merge use-trees that share the | 41 | // - `crate`: Merge imports from the same crate into a single use statement. This kind of |
42 | // same path prefix while also merging inner trees that share the same path-prefix. This kind of | ||
43 | // nesting is only supported in Rust versions later than 1.24. | 42 | // nesting is only supported in Rust versions later than 1.24. |
44 | // - `last`: This setting will cause auto-import to merge use-trees as long as the resulting tree | 43 | // - `module`: Merge imports from the same module into a single use statement. |
45 | // will only contain a nesting of single segment paths at the very end. | 44 | // - `item`: Don't merge imports at all, creating one import per item. |
46 | // - `none`: This setting will cause auto-import to never merge use-trees keeping them as simple | 45 | // - `preserve`: Do not change the granularity of any imports. For auto-import this has the same |
47 | // paths. | 46 | // effect as `item`. |
48 | // | 47 | // |
49 | // In `VS Code` the configuration for this is `rust-analyzer.assist.importMergeBehavior`. | 48 | // In `VS Code` the configuration for this is `rust-analyzer.assist.importGranularity`. |
50 | // | 49 | // |
51 | // .Import Prefix | 50 | // .Import Prefix |
52 | // | 51 | // |
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs index 31854840c..fc117bd9a 100644 --- a/crates/ide_assists/src/handlers/merge_imports.rs +++ b/crates/ide_assists/src/handlers/merge_imports.rs | |||
@@ -213,6 +213,20 @@ pub(crate) use std::fmt::{Debug, Display}; | |||
213 | } | 213 | } |
214 | 214 | ||
215 | #[test] | 215 | #[test] |
216 | fn merge_pub_in_path_crate() { | ||
217 | check_assist( | ||
218 | merge_imports, | ||
219 | r" | ||
220 | pub(in this::path) use std::fmt$0::Debug; | ||
221 | pub(in this::path) use std::fmt::Display; | ||
222 | ", | ||
223 | r" | ||
224 | pub(in this::path) use std::fmt::{Debug, Display}; | ||
225 | ", | ||
226 | ) | ||
227 | } | ||
228 | |||
229 | #[test] | ||
216 | fn test_merge_nested() { | 230 | fn test_merge_nested() { |
217 | check_assist( | 231 | check_assist( |
218 | merge_imports, | 232 | merge_imports, |
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index b091ab91a..6a9231e07 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -4,7 +4,10 @@ use expect_test::expect; | |||
4 | use hir::Semantics; | 4 | use hir::Semantics; |
5 | use ide_db::{ | 5 | use ide_db::{ |
6 | base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}, | 6 | base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}, |
7 | helpers::{insert_use::InsertUseConfig, merge_imports::MergeBehavior, SnippetCap}, | 7 | helpers::{ |
8 | insert_use::{ImportGranularity, InsertUseConfig}, | ||
9 | SnippetCap, | ||
10 | }, | ||
8 | source_change::FileSystemEdit, | 11 | source_change::FileSystemEdit, |
9 | RootDatabase, | 12 | RootDatabase, |
10 | }; | 13 | }; |
@@ -21,8 +24,9 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { | |||
21 | snippet_cap: SnippetCap::new(true), | 24 | snippet_cap: SnippetCap::new(true), |
22 | allowed: None, | 25 | allowed: None, |
23 | insert_use: InsertUseConfig { | 26 | insert_use: InsertUseConfig { |
24 | merge: Some(MergeBehavior::Crate), | 27 | granularity: ImportGranularity::Crate, |
25 | prefix_kind: hir::PrefixKind::Plain, | 28 | prefix_kind: hir::PrefixKind::Plain, |
29 | enforce_granularity: true, | ||
26 | group: true, | 30 | group: true, |
27 | }, | 31 | }, |
28 | }; | 32 | }; |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 1a762d3dc..6b04ee164 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -323,7 +323,7 @@ fn compute_type_match( | |||
323 | 323 | ||
324 | if completion_ty == expected_type { | 324 | if completion_ty == expected_type { |
325 | Some(CompletionRelevanceTypeMatch::Exact) | 325 | Some(CompletionRelevanceTypeMatch::Exact) |
326 | } else if expected_type.could_unify_with(completion_ty) { | 326 | } else if expected_type.could_unify_with(ctx.db, completion_ty) { |
327 | Some(CompletionRelevanceTypeMatch::CouldUnify) | 327 | Some(CompletionRelevanceTypeMatch::CouldUnify) |
328 | } else { | 328 | } else { |
329 | None | 329 | None |
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs index 939fb2d47..37be575e5 100644 --- a/crates/ide_completion/src/test_utils.rs +++ b/crates/ide_completion/src/test_utils.rs | |||
@@ -3,7 +3,10 @@ | |||
3 | use hir::{PrefixKind, Semantics}; | 3 | use hir::{PrefixKind, Semantics}; |
4 | use ide_db::{ | 4 | use ide_db::{ |
5 | base_db::{fixture::ChangeFixture, FileLoader, FilePosition}, | 5 | base_db::{fixture::ChangeFixture, FileLoader, FilePosition}, |
6 | helpers::{insert_use::InsertUseConfig, merge_imports::MergeBehavior, SnippetCap}, | 6 | helpers::{ |
7 | insert_use::{ImportGranularity, InsertUseConfig}, | ||
8 | SnippetCap, | ||
9 | }, | ||
7 | RootDatabase, | 10 | RootDatabase, |
8 | }; | 11 | }; |
9 | use itertools::Itertools; | 12 | use itertools::Itertools; |
@@ -20,8 +23,9 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { | |||
20 | add_call_argument_snippets: true, | 23 | add_call_argument_snippets: true, |
21 | snippet_cap: SnippetCap::new(true), | 24 | snippet_cap: SnippetCap::new(true), |
22 | insert_use: InsertUseConfig { | 25 | insert_use: InsertUseConfig { |
23 | merge: Some(MergeBehavior::Crate), | 26 | granularity: ImportGranularity::Crate, |
24 | prefix_kind: PrefixKind::Plain, | 27 | prefix_kind: PrefixKind::Plain, |
28 | enforce_granularity: true, | ||
25 | group: true, | 29 | group: true, |
26 | }, | 30 | }, |
27 | }; | 31 | }; |
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index 55cdc4da3..aa61c5bcb 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs | |||
@@ -4,20 +4,36 @@ use std::cmp::Ordering; | |||
4 | use hir::Semantics; | 4 | use hir::Semantics; |
5 | use syntax::{ | 5 | use syntax::{ |
6 | algo, | 6 | algo, |
7 | ast::{self, make, AstNode, PathSegmentKind}, | 7 | ast::{self, make, AstNode, AttrsOwner, ModuleItemOwner, PathSegmentKind, VisibilityOwner}, |
8 | ted, AstToken, Direction, NodeOrToken, SyntaxNode, SyntaxToken, | 8 | ted, AstToken, Direction, NodeOrToken, SyntaxNode, SyntaxToken, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | helpers::merge_imports::{try_merge_imports, use_tree_path_cmp, MergeBehavior}, | 12 | helpers::merge_imports::{ |
13 | common_prefix, eq_attrs, eq_visibility, try_merge_imports, use_tree_path_cmp, MergeBehavior, | ||
14 | }, | ||
13 | RootDatabase, | 15 | RootDatabase, |
14 | }; | 16 | }; |
15 | 17 | ||
16 | pub use hir::PrefixKind; | 18 | pub use hir::PrefixKind; |
17 | 19 | ||
20 | /// How imports should be grouped into use statements. | ||
21 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
22 | pub enum ImportGranularity { | ||
23 | /// Do not change the granularity of any imports and preserve the original structure written by the developer. | ||
24 | Preserve, | ||
25 | /// Merge imports from the same crate into a single use statement. | ||
26 | Crate, | ||
27 | /// Merge imports from the same module into a single use statement. | ||
28 | Module, | ||
29 | /// Flatten imports so that each has its own use statement. | ||
30 | Item, | ||
31 | } | ||
32 | |||
18 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 33 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
19 | pub struct InsertUseConfig { | 34 | pub struct InsertUseConfig { |
20 | pub merge: Option<MergeBehavior>, | 35 | pub granularity: ImportGranularity, |
36 | pub enforce_granularity: bool, | ||
21 | pub prefix_kind: PrefixKind, | 37 | pub prefix_kind: PrefixKind, |
22 | pub group: bool, | 38 | pub group: bool, |
23 | } | 39 | } |
@@ -65,15 +81,96 @@ impl ImportScope { | |||
65 | ImportScope::Module(item_list) => ImportScope::Module(item_list.clone_for_update()), | 81 | ImportScope::Module(item_list) => ImportScope::Module(item_list.clone_for_update()), |
66 | } | 82 | } |
67 | } | 83 | } |
84 | |||
85 | fn guess_granularity_from_scope(&self) -> ImportGranularityGuess { | ||
86 | // The idea is simple, just check each import as well as the import and its precedent together for | ||
87 | // whether they fulfill a granularity criteria. | ||
88 | let use_stmt = |item| match item { | ||
89 | ast::Item::Use(use_) => { | ||
90 | let use_tree = use_.use_tree()?; | ||
91 | Some((use_tree, use_.visibility(), use_.attrs())) | ||
92 | } | ||
93 | _ => None, | ||
94 | }; | ||
95 | let mut use_stmts = match self { | ||
96 | ImportScope::File(f) => f.items(), | ||
97 | ImportScope::Module(m) => m.items(), | ||
98 | } | ||
99 | .filter_map(use_stmt); | ||
100 | let mut res = ImportGranularityGuess::Unknown; | ||
101 | let (mut prev, mut prev_vis, mut prev_attrs) = match use_stmts.next() { | ||
102 | Some(it) => it, | ||
103 | None => return res, | ||
104 | }; | ||
105 | loop { | ||
106 | if let Some(use_tree_list) = prev.use_tree_list() { | ||
107 | if use_tree_list.use_trees().any(|tree| tree.use_tree_list().is_some()) { | ||
108 | // Nested tree lists can only occur in crate style, or with no proper style being enforced in the file. | ||
109 | break ImportGranularityGuess::Crate; | ||
110 | } else { | ||
111 | // Could still be crate-style so continue looking. | ||
112 | res = ImportGranularityGuess::CrateOrModule; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | let (curr, curr_vis, curr_attrs) = match use_stmts.next() { | ||
117 | Some(it) => it, | ||
118 | None => break res, | ||
119 | }; | ||
120 | if eq_visibility(prev_vis, curr_vis.clone()) && eq_attrs(prev_attrs, curr_attrs.clone()) | ||
121 | { | ||
122 | if let Some((prev_path, curr_path)) = prev.path().zip(curr.path()) { | ||
123 | if let Some(_) = common_prefix(&prev_path, &curr_path) { | ||
124 | if prev.use_tree_list().is_none() && curr.use_tree_list().is_none() { | ||
125 | // Same prefix but no use tree lists so this has to be of item style. | ||
126 | break ImportGranularityGuess::Item; // this overwrites CrateOrModule, technically the file doesn't adhere to anything here. | ||
127 | } else { | ||
128 | // Same prefix with item tree lists, has to be module style as it | ||
129 | // can't be crate style since the trees wouldn't share a prefix then. | ||
130 | break ImportGranularityGuess::Module; | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | prev = curr; | ||
136 | prev_vis = curr_vis; | ||
137 | prev_attrs = curr_attrs; | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | |||
142 | #[derive(PartialEq, PartialOrd, Debug, Clone, Copy)] | ||
143 | enum ImportGranularityGuess { | ||
144 | Unknown, | ||
145 | Item, | ||
146 | Module, | ||
147 | Crate, | ||
148 | CrateOrModule, | ||
68 | } | 149 | } |
69 | 150 | ||
70 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. | 151 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. |
71 | pub fn insert_use<'a>(scope: &ImportScope, path: ast::Path, cfg: InsertUseConfig) { | 152 | pub fn insert_use<'a>(scope: &ImportScope, path: ast::Path, cfg: InsertUseConfig) { |
72 | let _p = profile::span("insert_use"); | 153 | let _p = profile::span("insert_use"); |
154 | let mut mb = match cfg.granularity { | ||
155 | ImportGranularity::Crate => Some(MergeBehavior::Crate), | ||
156 | ImportGranularity::Module => Some(MergeBehavior::Module), | ||
157 | ImportGranularity::Item | ImportGranularity::Preserve => None, | ||
158 | }; | ||
159 | if !cfg.enforce_granularity { | ||
160 | let file_granularity = scope.guess_granularity_from_scope(); | ||
161 | mb = match file_granularity { | ||
162 | ImportGranularityGuess::Unknown => mb, | ||
163 | ImportGranularityGuess::Item => None, | ||
164 | ImportGranularityGuess::Module => Some(MergeBehavior::Module), | ||
165 | ImportGranularityGuess::Crate => Some(MergeBehavior::Crate), | ||
166 | ImportGranularityGuess::CrateOrModule => mb.or(Some(MergeBehavior::Crate)), | ||
167 | }; | ||
168 | } | ||
169 | |||
73 | let use_item = | 170 | let use_item = |
74 | make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update(); | 171 | make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update(); |
75 | // merge into existing imports if possible | 172 | // merge into existing imports if possible |
76 | if let Some(mb) = cfg.merge { | 173 | if let Some(mb) = mb { |
77 | for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { | 174 | for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { |
78 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { | 175 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { |
79 | ted::replace(existing_use.syntax(), merged.syntax()); | 176 | ted::replace(existing_use.syntax(), merged.syntax()); |
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs index 248227d29..78a2a87b3 100644 --- a/crates/ide_db/src/helpers/insert_use/tests.rs +++ b/crates/ide_db/src/helpers/insert_use/tests.rs | |||
@@ -21,7 +21,7 @@ use crate::bar::A; | |||
21 | use self::bar::A; | 21 | use self::bar::A; |
22 | use super::bar::A; | 22 | use super::bar::A; |
23 | use external_crate2::bar::A;", | 23 | use external_crate2::bar::A;", |
24 | None, | 24 | ImportGranularity::Item, |
25 | false, | 25 | false, |
26 | false, | 26 | false, |
27 | ); | 27 | ); |
@@ -36,7 +36,7 @@ fn insert_not_group_empty() { | |||
36 | r"use external_crate2::bar::A; | 36 | r"use external_crate2::bar::A; |
37 | 37 | ||
38 | ", | 38 | ", |
39 | None, | 39 | ImportGranularity::Item, |
40 | false, | 40 | false, |
41 | false, | 41 | false, |
42 | ); | 42 | ); |
@@ -281,7 +281,7 @@ fn insert_empty_module() { | |||
281 | r"{ | 281 | r"{ |
282 | use foo::bar; | 282 | use foo::bar; |
283 | }", | 283 | }", |
284 | None, | 284 | ImportGranularity::Item, |
285 | true, | 285 | true, |
286 | true, | 286 | true, |
287 | ) | 287 | ) |
@@ -631,11 +631,121 @@ fn merge_last_fail3() { | |||
631 | ); | 631 | ); |
632 | } | 632 | } |
633 | 633 | ||
634 | #[test] | ||
635 | fn guess_empty() { | ||
636 | check_guess("", ImportGranularityGuess::Unknown); | ||
637 | } | ||
638 | |||
639 | #[test] | ||
640 | fn guess_single() { | ||
641 | check_guess(r"use foo::{baz::{qux, quux}, bar};", ImportGranularityGuess::Crate); | ||
642 | check_guess(r"use foo::bar;", ImportGranularityGuess::Unknown); | ||
643 | check_guess(r"use foo::bar::{baz, qux};", ImportGranularityGuess::CrateOrModule); | ||
644 | } | ||
645 | |||
646 | #[test] | ||
647 | fn guess_unknown() { | ||
648 | check_guess( | ||
649 | r" | ||
650 | use foo::bar::baz; | ||
651 | use oof::rab::xuq; | ||
652 | ", | ||
653 | ImportGranularityGuess::Unknown, | ||
654 | ); | ||
655 | } | ||
656 | |||
657 | #[test] | ||
658 | fn guess_item() { | ||
659 | check_guess( | ||
660 | r" | ||
661 | use foo::bar::baz; | ||
662 | use foo::bar::qux; | ||
663 | ", | ||
664 | ImportGranularityGuess::Item, | ||
665 | ); | ||
666 | } | ||
667 | |||
668 | #[test] | ||
669 | fn guess_module() { | ||
670 | check_guess( | ||
671 | r" | ||
672 | use foo::bar::baz; | ||
673 | use foo::bar::{qux, quux}; | ||
674 | ", | ||
675 | ImportGranularityGuess::Module, | ||
676 | ); | ||
677 | // this is a rather odd case, technically this file isn't following any style properly. | ||
678 | check_guess( | ||
679 | r" | ||
680 | use foo::bar::baz; | ||
681 | use foo::{baz::{qux, quux}, bar}; | ||
682 | ", | ||
683 | ImportGranularityGuess::Module, | ||
684 | ); | ||
685 | } | ||
686 | |||
687 | #[test] | ||
688 | fn guess_crate_or_module() { | ||
689 | check_guess( | ||
690 | r" | ||
691 | use foo::bar::baz; | ||
692 | use oof::bar::{qux, quux}; | ||
693 | ", | ||
694 | ImportGranularityGuess::CrateOrModule, | ||
695 | ); | ||
696 | } | ||
697 | |||
698 | #[test] | ||
699 | fn guess_crate() { | ||
700 | check_guess( | ||
701 | r" | ||
702 | use frob::bar::baz; | ||
703 | use foo::{baz::{qux, quux}, bar}; | ||
704 | ", | ||
705 | ImportGranularityGuess::Crate, | ||
706 | ); | ||
707 | } | ||
708 | |||
709 | #[test] | ||
710 | fn guess_skips_differing_vis() { | ||
711 | check_guess( | ||
712 | r" | ||
713 | use foo::bar::baz; | ||
714 | pub use foo::bar::qux; | ||
715 | ", | ||
716 | ImportGranularityGuess::Unknown, | ||
717 | ); | ||
718 | } | ||
719 | |||
720 | #[test] | ||
721 | fn guess_skips_differing_attrs() { | ||
722 | check_guess( | ||
723 | r" | ||
724 | pub use foo::bar::baz; | ||
725 | #[doc(hidden)] | ||
726 | pub use foo::bar::qux; | ||
727 | ", | ||
728 | ImportGranularityGuess::Unknown, | ||
729 | ); | ||
730 | } | ||
731 | |||
732 | #[test] | ||
733 | fn guess_grouping_matters() { | ||
734 | check_guess( | ||
735 | r" | ||
736 | use foo::bar::baz; | ||
737 | use oof::bar::baz; | ||
738 | use foo::bar::qux; | ||
739 | ", | ||
740 | ImportGranularityGuess::Unknown, | ||
741 | ); | ||
742 | } | ||
743 | |||
634 | fn check( | 744 | fn check( |
635 | path: &str, | 745 | path: &str, |
636 | ra_fixture_before: &str, | 746 | ra_fixture_before: &str, |
637 | ra_fixture_after: &str, | 747 | ra_fixture_after: &str, |
638 | mb: Option<MergeBehavior>, | 748 | granularity: ImportGranularity, |
639 | module: bool, | 749 | module: bool, |
640 | group: bool, | 750 | group: bool, |
641 | ) { | 751 | ) { |
@@ -651,21 +761,30 @@ fn check( | |||
651 | .find_map(ast::Path::cast) | 761 | .find_map(ast::Path::cast) |
652 | .unwrap(); | 762 | .unwrap(); |
653 | 763 | ||
654 | insert_use(&file, path, InsertUseConfig { merge: mb, prefix_kind: PrefixKind::Plain, group }); | 764 | insert_use( |
765 | &file, | ||
766 | path, | ||
767 | InsertUseConfig { | ||
768 | granularity, | ||
769 | enforce_granularity: true, | ||
770 | prefix_kind: PrefixKind::Plain, | ||
771 | group, | ||
772 | }, | ||
773 | ); | ||
655 | let result = file.as_syntax_node().to_string(); | 774 | let result = file.as_syntax_node().to_string(); |
656 | assert_eq_text!(ra_fixture_after, &result); | 775 | assert_eq_text!(ra_fixture_after, &result); |
657 | } | 776 | } |
658 | 777 | ||
659 | fn check_crate(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 778 | fn check_crate(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
660 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Crate), false, true) | 779 | check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Crate, false, true) |
661 | } | 780 | } |
662 | 781 | ||
663 | fn check_module(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 782 | fn check_module(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
664 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Module), false, true) | 783 | check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Module, false, true) |
665 | } | 784 | } |
666 | 785 | ||
667 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 786 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
668 | check(path, ra_fixture_before, ra_fixture_after, None, false, true) | 787 | check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Item, false, true) |
669 | } | 788 | } |
670 | 789 | ||
671 | fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) { | 790 | fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) { |
@@ -686,3 +805,9 @@ fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior | |||
686 | let result = try_merge_imports(&use0, &use1, mb); | 805 | let result = try_merge_imports(&use0, &use1, mb); |
687 | assert_eq!(result.map(|u| u.to_string()), None); | 806 | assert_eq!(result.map(|u| u.to_string()), None); |
688 | } | 807 | } |
808 | |||
809 | fn check_guess(ra_fixture: &str, expected: ImportGranularityGuess) { | ||
810 | let syntax = ast::SourceFile::parse(ra_fixture).tree().syntax().clone(); | ||
811 | let file = super::ImportScope::from(syntax).unwrap(); | ||
812 | assert_eq!(file.guess_granularity_from_scope(), expected); | ||
813 | } | ||
diff --git a/crates/ide_db/src/helpers/merge_imports.rs b/crates/ide_db/src/helpers/merge_imports.rs index 8fb40e837..0dbabb44f 100644 --- a/crates/ide_db/src/helpers/merge_imports.rs +++ b/crates/ide_db/src/helpers/merge_imports.rs | |||
@@ -181,7 +181,7 @@ fn recursive_merge( | |||
181 | } | 181 | } |
182 | 182 | ||
183 | /// Traverses both paths until they differ, returning the common prefix of both. | 183 | /// Traverses both paths until they differ, returning the common prefix of both. |
184 | fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Path)> { | 184 | pub fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Path)> { |
185 | let mut res = None; | 185 | let mut res = None; |
186 | let mut lhs_curr = lhs.first_qualifier_or_self(); | 186 | let mut lhs_curr = lhs.first_qualifier_or_self(); |
187 | let mut rhs_curr = rhs.first_qualifier_or_self(); | 187 | let mut rhs_curr = rhs.first_qualifier_or_self(); |
@@ -289,23 +289,26 @@ fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering { | |||
289 | a.as_ref().map(ast::NameRef::text).cmp(&b.as_ref().map(ast::NameRef::text)) | 289 | a.as_ref().map(ast::NameRef::text).cmp(&b.as_ref().map(ast::NameRef::text)) |
290 | } | 290 | } |
291 | 291 | ||
292 | fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -> bool { | 292 | pub fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -> bool { |
293 | match (vis0, vis1) { | 293 | match (vis0, vis1) { |
294 | (None, None) => true, | 294 | (None, None) => true, |
295 | // FIXME: Don't use the string representation to check for equality | 295 | (Some(vis0), Some(vis1)) => vis0.is_eq_to(&vis1), |
296 | // spaces inside of the node would break this comparison | ||
297 | (Some(vis0), Some(vis1)) => vis0.to_string() == vis1.to_string(), | ||
298 | _ => false, | 296 | _ => false, |
299 | } | 297 | } |
300 | } | 298 | } |
301 | 299 | ||
302 | fn eq_attrs( | 300 | pub fn eq_attrs( |
303 | attrs0: impl Iterator<Item = ast::Attr>, | 301 | attrs0: impl Iterator<Item = ast::Attr>, |
304 | attrs1: impl Iterator<Item = ast::Attr>, | 302 | attrs1: impl Iterator<Item = ast::Attr>, |
305 | ) -> bool { | 303 | ) -> bool { |
306 | let attrs0 = attrs0.map(|attr| attr.to_string()); | 304 | // FIXME order of attributes should not matter |
307 | let attrs1 = attrs1.map(|attr| attr.to_string()); | 305 | let attrs0 = attrs0 |
308 | attrs0.eq(attrs1) | 306 | .flat_map(|attr| attr.syntax().descendants_with_tokens()) |
307 | .flat_map(|it| it.into_token()); | ||
308 | let attrs1 = attrs1 | ||
309 | .flat_map(|attr| attr.syntax().descendants_with_tokens()) | ||
310 | .flat_map(|it| it.into_token()); | ||
311 | stdx::iter_eq_by(attrs0, attrs1, |tok, tok2| tok.text() == tok2.text()) | ||
309 | } | 312 | } |
310 | 313 | ||
311 | fn path_is_self(path: &ast::Path) -> bool { | 314 | fn path_is_self(path: &ast::Path) -> bool { |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 339014fd3..b700d025f 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -12,8 +12,7 @@ use std::{ffi::OsString, iter, path::PathBuf}; | |||
12 | use flycheck::FlycheckConfig; | 12 | use flycheck::FlycheckConfig; |
13 | use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig}; | 13 | use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig}; |
14 | use ide_db::helpers::{ | 14 | use ide_db::helpers::{ |
15 | insert_use::{InsertUseConfig, PrefixKind}, | 15 | insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, |
16 | merge_imports::MergeBehavior, | ||
17 | SnippetCap, | 16 | SnippetCap, |
18 | }; | 17 | }; |
19 | use lsp_types::{ClientCapabilities, MarkupKind}; | 18 | use lsp_types::{ClientCapabilities, MarkupKind}; |
@@ -35,15 +34,18 @@ use crate::{ | |||
35 | // be specified directly in `package.json`. | 34 | // be specified directly in `package.json`. |
36 | config_data! { | 35 | config_data! { |
37 | struct ConfigData { | 36 | struct ConfigData { |
38 | /// The strategy to use when inserting new imports or merging imports. | 37 | /// How imports should be grouped into use statements. |
38 | assist_importGranularity | | ||
39 | assist_importMergeBehavior | | 39 | assist_importMergeBehavior | |
40 | assist_importMergeBehaviour: MergeBehaviorDef = "\"crate\"", | 40 | assist_importMergeBehaviour: ImportGranularityDef = "\"crate\"", |
41 | /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. | ||
42 | assist_importEnforceGranularity: bool = "false", | ||
41 | /// The path structure for newly inserted paths to use. | 43 | /// The path structure for newly inserted paths to use. |
42 | assist_importPrefix: ImportPrefixDef = "\"plain\"", | 44 | assist_importPrefix: ImportPrefixDef = "\"plain\"", |
43 | /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. | 45 | /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. |
44 | assist_importGroup: bool = "true", | 46 | assist_importGroup: bool = "true", |
45 | /// Show function name and docs in parameter hints. | 47 | /// Show function name and docs in parameter hints. |
46 | callInfo_full: bool = "true", | 48 | callInfo_full: bool = "true", |
47 | 49 | ||
48 | /// Automatically refresh project info via `cargo metadata` on | 50 | /// Automatically refresh project info via `cargo metadata` on |
49 | /// `Cargo.toml` changes. | 51 | /// `Cargo.toml` changes. |
@@ -624,11 +626,13 @@ impl Config { | |||
624 | } | 626 | } |
625 | fn insert_use_config(&self) -> InsertUseConfig { | 627 | fn insert_use_config(&self) -> InsertUseConfig { |
626 | InsertUseConfig { | 628 | InsertUseConfig { |
627 | merge: match self.data.assist_importMergeBehavior { | 629 | granularity: match self.data.assist_importGranularity { |
628 | MergeBehaviorDef::None => None, | 630 | ImportGranularityDef::Preserve => ImportGranularity::Preserve, |
629 | MergeBehaviorDef::Crate => Some(MergeBehavior::Crate), | 631 | ImportGranularityDef::Item => ImportGranularity::Item, |
630 | MergeBehaviorDef::Module => Some(MergeBehavior::Module), | 632 | ImportGranularityDef::Crate => ImportGranularity::Crate, |
633 | ImportGranularityDef::Module => ImportGranularity::Module, | ||
631 | }, | 634 | }, |
635 | enforce_granularity: self.data.assist_importEnforceGranularity, | ||
632 | prefix_kind: match self.data.assist_importPrefix { | 636 | prefix_kind: match self.data.assist_importPrefix { |
633 | ImportPrefixDef::Plain => PrefixKind::Plain, | 637 | ImportPrefixDef::Plain => PrefixKind::Plain, |
634 | ImportPrefixDef::ByCrate => PrefixKind::ByCrate, | 638 | ImportPrefixDef::ByCrate => PrefixKind::ByCrate, |
@@ -748,8 +752,10 @@ enum ManifestOrProjectJson { | |||
748 | 752 | ||
749 | #[derive(Deserialize, Debug, Clone)] | 753 | #[derive(Deserialize, Debug, Clone)] |
750 | #[serde(rename_all = "snake_case")] | 754 | #[serde(rename_all = "snake_case")] |
751 | enum MergeBehaviorDef { | 755 | enum ImportGranularityDef { |
752 | None, | 756 | Preserve, |
757 | #[serde(alias = "none")] | ||
758 | Item, | ||
753 | #[serde(alias = "full")] | 759 | #[serde(alias = "full")] |
754 | Crate, | 760 | Crate, |
755 | #[serde(alias = "last")] | 761 | #[serde(alias = "last")] |
@@ -782,7 +788,7 @@ macro_rules! _config_data { | |||
782 | (struct $name:ident { | 788 | (struct $name:ident { |
783 | $( | 789 | $( |
784 | $(#[doc=$doc:literal])* | 790 | $(#[doc=$doc:literal])* |
785 | $field:ident $(| $alias:ident)?: $ty:ty = $default:expr, | 791 | $field:ident $(| $alias:ident)*: $ty:ty = $default:expr, |
786 | )* | 792 | )* |
787 | }) => { | 793 | }) => { |
788 | #[allow(non_snake_case)] | 794 | #[allow(non_snake_case)] |
@@ -794,7 +800,7 @@ macro_rules! _config_data { | |||
794 | $field: get_field( | 800 | $field: get_field( |
795 | &mut json, | 801 | &mut json, |
796 | stringify!($field), | 802 | stringify!($field), |
797 | None$(.or(Some(stringify!($alias))))?, | 803 | None$(.or(Some(stringify!($alias))))*, |
798 | $default, | 804 | $default, |
799 | ), | 805 | ), |
800 | )*} | 806 | )*} |
@@ -931,6 +937,16 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json | |||
931 | "Merge imports from the same module into a single `use` statement." | 937 | "Merge imports from the same module into a single `use` statement." |
932 | ], | 938 | ], |
933 | }, | 939 | }, |
940 | "ImportGranularityDef" => set! { | ||
941 | "type": "string", | ||
942 | "enum": ["preserve", "crate", "module", "item"], | ||
943 | "enumDescriptions": [ | ||
944 | "Do not change the granularity of any imports and preserve the original structure written by the developer.", | ||
945 | "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.", | ||
946 | "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.", | ||
947 | "Flatten imports so that each has its own use statement." | ||
948 | ], | ||
949 | }, | ||
934 | "ImportPrefixDef" => set! { | 950 | "ImportPrefixDef" => set! { |
935 | "type": "string", | 951 | "type": "string", |
936 | "enum": [ | 952 | "enum": [ |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 51041d7a0..aa12fd94b 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -117,6 +117,16 @@ pub(crate) fn handle_view_hir( | |||
117 | Ok(res) | 117 | Ok(res) |
118 | } | 118 | } |
119 | 119 | ||
120 | pub(crate) fn handle_view_item_tree( | ||
121 | snap: GlobalStateSnapshot, | ||
122 | params: lsp_ext::ViewItemTreeParams, | ||
123 | ) -> Result<String> { | ||
124 | let _p = profile::span("handle_view_item_tree"); | ||
125 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | ||
126 | let res = snap.analysis.view_item_tree(file_id)?; | ||
127 | Ok(res) | ||
128 | } | ||
129 | |||
120 | pub(crate) fn handle_view_crate_graph(snap: GlobalStateSnapshot, (): ()) -> Result<String> { | 130 | pub(crate) fn handle_view_crate_graph(snap: GlobalStateSnapshot, (): ()) -> Result<String> { |
121 | let _p = profile::span("handle_view_crate_graph"); | 131 | let _p = profile::span("handle_view_crate_graph"); |
122 | let dot = snap.analysis.view_crate_graph()??; | 132 | let dot = snap.analysis.view_crate_graph()??; |
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index ba2790acb..781073fe5 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs | |||
@@ -13,7 +13,10 @@ | |||
13 | use std::{convert::TryFrom, sync::Arc}; | 13 | use std::{convert::TryFrom, sync::Arc}; |
14 | 14 | ||
15 | use ide::{Change, CompletionConfig, FilePosition, TextSize}; | 15 | use ide::{Change, CompletionConfig, FilePosition, TextSize}; |
16 | use ide_db::helpers::{insert_use::InsertUseConfig, merge_imports::MergeBehavior, SnippetCap}; | 16 | use ide_db::helpers::{ |
17 | insert_use::{ImportGranularity, InsertUseConfig}, | ||
18 | SnippetCap, | ||
19 | }; | ||
17 | use test_utils::project_root; | 20 | use test_utils::project_root; |
18 | use vfs::{AbsPathBuf, VfsPath}; | 21 | use vfs::{AbsPathBuf, VfsPath}; |
19 | 22 | ||
@@ -133,8 +136,9 @@ fn integrated_completion_benchmark() { | |||
133 | add_call_argument_snippets: true, | 136 | add_call_argument_snippets: true, |
134 | snippet_cap: SnippetCap::new(true), | 137 | snippet_cap: SnippetCap::new(true), |
135 | insert_use: InsertUseConfig { | 138 | insert_use: InsertUseConfig { |
136 | merge: Some(MergeBehavior::Crate), | 139 | granularity: ImportGranularity::Crate, |
137 | prefix_kind: hir::PrefixKind::ByCrate, | 140 | prefix_kind: hir::PrefixKind::ByCrate, |
141 | enforce_granularity: true, | ||
138 | group: true, | 142 | group: true, |
139 | }, | 143 | }, |
140 | }; | 144 | }; |
@@ -166,8 +170,9 @@ fn integrated_completion_benchmark() { | |||
166 | add_call_argument_snippets: true, | 170 | add_call_argument_snippets: true, |
167 | snippet_cap: SnippetCap::new(true), | 171 | snippet_cap: SnippetCap::new(true), |
168 | insert_use: InsertUseConfig { | 172 | insert_use: InsertUseConfig { |
169 | merge: Some(MergeBehavior::Crate), | 173 | granularity: ImportGranularity::Crate, |
170 | prefix_kind: hir::PrefixKind::ByCrate, | 174 | prefix_kind: hir::PrefixKind::ByCrate, |
175 | enforce_granularity: true, | ||
171 | group: true, | 176 | group: true, |
172 | }, | 177 | }, |
173 | }; | 178 | }; |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 34b53a7a8..905048793 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -70,6 +70,20 @@ impl Request for ViewCrateGraph { | |||
70 | const METHOD: &'static str = "rust-analyzer/viewCrateGraph"; | 70 | const METHOD: &'static str = "rust-analyzer/viewCrateGraph"; |
71 | } | 71 | } |
72 | 72 | ||
73 | #[derive(Deserialize, Serialize, Debug)] | ||
74 | #[serde(rename_all = "camelCase")] | ||
75 | pub struct ViewItemTreeParams { | ||
76 | pub text_document: TextDocumentIdentifier, | ||
77 | } | ||
78 | |||
79 | pub enum ViewItemTree {} | ||
80 | |||
81 | impl Request for ViewItemTree { | ||
82 | type Params = ViewItemTreeParams; | ||
83 | type Result = String; | ||
84 | const METHOD: &'static str = "rust-analyzer/viewItemTree"; | ||
85 | } | ||
86 | |||
73 | pub enum ExpandMacro {} | 87 | pub enum ExpandMacro {} |
74 | 88 | ||
75 | impl Request for ExpandMacro { | 89 | impl Request for ExpandMacro { |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 4e0791611..f837b89dd 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -514,6 +514,7 @@ impl GlobalState { | |||
514 | .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree) | 514 | .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree) |
515 | .on::<lsp_ext::ViewHir>(handlers::handle_view_hir) | 515 | .on::<lsp_ext::ViewHir>(handlers::handle_view_hir) |
516 | .on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph) | 516 | .on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph) |
517 | .on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree) | ||
517 | .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro) | 518 | .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro) |
518 | .on::<lsp_ext::ParentModule>(handlers::handle_parent_module) | 519 | .on::<lsp_ext::ParentModule>(handlers::handle_parent_module) |
519 | .on::<lsp_ext::Runnables>(handlers::handle_runnables) | 520 | .on::<lsp_ext::Runnables>(handlers::handle_runnables) |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 9dec46c78..0a3a56773 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -270,9 +270,12 @@ pub(crate) fn completion_item( | |||
270 | set_score(&mut lsp_item_with_ref, relevance); | 270 | set_score(&mut lsp_item_with_ref, relevance); |
271 | lsp_item_with_ref.label = | 271 | lsp_item_with_ref.label = |
272 | format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label); | 272 | format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label); |
273 | if let Some(lsp_types::CompletionTextEdit::Edit(it)) = &mut lsp_item_with_ref.text_edit | 273 | if let Some(it) = &mut lsp_item_with_ref.text_edit { |
274 | { | 274 | let new_text = match it { |
275 | it.new_text = format!("&{}{}", mutability.as_keyword_for_ref(), it.new_text); | 275 | lsp_types::CompletionTextEdit::Edit(it) => &mut it.new_text, |
276 | lsp_types::CompletionTextEdit::InsertAndReplace(it) => &mut it.new_text, | ||
277 | }; | ||
278 | *new_text = format!("&{}{}", mutability.as_keyword_for_ref(), new_text); | ||
276 | } | 279 | } |
277 | vec![lsp_item_with_ref, lsp_item] | 280 | vec![lsp_item_with_ref, lsp_item] |
278 | } | 281 | } |
@@ -1145,7 +1148,7 @@ mod tests { | |||
1145 | 1148 | ||
1146 | use ide::Analysis; | 1149 | use ide::Analysis; |
1147 | use ide_db::helpers::{ | 1150 | use ide_db::helpers::{ |
1148 | insert_use::{InsertUseConfig, PrefixKind}, | 1151 | insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, |
1149 | SnippetCap, | 1152 | SnippetCap, |
1150 | }; | 1153 | }; |
1151 | 1154 | ||
@@ -1177,8 +1180,9 @@ mod tests { | |||
1177 | add_call_argument_snippets: true, | 1180 | add_call_argument_snippets: true, |
1178 | snippet_cap: SnippetCap::new(true), | 1181 | snippet_cap: SnippetCap::new(true), |
1179 | insert_use: InsertUseConfig { | 1182 | insert_use: InsertUseConfig { |
1180 | merge: None, | 1183 | granularity: ImportGranularity::Item, |
1181 | prefix_kind: PrefixKind::Plain, | 1184 | prefix_kind: PrefixKind::Plain, |
1185 | enforce_granularity: true, | ||
1182 | group: true, | 1186 | group: true, |
1183 | }, | 1187 | }, |
1184 | }, | 1188 | }, |
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 340fcacfa..18d5fadb9 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -140,6 +140,34 @@ impl JodChild { | |||
140 | } | 140 | } |
141 | } | 141 | } |
142 | 142 | ||
143 | // feature: iter_order_by | ||
144 | // Iterator::eq_by | ||
145 | pub fn iter_eq_by<I, I2, F>(this: I2, other: I, mut eq: F) -> bool | ||
146 | where | ||
147 | I: IntoIterator, | ||
148 | I2: IntoIterator, | ||
149 | F: FnMut(I2::Item, I::Item) -> bool, | ||
150 | { | ||
151 | let mut other = other.into_iter(); | ||
152 | let mut this = this.into_iter(); | ||
153 | |||
154 | loop { | ||
155 | let x = match this.next() { | ||
156 | None => return other.next().is_none(), | ||
157 | Some(val) => val, | ||
158 | }; | ||
159 | |||
160 | let y = match other.next() { | ||
161 | None => return false, | ||
162 | Some(val) => val, | ||
163 | }; | ||
164 | |||
165 | if !eq(x, y) { | ||
166 | return false; | ||
167 | } | ||
168 | } | ||
169 | } | ||
170 | |||
143 | #[cfg(test)] | 171 | #[cfg(test)] |
144 | mod tests { | 172 | mod tests { |
145 | use super::*; | 173 | use super::*; |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index bef49238f..df8f98b5b 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -608,6 +608,29 @@ impl ast::Visibility { | |||
608 | None => VisibilityKind::Pub, | 608 | None => VisibilityKind::Pub, |
609 | } | 609 | } |
610 | } | 610 | } |
611 | |||
612 | pub fn is_eq_to(&self, other: &Self) -> bool { | ||
613 | match (self.kind(), other.kind()) { | ||
614 | (VisibilityKind::In(this), VisibilityKind::In(other)) => { | ||
615 | stdx::iter_eq_by(this.segments(), other.segments(), |lhs, rhs| { | ||
616 | lhs.kind().zip(rhs.kind()).map_or(false, |it| match it { | ||
617 | (PathSegmentKind::CrateKw, PathSegmentKind::CrateKw) | ||
618 | | (PathSegmentKind::SelfKw, PathSegmentKind::SelfKw) | ||
619 | | (PathSegmentKind::SuperKw, PathSegmentKind::SuperKw) => true, | ||
620 | (PathSegmentKind::Name(lhs), PathSegmentKind::Name(rhs)) => { | ||
621 | lhs.text() == rhs.text() | ||
622 | } | ||
623 | _ => false, | ||
624 | }) | ||
625 | }) | ||
626 | } | ||
627 | (VisibilityKind::PubSelf, VisibilityKind::PubSelf) | ||
628 | | (VisibilityKind::PubSuper, VisibilityKind::PubSuper) | ||
629 | | (VisibilityKind::PubCrate, VisibilityKind::PubCrate) | ||
630 | | (VisibilityKind::Pub, VisibilityKind::Pub) => true, | ||
631 | _ => false, | ||
632 | } | ||
633 | } | ||
611 | } | 634 | } |
612 | 635 | ||
613 | impl ast::LifetimeParam { | 636 | impl ast::LifetimeParam { |
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 3c4eacfeb..fbe2ce1c9 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -1,5 +1,5 @@ | |||
1 | <!--- | 1 | <!--- |
2 | lsp_ext.rs hash: 10a8988e6893e6b2 | 2 | lsp_ext.rs hash: 49f253e4a9307d4f |
3 | 3 | ||
4 | If you need to change the above hash to make the test pass, please check if you | 4 | If you need to change the above hash to make the test pass, please check if you |
5 | need to adjust this doc as well and ping this issue: | 5 | need to adjust this doc as well and ping this issue: |
@@ -464,7 +464,7 @@ Clients are discouraged from but are allowed to use the `health` status to decid | |||
464 | **Request:** | 464 | **Request:** |
465 | 465 | ||
466 | ```typescript | 466 | ```typescript |
467 | interface SyntaxTeeParams { | 467 | interface SyntaxTreeParams { |
468 | textDocument: TextDocumentIdentifier, | 468 | textDocument: TextDocumentIdentifier, |
469 | range?: Range, | 469 | range?: Range, |
470 | } | 470 | } |
@@ -486,6 +486,22 @@ Primarily for debugging, but very useful for all people working on rust-analyzer | |||
486 | Returns a textual representation of the HIR of the function containing the cursor. | 486 | Returns a textual representation of the HIR of the function containing the cursor. |
487 | For debugging or when working on rust-analyzer itself. | 487 | For debugging or when working on rust-analyzer itself. |
488 | 488 | ||
489 | ## View ItemTree | ||
490 | |||
491 | **Method:** `rust-analyzer/viewItemTree` | ||
492 | |||
493 | **Request:** | ||
494 | |||
495 | ```typescript | ||
496 | interface ViewItemTreeParams { | ||
497 | textDocument: TextDocumentIdentifier, | ||
498 | } | ||
499 | ``` | ||
500 | |||
501 | **Response:** `string` | ||
502 | |||
503 | Returns a textual representation of the `ItemTree` of the currently open file, for debugging. | ||
504 | |||
489 | ## View Crate Graph | 505 | ## View Crate Graph |
490 | 506 | ||
491 | **Method:** `rust-analyzer/viewCrateGraph` | 507 | **Method:** `rust-analyzer/viewCrateGraph` |
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index b32411887..c02bab7cc 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc | |||
@@ -1,7 +1,12 @@ | |||
1 | [[rust-analyzer.assist.importMergeBehavior]]rust-analyzer.assist.importMergeBehavior (default: `"crate"`):: | 1 | [[rust-analyzer.assist.importGranularity]]rust-analyzer.assist.importGranularity (default: `"crate"`):: |
2 | + | 2 | + |
3 | -- | 3 | -- |
4 | The strategy to use when inserting new imports or merging imports. | 4 | How imports should be grouped into use statements. |
5 | -- | ||
6 | [[rust-analyzer.assist.importEnforceGranularity]]rust-analyzer.assist.importEnforceGranularity (default: `false`):: | ||
7 | + | ||
8 | -- | ||
9 | Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. | ||
5 | -- | 10 | -- |
6 | [[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`):: | 11 | [[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`):: |
7 | + | 12 | + |
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index f96c09a79..1d8a1930a 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -276,7 +276,7 @@ nvim_lsp.rust_analyzer.setup({ | |||
276 | settings = { | 276 | settings = { |
277 | ["rust-analyzer"] = { | 277 | ["rust-analyzer"] = { |
278 | assist = { | 278 | assist = { |
279 | importMergeBehavior = "last", | 279 | importGranularity = "module", |
280 | importPrefix = "by_self", | 280 | importPrefix = "by_self", |
281 | }, | 281 | }, |
282 | cargo = { | 282 | cargo = { |
diff --git a/editors/code/package.json b/editors/code/package.json index 99223c4e8..17d9281ff 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -110,6 +110,11 @@ | |||
110 | "category": "Rust Analyzer" | 110 | "category": "Rust Analyzer" |
111 | }, | 111 | }, |
112 | { | 112 | { |
113 | "command": "rust-analyzer.viewItemTree", | ||
114 | "title": "Debug ItemTree", | ||
115 | "category": "Rust Analyzer" | ||
116 | }, | ||
117 | { | ||
113 | "command": "rust-analyzer.viewCrateGraph", | 118 | "command": "rust-analyzer.viewCrateGraph", |
114 | "title": "View Crate Graph", | 119 | "title": "View Crate Graph", |
115 | "category": "Rust Analyzer" | 120 | "category": "Rust Analyzer" |
@@ -385,21 +390,28 @@ | |||
385 | "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`" | 390 | "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`" |
386 | }, | 391 | }, |
387 | "$generated-start": false, | 392 | "$generated-start": false, |
388 | "rust-analyzer.assist.importMergeBehavior": { | 393 | "rust-analyzer.assist.importGranularity": { |
389 | "markdownDescription": "The strategy to use when inserting new imports or merging imports.", | 394 | "markdownDescription": "How imports should be grouped into use statements.", |
390 | "default": "crate", | 395 | "default": "crate", |
391 | "type": "string", | 396 | "type": "string", |
392 | "enum": [ | 397 | "enum": [ |
393 | "none", | 398 | "preserve", |
394 | "crate", | 399 | "crate", |
395 | "module" | 400 | "module", |
401 | "item" | ||
396 | ], | 402 | ], |
397 | "enumDescriptions": [ | 403 | "enumDescriptions": [ |
398 | "Do not merge imports at all.", | 404 | "Do not change the granularity of any imports and preserve the original structure written by the developer.", |
399 | "Merge imports from the same crate into a single `use` statement.", | 405 | "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.", |
400 | "Merge imports from the same module into a single `use` statement." | 406 | "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.", |
407 | "Flatten imports so that each has its own use statement." | ||
401 | ] | 408 | ] |
402 | }, | 409 | }, |
410 | "rust-analyzer.assist.importEnforceGranularity": { | ||
411 | "markdownDescription": "Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.", | ||
412 | "default": false, | ||
413 | "type": "boolean" | ||
414 | }, | ||
403 | "rust-analyzer.assist.importPrefix": { | 415 | "rust-analyzer.assist.importPrefix": { |
404 | "markdownDescription": "The path structure for newly inserted paths to use.", | 416 | "markdownDescription": "The path structure for newly inserted paths to use.", |
405 | "default": "plain", | 417 | "default": "plain", |
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 8ab259af2..0fdb9fe05 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts | |||
@@ -429,6 +429,56 @@ export function viewHir(ctx: Ctx): Cmd { | |||
429 | }; | 429 | }; |
430 | } | 430 | } |
431 | 431 | ||
432 | export function viewItemTree(ctx: Ctx): Cmd { | ||
433 | const tdcp = new class implements vscode.TextDocumentContentProvider { | ||
434 | readonly uri = vscode.Uri.parse('rust-analyzer://viewItemTree/itemtree.rs'); | ||
435 | readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); | ||
436 | constructor() { | ||
437 | vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions); | ||
438 | vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions); | ||
439 | } | ||
440 | |||
441 | private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { | ||
442 | if (isRustDocument(event.document)) { | ||
443 | // We need to order this after language server updates, but there's no API for that. | ||
444 | // Hence, good old sleep(). | ||
445 | void sleep(10).then(() => this.eventEmitter.fire(this.uri)); | ||
446 | } | ||
447 | } | ||
448 | private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) { | ||
449 | if (editor && isRustEditor(editor)) { | ||
450 | this.eventEmitter.fire(this.uri); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | provideTextDocumentContent(_uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> { | ||
455 | const rustEditor = ctx.activeRustEditor; | ||
456 | const client = ctx.client; | ||
457 | if (!rustEditor || !client) return ''; | ||
458 | |||
459 | const params = { | ||
460 | textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(rustEditor.document), | ||
461 | }; | ||
462 | return client.sendRequest(ra.viewItemTree, params, ct); | ||
463 | } | ||
464 | |||
465 | get onDidChange(): vscode.Event<vscode.Uri> { | ||
466 | return this.eventEmitter.event; | ||
467 | } | ||
468 | }; | ||
469 | |||
470 | ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp)); | ||
471 | |||
472 | return async () => { | ||
473 | const document = await vscode.workspace.openTextDocument(tdcp.uri); | ||
474 | tdcp.eventEmitter.fire(tdcp.uri); | ||
475 | void await vscode.window.showTextDocument(document, { | ||
476 | viewColumn: vscode.ViewColumn.Two, | ||
477 | preserveFocus: true | ||
478 | }); | ||
479 | }; | ||
480 | } | ||
481 | |||
432 | export function viewCrateGraph(ctx: Ctx): Cmd { | 482 | export function viewCrateGraph(ctx: Ctx): Cmd { |
433 | return async () => { | 483 | return async () => { |
434 | const panel = vscode.window.createWebviewPanel("rust-analyzer.crate-graph", "rust-analyzer crate graph", vscode.ViewColumn.Two); | 484 | const panel = vscode.window.createWebviewPanel("rust-analyzer.crate-graph", "rust-analyzer crate graph", vscode.ViewColumn.Two); |
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index aa745a65c..6d5c2ea72 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -27,6 +27,12 @@ export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>("ru | |||
27 | 27 | ||
28 | export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>("rust-analyzer/viewHir"); | 28 | export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>("rust-analyzer/viewHir"); |
29 | 29 | ||
30 | export interface ViewItemTreeParams { | ||
31 | textDocument: lc.TextDocumentIdentifier; | ||
32 | } | ||
33 | |||
34 | export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>("rust-analyzer/viewItemTree"); | ||
35 | |||
30 | export const viewCrateGraph = new lc.RequestType0<string, void>("rust-analyzer/viewCrateGraph"); | 36 | export const viewCrateGraph = new lc.RequestType0<string, void>("rust-analyzer/viewCrateGraph"); |
31 | 37 | ||
32 | export interface ExpandMacroParams { | 38 | export interface ExpandMacroParams { |
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 516322d03..92c797d47 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -106,6 +106,7 @@ async function tryActivate(context: vscode.ExtensionContext) { | |||
106 | ctx.registerCommand('parentModule', commands.parentModule); | 106 | ctx.registerCommand('parentModule', commands.parentModule); |
107 | ctx.registerCommand('syntaxTree', commands.syntaxTree); | 107 | ctx.registerCommand('syntaxTree', commands.syntaxTree); |
108 | ctx.registerCommand('viewHir', commands.viewHir); | 108 | ctx.registerCommand('viewHir', commands.viewHir); |
109 | ctx.registerCommand('viewItemTree', commands.viewItemTree); | ||
109 | ctx.registerCommand('viewCrateGraph', commands.viewCrateGraph); | 110 | ctx.registerCommand('viewCrateGraph', commands.viewCrateGraph); |
110 | ctx.registerCommand('expandMacro', commands.expandMacro); | 111 | ctx.registerCommand('expandMacro', commands.expandMacro); |
111 | ctx.registerCommand('run', commands.run); | 112 | ctx.registerCommand('run', commands.run); |