1 //! Defines hir-level representation of structs, enums and unions
2
3 use base_db::CrateId;
4 use bitflags::bitflags;
5 use cfg::CfgOptions;
6 use either::Either;
7
8 use hir_expand::{
9 name::{AsName, Name},
10 HirFileId, InFile,
11 };
12 use intern::Interned;
13 use la_arena::{Arena, ArenaMap};
14 use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
15 use syntax::ast::{self, HasName, HasVisibility};
16 use triomphe::Arc;
17
18 use crate::{
19 builtin_type::{BuiltinInt, BuiltinUint},
20 db::DefDatabase,
21 expander::CfgExpander,
22 item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
23 lang_item::LangItem,
24 lower::LowerCtx,
25 nameres::diagnostics::DefDiagnostic,
26 src::HasChildSource,
27 src::HasSource,
28 trace::Trace,
29 tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree},
30 type_ref::TypeRef,
31 visibility::RawVisibility,
32 EnumId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StructId, UnionId,
33 VariantId,
34 };
35
36 /// Note that we use `StructData` for unions as well!
37 #[derive(Debug, Clone, PartialEq, Eq)]
38 pub struct StructData {
39 pub name: Name,
40 pub variant_data: Arc<VariantData>,
41 pub repr: Option<ReprOptions>,
42 pub visibility: RawVisibility,
43 pub flags: StructFlags,
44 }
45
46 bitflags! {
47 #[derive(Debug, Clone, PartialEq, Eq)]
48 pub struct StructFlags: u8 {
49 const NO_FLAGS = 0;
50 /// Indicates whether the struct is `PhantomData`.
51 const IS_PHANTOM_DATA = 1 << 2;
52 /// Indicates whether the struct has a `#[fundamental]` attribute.
53 const IS_FUNDAMENTAL = 1 << 3;
54 // FIXME: should this be a flag?
55 /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute.
56 const IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL = 1 << 4;
57 /// Indicates whether this struct is `Box`.
58 const IS_BOX = 1 << 5;
59 /// Indicates whether this struct is `ManuallyDrop`.
60 const IS_MANUALLY_DROP = 1 << 6;
61 /// Indicates whether this struct is `UnsafeCell`.
62 const IS_UNSAFE_CELL = 1 << 7;
63 }
64 }
65
66 #[derive(Debug, Clone, PartialEq, Eq)]
67 pub struct EnumData {
68 pub name: Name,
69 pub variants: Arena<EnumVariantData>,
70 pub repr: Option<ReprOptions>,
71 pub visibility: RawVisibility,
72 pub rustc_has_incoherent_inherent_impls: bool,
73 }
74
75 #[derive(Debug, Clone, PartialEq, Eq)]
76 pub struct EnumVariantData {
77 pub name: Name,
78 pub variant_data: Arc<VariantData>,
79 }
80
81 #[derive(Debug, Clone, PartialEq, Eq)]
82 pub enum VariantData {
83 Record(Arena<FieldData>),
84 Tuple(Arena<FieldData>),
85 Unit,
86 }
87
88 /// A single field of an enum variant or struct
89 #[derive(Debug, Clone, PartialEq, Eq)]
90 pub struct FieldData {
91 pub name: Name,
92 pub type_ref: Interned<TypeRef>,
93 pub visibility: RawVisibility,
94 }
95
repr_from_value( db: &dyn DefDatabase, krate: CrateId, item_tree: &ItemTree, of: AttrOwner, ) -> Option<ReprOptions>96 fn repr_from_value(
97 db: &dyn DefDatabase,
98 krate: CrateId,
99 item_tree: &ItemTree,
100 of: AttrOwner,
101 ) -> Option<ReprOptions> {
102 item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
103 }
104
parse_repr_tt(tt: &Subtree) -> Option<ReprOptions>105 fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
106 match tt.delimiter {
107 Delimiter { kind: DelimiterKind::Parenthesis, .. } => {}
108 _ => return None,
109 }
110
111 let mut flags = ReprFlags::empty();
112 let mut int = None;
113 let mut max_align: Option<Align> = None;
114 let mut min_pack: Option<Align> = None;
115
116 let mut tts = tt.token_trees.iter().peekable();
117 while let Some(tt) = tts.next() {
118 if let TokenTree::Leaf(Leaf::Ident(ident)) = tt {
119 flags.insert(match &*ident.text {
120 "packed" => {
121 let pack = if let Some(TokenTree::Subtree(tt)) = tts.peek() {
122 tts.next();
123 if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
124 lit.text.parse().unwrap_or_default()
125 } else {
126 0
127 }
128 } else {
129 0
130 };
131 let pack = Align::from_bytes(pack).unwrap();
132 min_pack =
133 Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { pack });
134 ReprFlags::empty()
135 }
136 "align" => {
137 if let Some(TokenTree::Subtree(tt)) = tts.peek() {
138 tts.next();
139 if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
140 if let Ok(align) = lit.text.parse() {
141 let align = Align::from_bytes(align).ok();
142 max_align = max_align.max(align);
143 }
144 }
145 }
146 ReprFlags::empty()
147 }
148 "C" => ReprFlags::IS_C,
149 "transparent" => ReprFlags::IS_TRANSPARENT,
150 repr => {
151 if let Some(builtin) = BuiltinInt::from_suffix(repr)
152 .map(Either::Left)
153 .or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
154 {
155 int = Some(match builtin {
156 Either::Left(bi) => match bi {
157 BuiltinInt::Isize => IntegerType::Pointer(true),
158 BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true),
159 BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true),
160 BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true),
161 BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true),
162 BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true),
163 },
164 Either::Right(bu) => match bu {
165 BuiltinUint::Usize => IntegerType::Pointer(false),
166 BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false),
167 BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false),
168 BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false),
169 BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false),
170 BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false),
171 },
172 });
173 }
174 ReprFlags::empty()
175 }
176 })
177 }
178 }
179
180 Some(ReprOptions { int, align: max_align, pack: min_pack, flags, field_shuffle_seed: 0 })
181 }
182
183 impl StructData {
struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData>184 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
185 db.struct_data_with_diagnostics(id).0
186 }
187
struct_data_with_diagnostics_query( db: &dyn DefDatabase, id: StructId, ) -> (Arc<StructData>, Arc<[DefDiagnostic]>)188 pub(crate) fn struct_data_with_diagnostics_query(
189 db: &dyn DefDatabase,
190 id: StructId,
191 ) -> (Arc<StructData>, Arc<[DefDiagnostic]>) {
192 let loc = id.lookup(db);
193 let krate = loc.container.krate;
194 let item_tree = loc.id.item_tree(db);
195 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
196 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
197
198 let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
199
200 let mut flags = StructFlags::NO_FLAGS;
201 if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
202 flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
203 }
204 if attrs.by_key("fundamental").exists() {
205 flags |= StructFlags::IS_FUNDAMENTAL;
206 }
207 if let Some(lang) = attrs.lang_item() {
208 match lang {
209 LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA,
210 LangItem::OwnedBox => flags |= StructFlags::IS_BOX,
211 LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP,
212 LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL,
213 _ => (),
214 }
215 }
216
217 let strukt = &item_tree[loc.id.value];
218 let (variant_data, diagnostics) = lower_fields(
219 db,
220 krate,
221 loc.id.file_id(),
222 loc.container.local_id,
223 &item_tree,
224 &cfg_options,
225 &strukt.fields,
226 None,
227 );
228 (
229 Arc::new(StructData {
230 name: strukt.name.clone(),
231 variant_data: Arc::new(variant_data),
232 repr,
233 visibility: item_tree[strukt.visibility].clone(),
234 flags,
235 }),
236 diagnostics.into(),
237 )
238 }
239
union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData>240 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
241 db.union_data_with_diagnostics(id).0
242 }
243
union_data_with_diagnostics_query( db: &dyn DefDatabase, id: UnionId, ) -> (Arc<StructData>, Arc<[DefDiagnostic]>)244 pub(crate) fn union_data_with_diagnostics_query(
245 db: &dyn DefDatabase,
246 id: UnionId,
247 ) -> (Arc<StructData>, Arc<[DefDiagnostic]>) {
248 let loc = id.lookup(db);
249 let krate = loc.container.krate;
250 let item_tree = loc.id.item_tree(db);
251 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
252 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
253
254 let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
255 let mut flags = StructFlags::NO_FLAGS;
256 if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
257 flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
258 }
259 if attrs.by_key("fundamental").exists() {
260 flags |= StructFlags::IS_FUNDAMENTAL;
261 }
262
263 let union = &item_tree[loc.id.value];
264 let (variant_data, diagnostics) = lower_fields(
265 db,
266 krate,
267 loc.id.file_id(),
268 loc.container.local_id,
269 &item_tree,
270 &cfg_options,
271 &union.fields,
272 None,
273 );
274 (
275 Arc::new(StructData {
276 name: union.name.clone(),
277 variant_data: Arc::new(variant_data),
278 repr,
279 visibility: item_tree[union.visibility].clone(),
280 flags,
281 }),
282 diagnostics.into(),
283 )
284 }
285 }
286
287 impl EnumData {
enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData>288 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
289 db.enum_data_with_diagnostics(e).0
290 }
291
enum_data_with_diagnostics_query( db: &dyn DefDatabase, e: EnumId, ) -> (Arc<EnumData>, Arc<[DefDiagnostic]>)292 pub(crate) fn enum_data_with_diagnostics_query(
293 db: &dyn DefDatabase,
294 e: EnumId,
295 ) -> (Arc<EnumData>, Arc<[DefDiagnostic]>) {
296 let loc = e.lookup(db);
297 let krate = loc.container.krate;
298 let item_tree = loc.id.item_tree(db);
299 let cfg_options = db.crate_graph()[krate].cfg_options.clone();
300 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
301 let rustc_has_incoherent_inherent_impls = item_tree
302 .attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
303 .by_key("rustc_has_incoherent_inherent_impls")
304 .exists();
305
306 let enum_ = &item_tree[loc.id.value];
307 let mut variants = Arena::new();
308 let mut diagnostics = Vec::new();
309 for tree_id in enum_.variants.clone() {
310 let attrs = item_tree.attrs(db, krate, tree_id.into());
311 let var = &item_tree[tree_id];
312 if attrs.is_cfg_enabled(&cfg_options) {
313 let (var_data, field_diagnostics) = lower_fields(
314 db,
315 krate,
316 loc.id.file_id(),
317 loc.container.local_id,
318 &item_tree,
319 &cfg_options,
320 &var.fields,
321 Some(enum_.visibility),
322 );
323 diagnostics.extend(field_diagnostics);
324
325 variants.alloc(EnumVariantData {
326 name: var.name.clone(),
327 variant_data: Arc::new(var_data),
328 });
329 } else {
330 diagnostics.push(DefDiagnostic::unconfigured_code(
331 loc.container.local_id,
332 InFile::new(loc.id.file_id(), var.ast_id.upcast()),
333 attrs.cfg().unwrap(),
334 cfg_options.clone(),
335 ))
336 }
337 }
338
339 (
340 Arc::new(EnumData {
341 name: enum_.name.clone(),
342 variants,
343 repr,
344 visibility: item_tree[enum_.visibility].clone(),
345 rustc_has_incoherent_inherent_impls,
346 }),
347 diagnostics.into(),
348 )
349 }
350
variant(&self, name: &Name) -> Option<LocalEnumVariantId>351 pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
352 let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?;
353 Some(id)
354 }
355
variant_body_type(&self) -> IntegerType356 pub fn variant_body_type(&self) -> IntegerType {
357 match self.repr {
358 Some(ReprOptions { int: Some(builtin), .. }) => builtin,
359 _ => IntegerType::Pointer(true),
360 }
361 }
362 }
363
364 impl HasChildSource<LocalEnumVariantId> for EnumId {
365 type Value = ast::Variant;
child_source( &self, db: &dyn DefDatabase, ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>>366 fn child_source(
367 &self,
368 db: &dyn DefDatabase,
369 ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> {
370 let src = self.lookup(db).source(db);
371 let mut trace = Trace::new_for_map();
372 lower_enum(db, &mut trace, &src, self.lookup(db).container);
373 src.with_value(trace.into_map())
374 }
375 }
376
lower_enum( db: &dyn DefDatabase, trace: &mut Trace<EnumVariantData, ast::Variant>, ast: &InFile<ast::Enum>, module_id: ModuleId, )377 fn lower_enum(
378 db: &dyn DefDatabase,
379 trace: &mut Trace<EnumVariantData, ast::Variant>,
380 ast: &InFile<ast::Enum>,
381 module_id: ModuleId,
382 ) {
383 let expander = CfgExpander::new(db, ast.file_id, module_id.krate);
384 let variants = ast
385 .value
386 .variant_list()
387 .into_iter()
388 .flat_map(|it| it.variants())
389 .filter(|var| expander.is_cfg_enabled(db, var));
390 for var in variants {
391 trace.alloc(
392 || var.clone(),
393 || EnumVariantData {
394 name: var.name().map_or_else(Name::missing, |it| it.as_name()),
395 variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)),
396 },
397 );
398 }
399 }
400
401 impl VariantData {
new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self402 fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self {
403 let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate);
404 let mut trace = Trace::new_for_arena();
405 match lower_struct(db, &mut expander, &mut trace, &flavor) {
406 StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
407 StructKind::Record => VariantData::Record(trace.into_arena()),
408 StructKind::Unit => VariantData::Unit,
409 }
410 }
411
fields(&self) -> &Arena<FieldData>412 pub fn fields(&self) -> &Arena<FieldData> {
413 const EMPTY: &Arena<FieldData> = &Arena::new();
414 match &self {
415 VariantData::Record(fields) | VariantData::Tuple(fields) => fields,
416 _ => EMPTY,
417 }
418 }
419
field(&self, name: &Name) -> Option<LocalFieldId>420 pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
421 self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
422 }
423
kind(&self) -> StructKind424 pub fn kind(&self) -> StructKind {
425 match self {
426 VariantData::Record(_) => StructKind::Record,
427 VariantData::Tuple(_) => StructKind::Tuple,
428 VariantData::Unit => StructKind::Unit,
429 }
430 }
431 }
432
433 impl HasChildSource<LocalFieldId> for VariantId {
434 type Value = Either<ast::TupleField, ast::RecordField>;
435
child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>>436 fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
437 let (src, module_id) = match self {
438 VariantId::EnumVariantId(it) => {
439 // I don't really like the fact that we call into parent source
440 // here, this might add to more queries then necessary.
441 let src = it.parent.child_source(db);
442 (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container)
443 }
444 VariantId::StructId(it) => {
445 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container)
446 }
447 VariantId::UnionId(it) => (
448 it.lookup(db).source(db).map(|it| {
449 it.record_field_list()
450 .map(ast::StructKind::Record)
451 .unwrap_or(ast::StructKind::Unit)
452 }),
453 it.lookup(db).container,
454 ),
455 };
456 let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
457 let mut trace = Trace::new_for_map();
458 lower_struct(db, &mut expander, &mut trace, &src);
459 src.with_value(trace.into_map())
460 }
461 }
462
463 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
464 pub enum StructKind {
465 Tuple,
466 Record,
467 Unit,
468 }
469
lower_struct( db: &dyn DefDatabase, expander: &mut CfgExpander, trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>, ast: &InFile<ast::StructKind>, ) -> StructKind470 fn lower_struct(
471 db: &dyn DefDatabase,
472 expander: &mut CfgExpander,
473 trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
474 ast: &InFile<ast::StructKind>,
475 ) -> StructKind {
476 let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id);
477
478 match &ast.value {
479 ast::StructKind::Tuple(fl) => {
480 for (i, fd) in fl.fields().enumerate() {
481 if !expander.is_cfg_enabled(db, &fd) {
482 continue;
483 }
484
485 trace.alloc(
486 || Either::Left(fd.clone()),
487 || FieldData {
488 name: Name::new_tuple_field(i),
489 type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
490 visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
491 },
492 );
493 }
494 StructKind::Tuple
495 }
496 ast::StructKind::Record(fl) => {
497 for fd in fl.fields() {
498 if !expander.is_cfg_enabled(db, &fd) {
499 continue;
500 }
501
502 trace.alloc(
503 || Either::Right(fd.clone()),
504 || FieldData {
505 name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
506 type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
507 visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
508 },
509 );
510 }
511 StructKind::Record
512 }
513 ast::StructKind::Unit => StructKind::Unit,
514 }
515 }
516
lower_fields( db: &dyn DefDatabase, krate: CrateId, current_file_id: HirFileId, container: LocalModuleId, item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields, override_visibility: Option<RawVisibilityId>, ) -> (VariantData, Vec<DefDiagnostic>)517 fn lower_fields(
518 db: &dyn DefDatabase,
519 krate: CrateId,
520 current_file_id: HirFileId,
521 container: LocalModuleId,
522 item_tree: &ItemTree,
523 cfg_options: &CfgOptions,
524 fields: &Fields,
525 override_visibility: Option<RawVisibilityId>,
526 ) -> (VariantData, Vec<DefDiagnostic>) {
527 let mut diagnostics = Vec::new();
528 match fields {
529 Fields::Record(flds) => {
530 let mut arena = Arena::new();
531 for field_id in flds.clone() {
532 let attrs = item_tree.attrs(db, krate, field_id.into());
533 let field = &item_tree[field_id];
534 if attrs.is_cfg_enabled(cfg_options) {
535 arena.alloc(lower_field(item_tree, field, override_visibility));
536 } else {
537 diagnostics.push(DefDiagnostic::unconfigured_code(
538 container,
539 InFile::new(
540 current_file_id,
541 match field.ast_id {
542 FieldAstId::Record(it) => it.upcast(),
543 FieldAstId::Tuple(it) => it.upcast(),
544 },
545 ),
546 attrs.cfg().unwrap(),
547 cfg_options.clone(),
548 ))
549 }
550 }
551 (VariantData::Record(arena), diagnostics)
552 }
553 Fields::Tuple(flds) => {
554 let mut arena = Arena::new();
555 for field_id in flds.clone() {
556 let attrs = item_tree.attrs(db, krate, field_id.into());
557 let field = &item_tree[field_id];
558 if attrs.is_cfg_enabled(cfg_options) {
559 arena.alloc(lower_field(item_tree, field, override_visibility));
560 } else {
561 diagnostics.push(DefDiagnostic::unconfigured_code(
562 container,
563 InFile::new(
564 current_file_id,
565 match field.ast_id {
566 FieldAstId::Record(it) => it.upcast(),
567 FieldAstId::Tuple(it) => it.upcast(),
568 },
569 ),
570 attrs.cfg().unwrap(),
571 cfg_options.clone(),
572 ))
573 }
574 }
575 (VariantData::Tuple(arena), diagnostics)
576 }
577 Fields::Unit => (VariantData::Unit, diagnostics),
578 }
579 }
580
lower_field( item_tree: &ItemTree, field: &Field, override_visibility: Option<RawVisibilityId>, ) -> FieldData581 fn lower_field(
582 item_tree: &ItemTree,
583 field: &Field,
584 override_visibility: Option<RawVisibilityId>,
585 ) -> FieldData {
586 FieldData {
587 name: field.name.clone(),
588 type_ref: field.type_ref.clone(),
589 visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),
590 }
591 }
592