1 //! Defines hir-level representation of structs, enums and unions
3 use std::{num::NonZeroU32, sync::Arc};
11 use la_arena::{Arena, ArenaMap};
12 use syntax::ast::{self, HasName, HasVisibility};
13 use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
16 body::{CfgExpander, LowerCtx},
17 builtin_type::{BuiltinInt, BuiltinUint},
20 item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem, RawVisibilityId},
25 visibility::RawVisibility,
26 EnumId, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, VariantId,
30 /// Note that we use `StructData` for unions as well!
31 #[derive(Debug, Clone, PartialEq, Eq)]
32 pub struct StructData {
34 pub variant_data: Arc<VariantData>,
35 pub repr: Option<ReprData>,
36 pub visibility: RawVisibility,
39 #[derive(Debug, Clone, PartialEq, Eq)]
42 pub variants: Arena<EnumVariantData>,
43 pub repr: Option<ReprData>,
44 pub visibility: RawVisibility,
47 #[derive(Debug, Clone, PartialEq, Eq)]
48 pub struct EnumVariantData {
50 pub variant_data: Arc<VariantData>,
53 #[derive(Debug, Clone, PartialEq, Eq)]
54 pub enum VariantData {
55 Record(Arena<FieldData>),
56 Tuple(Arena<FieldData>),
60 /// A single field of an enum variant or struct
61 #[derive(Debug, Clone, PartialEq, Eq)]
62 pub struct FieldData {
64 pub type_ref: Interned<TypeRef>,
65 pub visibility: RawVisibility,
68 #[derive(Copy, Debug, Clone, PartialEq, Eq)]
71 BuiltinInt { builtin: Either<BuiltinInt, BuiltinUint>, is_c: bool },
76 #[derive(Copy, Debug, Clone, PartialEq, Eq)]
80 pub align: Option<NonZeroU32>,
88 ) -> Option<ReprData> {
89 item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
92 fn parse_repr_tt(tt: &Subtree) -> Option<ReprData> {
94 Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
98 let mut data = ReprData { kind: ReprKind::Default, packed: false, align: None };
100 let mut tts = tt.token_trees.iter().peekable();
101 while let Some(tt) = tts.next() {
102 if let TokenTree::Leaf(Leaf::Ident(ident)) = tt {
106 if let Some(TokenTree::Subtree(_)) = tts.peek() {
111 if let Some(TokenTree::Subtree(tt)) = tts.peek() {
113 if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
114 if let Ok(align) = lit.text.parse() {
115 data.align = Some(align);
121 if let ReprKind::BuiltinInt { is_c, .. } = &mut data.kind {
124 data.kind = ReprKind::C;
127 "transparent" => data.kind = ReprKind::Transparent,
129 let is_c = matches!(data.kind, ReprKind::C);
130 if let Some(builtin) = BuiltinInt::from_suffix(repr)
132 .or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
134 data.kind = ReprKind::BuiltinInt { builtin, is_c };
145 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
146 let loc = id.lookup(db);
147 let krate = loc.container.krate;
148 let item_tree = loc.id.item_tree(db);
149 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
150 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
152 let strukt = &item_tree[loc.id.value];
153 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None);
154 Arc::new(StructData {
155 name: strukt.name.clone(),
156 variant_data: Arc::new(variant_data),
158 visibility: item_tree[strukt.visibility].clone(),
162 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
163 let loc = id.lookup(db);
164 let krate = loc.container.krate;
165 let item_tree = loc.id.item_tree(db);
166 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
167 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
169 let union = &item_tree[loc.id.value];
170 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None);
172 Arc::new(StructData {
173 name: union.name.clone(),
174 variant_data: Arc::new(variant_data),
176 visibility: item_tree[union.visibility].clone(),
182 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
183 let loc = e.lookup(db);
184 let krate = loc.container.krate;
185 let item_tree = loc.id.item_tree(db);
186 let cfg_options = db.crate_graph()[krate].cfg_options.clone();
187 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
189 let enum_ = &item_tree[loc.id.value];
190 let mut variants = Arena::new();
191 for tree_id in enum_.variants.clone() {
192 if item_tree.attrs(db, krate, tree_id.into()).is_cfg_enabled(&cfg_options) {
193 let var = &item_tree[tree_id];
194 let var_data = lower_fields(
200 Some(enum_.visibility),
203 variants.alloc(EnumVariantData {
204 name: var.name.clone(),
205 variant_data: Arc::new(var_data),
211 name: enum_.name.clone(),
214 visibility: item_tree[enum_.visibility].clone(),
218 pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
219 let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?;
224 impl HasChildSource<LocalEnumVariantId> for EnumId {
225 type Value = ast::Variant;
228 db: &dyn DefDatabase,
229 ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> {
230 let src = self.lookup(db).source(db);
231 let mut trace = Trace::new_for_map();
232 lower_enum(db, &mut trace, &src, self.lookup(db).container);
233 src.with_value(trace.into_map())
238 db: &dyn DefDatabase,
239 trace: &mut Trace<EnumVariantData, ast::Variant>,
240 ast: &InFile<ast::Enum>,
243 let expander = CfgExpander::new(db, ast.file_id, module_id.krate);
248 .flat_map(|it| it.variants())
249 .filter(|var| expander.is_cfg_enabled(db, var));
250 for var in variants {
254 name: var.name().map_or_else(Name::missing, |it| it.as_name()),
255 variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)),
262 fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self {
263 let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate);
264 let mut trace = Trace::new_for_arena();
265 match lower_struct(db, &mut expander, &mut trace, &flavor) {
266 StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
267 StructKind::Record => VariantData::Record(trace.into_arena()),
268 StructKind::Unit => VariantData::Unit,
272 pub fn fields(&self) -> &Arena<FieldData> {
273 const EMPTY: &Arena<FieldData> = &Arena::new();
275 VariantData::Record(fields) | VariantData::Tuple(fields) => fields,
280 pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
281 self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
284 pub fn kind(&self) -> StructKind {
286 VariantData::Record(_) => StructKind::Record,
287 VariantData::Tuple(_) => StructKind::Tuple,
288 VariantData::Unit => StructKind::Unit,
293 impl HasChildSource<LocalFieldId> for VariantId {
294 type Value = Either<ast::TupleField, ast::RecordField>;
296 fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
297 let (src, module_id) = match self {
298 VariantId::EnumVariantId(it) => {
299 // I don't really like the fact that we call into parent source
300 // here, this might add to more queries then necessary.
301 let src = it.parent.child_source(db);
302 (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container)
304 VariantId::StructId(it) => {
305 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container)
307 VariantId::UnionId(it) => (
308 it.lookup(db).source(db).map(|it| {
309 it.record_field_list()
310 .map(ast::StructKind::Record)
311 .unwrap_or(ast::StructKind::Unit)
313 it.lookup(db).container,
316 let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
317 let mut trace = Trace::new_for_map();
318 lower_struct(db, &mut expander, &mut trace, &src);
319 src.with_value(trace.into_map())
323 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
324 pub enum StructKind {
331 db: &dyn DefDatabase,
332 expander: &mut CfgExpander,
333 trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
334 ast: &InFile<ast::StructKind>,
336 let ctx = LowerCtx::new(db, ast.file_id);
339 ast::StructKind::Tuple(fl) => {
340 for (i, fd) in fl.fields().enumerate() {
341 if !expander.is_cfg_enabled(db, &fd) {
346 || Either::Left(fd.clone()),
348 name: Name::new_tuple_field(i),
349 type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
350 visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
356 ast::StructKind::Record(fl) => {
357 for fd in fl.fields() {
358 if !expander.is_cfg_enabled(db, &fd) {
363 || Either::Right(fd.clone()),
365 name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
366 type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
367 visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
373 ast::StructKind::Unit => StructKind::Unit,
378 db: &dyn DefDatabase,
380 item_tree: &ItemTree,
381 cfg_options: &CfgOptions,
383 override_visibility: Option<RawVisibilityId>,
386 Fields::Record(flds) => {
387 let mut arena = Arena::new();
388 for field_id in flds.clone() {
389 if item_tree.attrs(db, krate, field_id.into()).is_cfg_enabled(cfg_options) {
390 arena.alloc(lower_field(item_tree, &item_tree[field_id], override_visibility));
393 VariantData::Record(arena)
395 Fields::Tuple(flds) => {
396 let mut arena = Arena::new();
397 for field_id in flds.clone() {
398 if item_tree.attrs(db, krate, field_id.into()).is_cfg_enabled(cfg_options) {
399 arena.alloc(lower_field(item_tree, &item_tree[field_id], override_visibility));
402 VariantData::Tuple(arena)
404 Fields::Unit => VariantData::Unit,
409 item_tree: &ItemTree,
411 override_visibility: Option<RawVisibilityId>,
414 name: field.name.clone(),
415 type_ref: field.type_ref.clone(),
416 visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),