Skip to content

Commit 6e8a54d

Browse files
committed
Auto merge of #13490 - HKalbasi:layout, r=jonas-schievink
Compute data layout of types cc #4091 Things that aren't working: * Closures * Generators (so no support for `Future` I think) * Opaque types * Type alias and associated types which may need normalization Things that show wrong result: * ~Enums with explicit discriminant~ * SIMD types * ~`NonZero*` and similar standard library items which control layout with special attributes~ At the user level, I didn't put much work, since I wasn't confident about what is the best way to present this information. Currently it shows size and align for ADTs, and size, align, offset for struct fields, in the hover, similar to clangd. I used it some days and I feel I liked it, but we may consider it too noisy and move it to an assist or command.
2 parents df07c8f + 948a8f0 commit 6e8a54d

File tree

21 files changed

+1112
-164
lines changed

21 files changed

+1112
-164
lines changed

Cargo.lock

+24
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir-def/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ base-db = { path = "../base-db", version = "0.0.0" }
3333
syntax = { path = "../syntax", version = "0.0.0" }
3434
profile = { path = "../profile", version = "0.0.0" }
3535
hir-expand = { path = "../hir-expand", version = "0.0.0" }
36+
rustc_abi = { version = "0.0.20221125", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
37+
rustc_index = { version = "0.0.20221125", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
3638
mbe = { path = "../mbe", version = "0.0.0" }
3739
cfg = { path = "../cfg", version = "0.0.0" }
3840
tt = { path = "../tt", version = "0.0.0" }

crates/hir-def/src/adt.rs

+54-41
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Defines hir-level representation of structs, enums and unions
22
3-
use std::{num::NonZeroU32, sync::Arc};
3+
use std::sync::Arc;
44

55
use base_db::CrateId;
66
use either::Either;
@@ -9,6 +9,7 @@ use hir_expand::{
99
HirFileId, InFile,
1010
};
1111
use la_arena::{Arena, ArenaMap};
12+
use rustc_abi::{Integer, IntegerType};
1213
use syntax::ast::{self, HasName, HasVisibility};
1314
use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
1415

@@ -18,6 +19,7 @@ use crate::{
1819
db::DefDatabase,
1920
intern::Interned,
2021
item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
22+
layout::{Align, ReprFlags, ReprOptions},
2123
nameres::diagnostics::DefDiagnostic,
2224
src::HasChildSource,
2325
src::HasSource,
@@ -34,7 +36,7 @@ use cfg::CfgOptions;
3436
pub struct StructData {
3537
pub name: Name,
3638
pub variant_data: Arc<VariantData>,
37-
pub repr: Option<ReprData>,
39+
pub repr: Option<ReprOptions>,
3840
pub visibility: RawVisibility,
3941
pub rustc_has_incoherent_inherent_impls: bool,
4042
}
@@ -43,7 +45,7 @@ pub struct StructData {
4345
pub struct EnumData {
4446
pub name: Name,
4547
pub variants: Arena<EnumVariantData>,
46-
pub repr: Option<ReprData>,
48+
pub repr: Option<ReprOptions>,
4749
pub visibility: RawVisibility,
4850
pub rustc_has_incoherent_inherent_impls: bool,
4951
}
@@ -69,80 +71,91 @@ pub struct FieldData {
6971
pub visibility: RawVisibility,
7072
}
7173

72-
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
73-
pub enum ReprKind {
74-
C,
75-
BuiltinInt { builtin: Either<BuiltinInt, BuiltinUint>, is_c: bool },
76-
Transparent,
77-
Default,
78-
}
79-
80-
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
81-
pub struct ReprData {
82-
pub kind: ReprKind,
83-
pub packed: bool,
84-
pub align: Option<NonZeroU32>,
85-
}
86-
8774
fn repr_from_value(
8875
db: &dyn DefDatabase,
8976
krate: CrateId,
9077
item_tree: &ItemTree,
9178
of: AttrOwner,
92-
) -> Option<ReprData> {
79+
) -> Option<ReprOptions> {
9380
item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
9481
}
9582

96-
fn parse_repr_tt(tt: &Subtree) -> Option<ReprData> {
83+
fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
9784
match tt.delimiter {
9885
Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
9986
_ => return None,
10087
}
10188

102-
let mut data = ReprData { kind: ReprKind::Default, packed: false, align: None };
89+
let mut flags = ReprFlags::empty();
90+
let mut int = None;
91+
let mut max_align: Option<Align> = None;
92+
let mut min_pack: Option<Align> = None;
10393

10494
let mut tts = tt.token_trees.iter().peekable();
10595
while let Some(tt) = tts.next() {
10696
if let TokenTree::Leaf(Leaf::Ident(ident)) = tt {
107-
match &*ident.text {
97+
flags.insert(match &*ident.text {
10898
"packed" => {
109-
data.packed = true;
110-
if let Some(TokenTree::Subtree(_)) = tts.peek() {
99+
let pack = if let Some(TokenTree::Subtree(tt)) = tts.peek() {
111100
tts.next();
112-
}
101+
if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
102+
lit.text.parse().unwrap_or_default()
103+
} else {
104+
0
105+
}
106+
} else {
107+
0
108+
};
109+
let pack = Align::from_bytes(pack).unwrap();
110+
min_pack =
111+
Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { pack });
112+
ReprFlags::empty()
113113
}
114114
"align" => {
115115
if let Some(TokenTree::Subtree(tt)) = tts.peek() {
116116
tts.next();
117117
if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
118118
if let Ok(align) = lit.text.parse() {
119-
data.align = Some(align);
119+
let align = Align::from_bytes(align).ok();
120+
max_align = max_align.max(align);
120121
}
121122
}
122123
}
124+
ReprFlags::empty()
123125
}
124-
"C" => {
125-
if let ReprKind::BuiltinInt { is_c, .. } = &mut data.kind {
126-
*is_c = true;
127-
} else {
128-
data.kind = ReprKind::C;
129-
}
130-
}
131-
"transparent" => data.kind = ReprKind::Transparent,
126+
"C" => ReprFlags::IS_C,
127+
"transparent" => ReprFlags::IS_TRANSPARENT,
132128
repr => {
133-
let is_c = matches!(data.kind, ReprKind::C);
134129
if let Some(builtin) = BuiltinInt::from_suffix(repr)
135130
.map(Either::Left)
136131
.or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
137132
{
138-
data.kind = ReprKind::BuiltinInt { builtin, is_c };
133+
int = Some(match builtin {
134+
Either::Left(bi) => match bi {
135+
BuiltinInt::Isize => IntegerType::Pointer(true),
136+
BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true),
137+
BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true),
138+
BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true),
139+
BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true),
140+
BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true),
141+
},
142+
Either::Right(bu) => match bu {
143+
BuiltinUint::Usize => IntegerType::Pointer(false),
144+
BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false),
145+
BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false),
146+
BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false),
147+
BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false),
148+
BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false),
149+
},
150+
});
139151
}
152+
ReprFlags::empty()
140153
}
141-
}
154+
})
142155
}
143156
}
144157

145-
Some(data)
158+
Some(ReprOptions { int, align: max_align, pack: min_pack, flags, field_shuffle_seed: 0 })
146159
}
147160

148161
impl StructData {
@@ -299,10 +312,10 @@ impl EnumData {
299312
Some(id)
300313
}
301314

302-
pub fn variant_body_type(&self) -> Either<BuiltinInt, BuiltinUint> {
315+
pub fn variant_body_type(&self) -> IntegerType {
303316
match self.repr {
304-
Some(ReprData { kind: ReprKind::BuiltinInt { builtin, .. }, .. }) => builtin,
305-
_ => Either::Left(BuiltinInt::Isize),
317+
Some(ReprOptions { int: Some(builtin), .. }) => builtin,
318+
_ => IntegerType::Pointer(true),
306319
}
307320
}
308321
}

crates/hir-def/src/layout.rs

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//! Definitions needed for computing data layout of types.
2+
3+
use std::cmp;
4+
5+
use la_arena::{Idx, RawIdx};
6+
pub use rustc_abi::{
7+
Abi, AbiAndPrefAlign, AddressSpace, Align, Endian, FieldsShape, Integer, IntegerType,
8+
LayoutCalculator, Niche, Primitive, ReprFlags, ReprOptions, Scalar, Size, StructKind,
9+
TargetDataLayout, WrappingRange,
10+
};
11+
12+
use crate::LocalEnumVariantId;
13+
14+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15+
pub struct RustcEnumVariantIdx(pub LocalEnumVariantId);
16+
17+
impl rustc_index::vec::Idx for RustcEnumVariantIdx {
18+
fn new(idx: usize) -> Self {
19+
RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32)))
20+
}
21+
22+
fn index(self) -> usize {
23+
u32::from(self.0.into_raw()) as usize
24+
}
25+
}
26+
27+
pub type Layout = rustc_abi::LayoutS<RustcEnumVariantIdx>;
28+
pub type TagEncoding = rustc_abi::TagEncoding<RustcEnumVariantIdx>;
29+
pub type Variants = rustc_abi::Variants<RustcEnumVariantIdx>;
30+
31+
pub trait IntegerExt {
32+
fn repr_discr(
33+
dl: &TargetDataLayout,
34+
repr: &ReprOptions,
35+
min: i128,
36+
max: i128,
37+
) -> Result<(Integer, bool), LayoutError>;
38+
}
39+
40+
impl IntegerExt for Integer {
41+
/// Finds the appropriate Integer type and signedness for the given
42+
/// signed discriminant range and `#[repr]` attribute.
43+
/// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
44+
/// that shouldn't affect anything, other than maybe debuginfo.
45+
fn repr_discr(
46+
dl: &TargetDataLayout,
47+
repr: &ReprOptions,
48+
min: i128,
49+
max: i128,
50+
) -> Result<(Integer, bool), LayoutError> {
51+
// Theoretically, negative values could be larger in unsigned representation
52+
// than the unsigned representation of the signed minimum. However, if there
53+
// are any negative values, the only valid unsigned representation is u128
54+
// which can fit all i128 values, so the result remains unaffected.
55+
let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
56+
let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
57+
58+
if let Some(ity) = repr.int {
59+
let discr = Integer::from_attr(dl, ity);
60+
let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
61+
if discr < fit {
62+
return Err(LayoutError::UserError(
63+
"Integer::repr_discr: `#[repr]` hint too small for \
64+
discriminant range of enum "
65+
.to_string(),
66+
));
67+
}
68+
return Ok((discr, ity.is_signed()));
69+
}
70+
71+
let at_least = if repr.c() {
72+
// This is usually I32, however it can be different on some platforms,
73+
// notably hexagon and arm-none/thumb-none
74+
dl.c_enum_min_size
75+
} else {
76+
// repr(Rust) enums try to be as small as possible
77+
Integer::I8
78+
};
79+
80+
// If there are no negative values, we can use the unsigned fit.
81+
Ok(if min >= 0 {
82+
(cmp::max(unsigned_fit, at_least), false)
83+
} else {
84+
(cmp::max(signed_fit, at_least), true)
85+
})
86+
}
87+
}
88+
89+
#[derive(Debug, PartialEq, Eq, Clone)]
90+
pub enum LayoutError {
91+
UserError(String),
92+
SizeOverflow,
93+
HasPlaceholder,
94+
NotImplemented,
95+
Unknown,
96+
}

crates/hir-def/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub mod adt;
3434
pub mod data;
3535
pub mod generics;
3636
pub mod lang_item;
37+
pub mod layout;
3738

3839
pub mod expr;
3940
pub mod body;

crates/hir-expand/src/name.rs

+1
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ pub mod known {
419419
shr,
420420
sub_assign,
421421
sub,
422+
unsafe_cell,
422423
va_list
423424
);
424425

crates/hir-ty/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ chalk-derive = "0.88.0"
2525
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
2626
once_cell = "1.15.0"
2727
typed-arena = "2.0.1"
28+
rustc_index = { version = "0.0.20221125", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
2829

2930
stdx = { path = "../stdx", version = "0.0.0" }
3031
hir-def = { path = "../hir-def", version = "0.0.0" }

crates/hir-ty/src/db.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ use std::sync::Arc;
55

66
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
77
use hir_def::{
8-
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
9-
FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
8+
db::DefDatabase,
9+
expr::ExprId,
10+
layout::{Layout, LayoutError, TargetDataLayout},
11+
AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId,
12+
ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
1013
};
1114
use la_arena::ArenaMap;
1215
use smallvec::SmallVec;
@@ -16,7 +19,7 @@ use crate::{
1619
consteval::{ComputedExpr, ConstEvalError},
1720
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
1821
Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig,
19-
QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId,
22+
QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId, ValueTyDefId,
2023
};
2124
use hir_expand::name::Name;
2225

@@ -57,6 +60,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
5760
#[salsa::invoke(crate::lower::field_types_query)]
5861
fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>;
5962

63+
#[salsa::invoke(crate::layout::layout_of_adt_query)]
64+
#[salsa::cycle(crate::layout::layout_of_adt_recover)]
65+
fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result<Layout, LayoutError>;
66+
67+
#[salsa::invoke(crate::layout::current_target_data_layout_query)]
68+
fn current_target_data_layout(&self) -> Arc<TargetDataLayout>;
69+
6070
#[salsa::invoke(crate::lower::callable_item_sig)]
6171
fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
6272

0 commit comments

Comments
 (0)