diff options
-rw-r--r-- | crates/hir_def/src/body.rs | 18 | ||||
-rw-r--r-- | crates/hir_def/src/type_ref.rs | 38 | ||||
-rw-r--r-- | crates/hir_ty/src/lower.rs | 71 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/macros.rs | 26 |
4 files changed, 94 insertions, 59 deletions
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 8a9b936ea..131f424cc 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -37,13 +37,15 @@ use crate::{ | |||
37 | 37 | ||
38 | /// A subset of Expander that only deals with cfg attributes. We only need it to | 38 | /// A subset of Expander that only deals with cfg attributes. We only need it to |
39 | /// avoid cyclic queries in crate def map during enum processing. | 39 | /// avoid cyclic queries in crate def map during enum processing. |
40 | #[derive(Debug)] | ||
40 | pub(crate) struct CfgExpander { | 41 | pub(crate) struct CfgExpander { |
41 | cfg_options: CfgOptions, | 42 | cfg_options: CfgOptions, |
42 | hygiene: Hygiene, | 43 | hygiene: Hygiene, |
43 | krate: CrateId, | 44 | krate: CrateId, |
44 | } | 45 | } |
45 | 46 | ||
46 | pub(crate) struct Expander { | 47 | #[derive(Debug)] |
48 | pub struct Expander { | ||
47 | cfg_expander: CfgExpander, | 49 | cfg_expander: CfgExpander, |
48 | def_map: Arc<DefMap>, | 50 | def_map: Arc<DefMap>, |
49 | current_file_id: HirFileId, | 51 | current_file_id: HirFileId, |
@@ -80,11 +82,7 @@ impl CfgExpander { | |||
80 | } | 82 | } |
81 | 83 | ||
82 | impl Expander { | 84 | impl Expander { |
83 | pub(crate) fn new( | 85 | pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { |
84 | db: &dyn DefDatabase, | ||
85 | current_file_id: HirFileId, | ||
86 | module: ModuleId, | ||
87 | ) -> Expander { | ||
88 | let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); | 86 | let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); |
89 | let def_map = module.def_map(db); | 87 | let def_map = module.def_map(db); |
90 | let ast_id_map = db.ast_id_map(current_file_id); | 88 | let ast_id_map = db.ast_id_map(current_file_id); |
@@ -98,7 +96,7 @@ impl Expander { | |||
98 | } | 96 | } |
99 | } | 97 | } |
100 | 98 | ||
101 | pub(crate) fn enter_expand<T: ast::AstNode>( | 99 | pub fn enter_expand<T: ast::AstNode>( |
102 | &mut self, | 100 | &mut self, |
103 | db: &dyn DefDatabase, | 101 | db: &dyn DefDatabase, |
104 | macro_call: ast::MacroCall, | 102 | macro_call: ast::MacroCall, |
@@ -170,7 +168,7 @@ impl Expander { | |||
170 | Ok(ExpandResult { value: Some((mark, node)), err }) | 168 | Ok(ExpandResult { value: Some((mark, node)), err }) |
171 | } | 169 | } |
172 | 170 | ||
173 | pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { | 171 | pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { |
174 | self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); | 172 | self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); |
175 | self.current_file_id = mark.file_id; | 173 | self.current_file_id = mark.file_id; |
176 | self.ast_id_map = mem::take(&mut mark.ast_id_map); | 174 | self.ast_id_map = mem::take(&mut mark.ast_id_map); |
@@ -190,7 +188,7 @@ impl Expander { | |||
190 | &self.cfg_expander.cfg_options | 188 | &self.cfg_expander.cfg_options |
191 | } | 189 | } |
192 | 190 | ||
193 | pub(crate) fn current_file_id(&self) -> HirFileId { | 191 | pub fn current_file_id(&self) -> HirFileId { |
194 | self.current_file_id | 192 | self.current_file_id |
195 | } | 193 | } |
196 | 194 | ||
@@ -210,7 +208,7 @@ impl Expander { | |||
210 | } | 208 | } |
211 | 209 | ||
212 | #[derive(Debug)] | 210 | #[derive(Debug)] |
213 | pub(crate) struct Mark { | 211 | pub struct Mark { |
214 | file_id: HirFileId, | 212 | file_id: HirFileId, |
215 | ast_id_map: Arc<AstIdMap>, | 213 | ast_id_map: Arc<AstIdMap>, |
216 | bomb: DropBomb, | 214 | bomb: DropBomb, |
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index e18712d24..ea29da5da 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs | |||
@@ -1,15 +1,10 @@ | |||
1 | //! HIR for references to types. Paths in these are not yet resolved. They can | 1 | //! HIR for references to types. Paths in these are not yet resolved. They can |
2 | //! be directly created from an ast::TypeRef, without further queries. | 2 | //! be directly created from an ast::TypeRef, without further queries. |
3 | 3 | ||
4 | use hir_expand::{name::Name, AstId, ExpandResult, InFile}; | 4 | use hir_expand::{name::Name, AstId, InFile}; |
5 | use syntax::ast; | 5 | use syntax::ast; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{body::LowerCtx, path::Path}; |
8 | body::{Expander, LowerCtx}, | ||
9 | db::DefDatabase, | ||
10 | path::Path, | ||
11 | ModuleId, | ||
12 | }; | ||
13 | 8 | ||
14 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | 9 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |
15 | pub enum Mutability { | 10 | pub enum Mutability { |
@@ -124,7 +119,7 @@ pub enum TypeBound { | |||
124 | 119 | ||
125 | impl TypeRef { | 120 | impl TypeRef { |
126 | /// Converts an `ast::TypeRef` to a `hir::TypeRef`. | 121 | /// Converts an `ast::TypeRef` to a `hir::TypeRef`. |
127 | pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { | 122 | pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { |
128 | match node { | 123 | match node { |
129 | ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), | 124 | ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), |
130 | ast::Type::TupleType(inner) => { | 125 | ast::Type::TupleType(inner) => { |
@@ -303,30 +298,3 @@ impl TypeBound { | |||
303 | } | 298 | } |
304 | } | 299 | } |
305 | } | 300 | } |
306 | |||
307 | pub fn expand_macro_type( | ||
308 | db: &dyn DefDatabase, | ||
309 | module_id: ModuleId, | ||
310 | macro_type: &TypeRef, | ||
311 | ) -> Option<TypeRef> { | ||
312 | let macro_call = match macro_type { | ||
313 | TypeRef::Macro(macro_call) => macro_call, | ||
314 | _ => panic!("expected TypeRef::Macro"), | ||
315 | }; | ||
316 | |||
317 | let file_id = macro_call.file_id; | ||
318 | let macro_call = macro_call.to_node(db.upcast()); | ||
319 | |||
320 | let mut expander = Expander::new(db, file_id, module_id); | ||
321 | let (file_id, expanded) = match expander.enter_expand::<ast::Type>(db, macro_call.clone()) { | ||
322 | Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { | ||
323 | let file_id = expander.current_file_id(); | ||
324 | expander.exit(db, mark); | ||
325 | (file_id, expanded) | ||
326 | } | ||
327 | _ => return None, | ||
328 | }; | ||
329 | |||
330 | let ctx = LowerCtx::new(db, file_id); | ||
331 | return Some(TypeRef::from_ast(&ctx, expanded)); | ||
332 | } | ||
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index e01b7aa91..a883334af 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -5,25 +5,28 @@ | |||
5 | //! - Building the type for an item: This happens through the `type_for_def` query. | 5 | //! - Building the type for an item: This happens through the `type_for_def` query. |
6 | //! | 6 | //! |
7 | //! This usually involves resolving names, collecting generic arguments etc. | 7 | //! This usually involves resolving names, collecting generic arguments etc. |
8 | use std::cell::{Cell, RefCell}; | ||
8 | use std::{iter, sync::Arc}; | 9 | use std::{iter, sync::Arc}; |
9 | 10 | ||
10 | use base_db::CrateId; | 11 | use base_db::CrateId; |
11 | use chalk_ir::{cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety}; | 12 | use chalk_ir::{cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety}; |
12 | use hir_def::{ | 13 | use hir_def::{ |
13 | adt::StructKind, | 14 | adt::StructKind, |
15 | body::{Expander, LowerCtx}, | ||
14 | builtin_type::BuiltinType, | 16 | builtin_type::BuiltinType, |
15 | generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, | 17 | generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, |
16 | path::{GenericArg, Path, PathSegment, PathSegments}, | 18 | path::{GenericArg, Path, PathSegment, PathSegments}, |
17 | resolver::{HasResolver, Resolver, TypeNs}, | 19 | resolver::{HasResolver, Resolver, TypeNs}, |
18 | type_ref::{expand_macro_type, TraitRef as HirTraitRef, TypeBound, TypeRef}, | 20 | type_ref::{TraitRef as HirTraitRef, TypeBound, TypeRef}, |
19 | AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, | 21 | AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, |
20 | GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, | 22 | GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, |
21 | TypeAliasId, TypeParamId, UnionId, VariantId, | 23 | TypeAliasId, TypeParamId, UnionId, VariantId, |
22 | }; | 24 | }; |
23 | use hir_expand::name::Name; | 25 | use hir_expand::{name::Name, ExpandResult}; |
24 | use la_arena::ArenaMap; | 26 | use la_arena::ArenaMap; |
25 | use smallvec::SmallVec; | 27 | use smallvec::SmallVec; |
26 | use stdx::impl_from; | 28 | use stdx::impl_from; |
29 | use syntax::ast; | ||
27 | 30 | ||
28 | use crate::{ | 31 | use crate::{ |
29 | db::HirDatabase, | 32 | db::HirDatabase, |
@@ -50,7 +53,7 @@ pub struct TyLoweringContext<'a> { | |||
50 | /// possible currently, so this should be fine for now. | 53 | /// possible currently, so this should be fine for now. |
51 | pub type_param_mode: TypeParamLoweringMode, | 54 | pub type_param_mode: TypeParamLoweringMode, |
52 | pub impl_trait_mode: ImplTraitLoweringMode, | 55 | pub impl_trait_mode: ImplTraitLoweringMode, |
53 | impl_trait_counter: std::cell::Cell<u16>, | 56 | impl_trait_counter: Cell<u16>, |
54 | /// When turning `impl Trait` into opaque types, we have to collect the | 57 | /// When turning `impl Trait` into opaque types, we have to collect the |
55 | /// bounds at the same time to get the IDs correct (without becoming too | 58 | /// bounds at the same time to get the IDs correct (without becoming too |
56 | /// complicated). I don't like using interior mutability (as for the | 59 | /// complicated). I don't like using interior mutability (as for the |
@@ -59,16 +62,17 @@ pub struct TyLoweringContext<'a> { | |||
59 | /// we're grouping the mutable data (the counter and this field) together | 62 | /// we're grouping the mutable data (the counter and this field) together |
60 | /// with the immutable context (the references to the DB and resolver). | 63 | /// with the immutable context (the references to the DB and resolver). |
61 | /// Splitting this up would be a possible fix. | 64 | /// Splitting this up would be a possible fix. |
62 | opaque_type_data: std::cell::RefCell<Vec<ReturnTypeImplTrait>>, | 65 | opaque_type_data: RefCell<Vec<ReturnTypeImplTrait>>, |
66 | expander: RefCell<Option<Expander>>, | ||
63 | } | 67 | } |
64 | 68 | ||
65 | impl<'a> TyLoweringContext<'a> { | 69 | impl<'a> TyLoweringContext<'a> { |
66 | pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self { | 70 | pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self { |
67 | let impl_trait_counter = std::cell::Cell::new(0); | 71 | let impl_trait_counter = Cell::new(0); |
68 | let impl_trait_mode = ImplTraitLoweringMode::Disallowed; | 72 | let impl_trait_mode = ImplTraitLoweringMode::Disallowed; |
69 | let type_param_mode = TypeParamLoweringMode::Placeholder; | 73 | let type_param_mode = TypeParamLoweringMode::Placeholder; |
70 | let in_binders = DebruijnIndex::INNERMOST; | 74 | let in_binders = DebruijnIndex::INNERMOST; |
71 | let opaque_type_data = std::cell::RefCell::new(Vec::new()); | 75 | let opaque_type_data = RefCell::new(Vec::new()); |
72 | Self { | 76 | Self { |
73 | db, | 77 | db, |
74 | resolver, | 78 | resolver, |
@@ -77,6 +81,7 @@ impl<'a> TyLoweringContext<'a> { | |||
77 | impl_trait_counter, | 81 | impl_trait_counter, |
78 | type_param_mode, | 82 | type_param_mode, |
79 | opaque_type_data, | 83 | opaque_type_data, |
84 | expander: RefCell::new(None), | ||
80 | } | 85 | } |
81 | } | 86 | } |
82 | 87 | ||
@@ -86,15 +91,18 @@ impl<'a> TyLoweringContext<'a> { | |||
86 | f: impl FnOnce(&TyLoweringContext) -> T, | 91 | f: impl FnOnce(&TyLoweringContext) -> T, |
87 | ) -> T { | 92 | ) -> T { |
88 | let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new()); | 93 | let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new()); |
94 | let expander = self.expander.replace(None); | ||
89 | let new_ctx = Self { | 95 | let new_ctx = Self { |
90 | in_binders: debruijn, | 96 | in_binders: debruijn, |
91 | impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), | 97 | impl_trait_counter: Cell::new(self.impl_trait_counter.get()), |
92 | opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec), | 98 | opaque_type_data: RefCell::new(opaque_ty_data_vec), |
99 | expander: RefCell::new(expander), | ||
93 | ..*self | 100 | ..*self |
94 | }; | 101 | }; |
95 | let result = f(&new_ctx); | 102 | let result = f(&new_ctx); |
96 | self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); | 103 | self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); |
97 | self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner()); | 104 | self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner()); |
105 | self.expander.replace(new_ctx.expander.into_inner()); | ||
98 | result | 106 | result |
99 | } | 107 | } |
100 | 108 | ||
@@ -287,15 +295,50 @@ impl<'a> TyLoweringContext<'a> { | |||
287 | } | 295 | } |
288 | } | 296 | } |
289 | } | 297 | } |
290 | mt @ TypeRef::Macro(_) => { | 298 | TypeRef::Macro(macro_call) => { |
291 | if let Some(module_id) = self.resolver.module() { | 299 | let (expander, recursion_start) = match self.expander.borrow_mut() { |
292 | match expand_macro_type(self.db.upcast(), module_id, mt) { | 300 | expander if expander.is_some() => (Some(expander), false), |
293 | Some(type_ref) => self.lower_ty(&type_ref), | 301 | mut expander => { |
294 | None => TyKind::Error.intern(&Interner), | 302 | if let Some(module_id) = self.resolver.module() { |
303 | *expander = Some(Expander::new( | ||
304 | self.db.upcast(), | ||
305 | macro_call.file_id, | ||
306 | module_id, | ||
307 | )); | ||
308 | (Some(expander), true) | ||
309 | } else { | ||
310 | (None, false) | ||
311 | } | ||
312 | } | ||
313 | }; | ||
314 | let ty = if let Some(mut expander) = expander { | ||
315 | let expander_mut = expander.as_mut().unwrap(); | ||
316 | let macro_call = macro_call.to_node(self.db.upcast()); | ||
317 | match expander_mut.enter_expand::<ast::Type>(self.db.upcast(), macro_call) { | ||
318 | Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { | ||
319 | let ctx = | ||
320 | LowerCtx::new(self.db.upcast(), expander_mut.current_file_id()); | ||
321 | let type_ref = TypeRef::from_ast(&ctx, expanded); | ||
322 | |||
323 | drop(expander); | ||
324 | let ty = self.lower_ty(&type_ref); | ||
325 | |||
326 | self.expander | ||
327 | .borrow_mut() | ||
328 | .as_mut() | ||
329 | .unwrap() | ||
330 | .exit(self.db.upcast(), mark); | ||
331 | Some(ty) | ||
332 | } | ||
333 | _ => None, | ||
295 | } | 334 | } |
296 | } else { | 335 | } else { |
297 | TyKind::Error.intern(&Interner) | 336 | None |
337 | }; | ||
338 | if recursion_start { | ||
339 | *self.expander.borrow_mut() = None; | ||
298 | } | 340 | } |
341 | ty.unwrap_or_else(|| TyKind::Error.intern(&Interner)) | ||
299 | } | 342 | } |
300 | TypeRef::Error => TyKind::Error.intern(&Interner), | 343 | TypeRef::Error => TyKind::Error.intern(&Interner), |
301 | }; | 344 | }; |
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index cbe05a5c1..8de1e229f 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs | |||
@@ -1243,3 +1243,29 @@ fn macros_in_type_generics() { | |||
1243 | "#]], | 1243 | "#]], |
1244 | ); | 1244 | ); |
1245 | } | 1245 | } |
1246 | |||
1247 | #[test] | ||
1248 | fn infinitely_recursive_macro_type() { | ||
1249 | check_infer( | ||
1250 | r#" | ||
1251 | struct Bar<T>(T); | ||
1252 | |||
1253 | macro_rules! Foo { | ||
1254 | () => { Foo!() } | ||
1255 | } | ||
1256 | |||
1257 | type A = Foo!(); | ||
1258 | type B = Bar<Foo!()>; | ||
1259 | |||
1260 | fn main() { | ||
1261 | let a: A; | ||
1262 | let b: B; | ||
1263 | } | ||
1264 | "#, | ||
1265 | expect![[r#" | ||
1266 | 112..143 '{ ...: B; }': () | ||
1267 | 122..123 'a': {unknown} | ||
1268 | 136..137 'b': Bar<{unknown}> | ||
1269 | "#]], | ||
1270 | ); | ||
1271 | } | ||