Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--Cargo.lock24
-rw-r--r--crates/hir-def/Cargo.toml2
-rw-r--r--crates/hir-def/src/adt.rs95
-rw-r--r--crates/hir-def/src/layout.rs96
-rw-r--r--crates/hir-def/src/lib.rs1
-rw-r--r--crates/hir-expand/src/name.rs1
-rw-r--r--crates/hir-ty/Cargo.toml1
-rw-r--r--crates/hir-ty/src/db.rs16
-rw-r--r--crates/hir-ty/src/diagnostics/match_check.rs13
-rw-r--r--crates/hir-ty/src/infer.rs25
-rw-r--r--crates/hir-ty/src/lang_items.rs20
-rw-r--r--crates/hir-ty/src/layout.rs272
-rw-r--r--crates/hir-ty/src/layout/adt.rs133
-rw-r--r--crates/hir-ty/src/layout/target.rs46
-rw-r--r--crates/hir-ty/src/layout/tests.rs196
-rw-r--r--crates/hir-ty/src/lib.rs2
-rw-r--r--crates/hir/src/lib.rs49
-rw-r--r--crates/ide/src/hover/render.rs47
-rw-r--r--crates/ide/src/hover/tests.rs219
-rw-r--r--crates/test-utils/src/minicore.rs10
-rw-r--r--lib/la-arena/src/map.rs8
21 files changed, 1112 insertions, 164 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 948bf427d4..3d9f0d7cc5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -510,6 +510,8 @@ dependencies = [
"fst",
"hashbrown",
"hir-expand",
+ "hkalbasi-rustc-ap-rustc_abi",
+ "hkalbasi-rustc-ap-rustc_index",
"indexmap",
"itertools",
"la-arena",
@@ -564,6 +566,7 @@ dependencies = [
"expect-test",
"hir-def",
"hir-expand",
+ "hkalbasi-rustc-ap-rustc_index",
"itertools",
"la-arena",
"limit",
@@ -582,6 +585,27 @@ dependencies = [
]
[[package]]
+name = "hkalbasi-rustc-ap-rustc_abi"
+version = "0.0.20221125"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c8368a30e518c0102d670d8515f7d424d875ee615ec7a7b6d29217b57a0371"
+dependencies = [
+ "bitflags",
+ "hkalbasi-rustc-ap-rustc_index",
+ "tracing",
+]
+
+[[package]]
+name = "hkalbasi-rustc-ap-rustc_index"
+version = "0.0.20221125"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c07bba80d7f6a8e1efb0f3e2115ef1eecbf97292dc8cad84e4982226b9aa12e2"
+dependencies = [
+ "arrayvec",
+ "smallvec",
+]
+
+[[package]]
name = "home"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml
index 22f98ea7cd..9ecce46601 100644
--- a/crates/hir-def/Cargo.toml
+++ b/crates/hir-def/Cargo.toml
@@ -33,6 +33,8 @@ base-db = { path = "../base-db", version = "0.0.0" }
syntax = { path = "../syntax", version = "0.0.0" }
profile = { path = "../profile", version = "0.0.0" }
hir-expand = { path = "../hir-expand", version = "0.0.0" }
+rustc_abi = { version = "0.0.20221125", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
+rustc_index = { version = "0.0.20221125", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
mbe = { path = "../mbe", version = "0.0.0" }
cfg = { path = "../cfg", version = "0.0.0" }
tt = { path = "../tt", version = "0.0.0" }
diff --git a/crates/hir-def/src/adt.rs b/crates/hir-def/src/adt.rs
index e5ab7bf3f6..db3b419488 100644
--- a/crates/hir-def/src/adt.rs
+++ b/crates/hir-def/src/adt.rs
@@ -1,6 +1,6 @@
//! Defines hir-level representation of structs, enums and unions
-use std::{num::NonZeroU32, sync::Arc};
+use std::sync::Arc;
use base_db::CrateId;
use either::Either;
@@ -9,6 +9,7 @@ use hir_expand::{
HirFileId, InFile,
};
use la_arena::{Arena, ArenaMap};
+use rustc_abi::{Integer, IntegerType};
use syntax::ast::{self, HasName, HasVisibility};
use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
@@ -18,6 +19,7 @@ use crate::{
db::DefDatabase,
intern::Interned,
item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
+ layout::{Align, ReprFlags, ReprOptions},
nameres::diagnostics::DefDiagnostic,
src::HasChildSource,
src::HasSource,
@@ -34,7 +36,7 @@ use cfg::CfgOptions;
pub struct StructData {
pub name: Name,
pub variant_data: Arc<VariantData>,
- pub repr: Option<ReprData>,
+ pub repr: Option<ReprOptions>,
pub visibility: RawVisibility,
pub rustc_has_incoherent_inherent_impls: bool,
}
@@ -43,7 +45,7 @@ pub struct StructData {
pub struct EnumData {
pub name: Name,
pub variants: Arena<EnumVariantData>,
- pub repr: Option<ReprData>,
+ pub repr: Option<ReprOptions>,
pub visibility: RawVisibility,
pub rustc_has_incoherent_inherent_impls: bool,
}
@@ -69,80 +71,91 @@ pub struct FieldData {
pub visibility: RawVisibility,
}
-#[derive(Copy, Debug, Clone, PartialEq, Eq)]
-pub enum ReprKind {
- C,
- BuiltinInt { builtin: Either<BuiltinInt, BuiltinUint>, is_c: bool },
- Transparent,
- Default,
-}
-
-#[derive(Copy, Debug, Clone, PartialEq, Eq)]
-pub struct ReprData {
- pub kind: ReprKind,
- pub packed: bool,
- pub align: Option<NonZeroU32>,
-}
-
fn repr_from_value(
db: &dyn DefDatabase,
krate: CrateId,
item_tree: &ItemTree,
of: AttrOwner,
-) -> Option<ReprData> {
+) -> Option<ReprOptions> {
item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
}
-fn parse_repr_tt(tt: &Subtree) -> Option<ReprData> {
+fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
match tt.delimiter {
Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
_ => return None,
}
- let mut data = ReprData { kind: ReprKind::Default, packed: false, align: None };
+ let mut flags = ReprFlags::empty();
+ let mut int = None;
+ let mut max_align: Option<Align> = None;
+ let mut min_pack: Option<Align> = None;
let mut tts = tt.token_trees.iter().peekable();
while let Some(tt) = tts.next() {
if let TokenTree::Leaf(Leaf::Ident(ident)) = tt {
- match &*ident.text {
+ flags.insert(match &*ident.text {
"packed" => {
- data.packed = true;
- if let Some(TokenTree::Subtree(_)) = tts.peek() {
+ let pack = if let Some(TokenTree::Subtree(tt)) = tts.peek() {
tts.next();
- }
+ if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
+ lit.text.parse().unwrap_or_default()
+ } else {
+ 0
+ }
+ } else {
+ 0
+ };
+ let pack = Align::from_bytes(pack).unwrap();
+ min_pack =
+ Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { pack });
+ ReprFlags::empty()
}
"align" => {
if let Some(TokenTree::Subtree(tt)) = tts.peek() {
tts.next();
if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
if let Ok(align) = lit.text.parse() {
- data.align = Some(align);
+ let align = Align::from_bytes(align).ok();
+ max_align = max_align.max(align);
}
}
}
+ ReprFlags::empty()
}
- "C" => {
- if let ReprKind::BuiltinInt { is_c, .. } = &mut data.kind {
- *is_c = true;
- } else {
- data.kind = ReprKind::C;
- }
- }
- "transparent" => data.kind = ReprKind::Transparent,
+ "C" => ReprFlags::IS_C,
+ "transparent" => ReprFlags::IS_TRANSPARENT,
repr => {
- let is_c = matches!(data.kind, ReprKind::C);
if let Some(builtin) = BuiltinInt::from_suffix(repr)
.map(Either::Left)
.or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
{
- data.kind = ReprKind::BuiltinInt { builtin, is_c };
+ int = Some(match builtin {
+ Either::Left(bi) => match bi {
+ BuiltinInt::Isize => IntegerType::Pointer(true),
+ BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true),
+ BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true),
+ BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true),
+ BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true),
+ BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true),
+ },
+ Either::Right(bu) => match bu {
+ BuiltinUint::Usize => IntegerType::Pointer(false),
+ BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false),
+ BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false),
+ BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false),
+ BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false),
+ BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false),
+ },
+ });
}
+ ReprFlags::empty()
}
- }
+ })
}
}
- Some(data)
+ Some(ReprOptions { int, align: max_align, pack: min_pack, flags, field_shuffle_seed: 0 })
}
impl StructData {
@@ -299,10 +312,10 @@ impl EnumData {
Some(id)
}
- pub fn variant_body_type(&self) -> Either<BuiltinInt, BuiltinUint> {
+ pub fn variant_body_type(&self) -> IntegerType {
match self.repr {
- Some(ReprData { kind: ReprKind::BuiltinInt { builtin, .. }, .. }) => builtin,
- _ => Either::Left(BuiltinInt::Isize),
+ Some(ReprOptions { int: Some(builtin), .. }) => builtin,
+ _ => IntegerType::Pointer(true),
}
}
}
diff --git a/crates/hir-def/src/layout.rs b/crates/hir-def/src/layout.rs
new file mode 100644
index 0000000000..a427c464bc
--- /dev/null
+++ b/crates/hir-def/src/layout.rs
@@ -0,0 +1,96 @@
+//! Definitions needed for computing data layout of types.
+
+use std::cmp;
+
+use la_arena::{Idx, RawIdx};
+pub use rustc_abi::{
+ Abi, AbiAndPrefAlign, AddressSpace, Align, Endian, FieldsShape, Integer, IntegerType,
+ LayoutCalculator, Niche, Primitive, ReprFlags, ReprOptions, Scalar, Size, StructKind,
+ TargetDataLayout, WrappingRange,
+};
+
+use crate::LocalEnumVariantId;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct RustcEnumVariantIdx(pub LocalEnumVariantId);
+
+impl rustc_index::vec::Idx for RustcEnumVariantIdx {
+ fn new(idx: usize) -> Self {
+ RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32)))
+ }
+
+ fn index(self) -> usize {
+ u32::from(self.0.into_raw()) as usize
+ }
+}
+
+pub type Layout = rustc_abi::LayoutS<RustcEnumVariantIdx>;
+pub type TagEncoding = rustc_abi::TagEncoding<RustcEnumVariantIdx>;
+pub type Variants = rustc_abi::Variants<RustcEnumVariantIdx>;
+
+pub trait IntegerExt {
+ fn repr_discr(
+ dl: &TargetDataLayout,
+ repr: &ReprOptions,
+ min: i128,
+ max: i128,
+ ) -> Result<(Integer, bool), LayoutError>;
+}
+
+impl IntegerExt for Integer {
+ /// Finds the appropriate Integer type and signedness for the given
+ /// signed discriminant range and `#[repr]` attribute.
+ /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
+ /// that shouldn't affect anything, other than maybe debuginfo.
+ fn repr_discr(
+ dl: &TargetDataLayout,
+ repr: &ReprOptions,
+ min: i128,
+ max: i128,
+ ) -> Result<(Integer, bool), LayoutError> {
+ // Theoretically, negative values could be larger in unsigned representation
+ // than the unsigned representation of the signed minimum. However, if there
+ // are any negative values, the only valid unsigned representation is u128
+ // which can fit all i128 values, so the result remains unaffected.
+ let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
+ let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
+
+ if let Some(ity) = repr.int {
+ let discr = Integer::from_attr(dl, ity);
+ let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
+ if discr < fit {
+ return Err(LayoutError::UserError(
+ "Integer::repr_discr: `#[repr]` hint too small for \
+ discriminant range of enum "
+ .to_string(),
+ ));
+ }
+ return Ok((discr, ity.is_signed()));
+ }
+
+ let at_least = if repr.c() {
+ // This is usually I32, however it can be different on some platforms,
+ // notably hexagon and arm-none/thumb-none
+ dl.c_enum_min_size
+ } else {
+ // repr(Rust) enums try to be as small as possible
+ Integer::I8
+ };
+
+ // If there are no negative values, we can use the unsigned fit.
+ Ok(if min >= 0 {
+ (cmp::max(unsigned_fit, at_least), false)
+ } else {
+ (cmp::max(signed_fit, at_least), true)
+ })
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum LayoutError {
+ UserError(String),
+ SizeOverflow,
+ HasPlaceholder,
+ NotImplemented,
+ Unknown,
+}
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 5c7aa72349..8267ef09cb 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -34,6 +34,7 @@ pub mod adt;
pub mod data;
pub mod generics;
pub mod lang_item;
+pub mod layout;
pub mod expr;
pub mod body;
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 259fe1327f..beff3f6ad9 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -419,6 +419,7 @@ pub mod known {
shr,
sub_assign,
sub,
+ unsafe_cell,
va_list
);
diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml
index a8a74f3bf4..e4dd244ab8 100644
--- a/crates/hir-ty/Cargo.toml
+++ b/crates/hir-ty/Cargo.toml
@@ -25,6 +25,7 @@ chalk-derive = "0.88.0"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
once_cell = "1.15.0"
typed-arena = "2.0.1"
+rustc_index = { version = "0.0.20221125", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
stdx = { path = "../stdx", version = "0.0.0" }
hir-def = { path = "../hir-def", version = "0.0.0" }
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index ae6bf786cf..23ad335aac 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -5,8 +5,11 @@ use std::sync::Arc;
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{
- db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
- FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
+ db::DefDatabase,
+ expr::ExprId,
+ layout::{Layout, LayoutError, TargetDataLayout},
+ AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId,
+ ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
};
use la_arena::ArenaMap;
use smallvec::SmallVec;
@@ -16,7 +19,7 @@ use crate::{
consteval::{ComputedExpr, ConstEvalError},
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig,
- QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId,
+ QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId, ValueTyDefId,
};
use hir_expand::name::Name;
@@ -57,6 +60,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::lower::field_types_query)]
fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>;
+ #[salsa::invoke(crate::layout::layout_of_adt_query)]
+ #[salsa::cycle(crate::layout::layout_of_adt_recover)]
+ fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result<Layout, LayoutError>;
+
+ #[salsa::invoke(crate::layout::current_target_data_layout_query)]
+ fn current_target_data_layout(&self) -> Arc<TargetDataLayout>;
+
#[salsa::invoke(crate::lower::callable_item_sig)]
fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs
index d51ad72bd2..e0905e01b6 100644
--- a/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/crates/hir-ty/src/diagnostics/match_check.rs
@@ -12,16 +12,16 @@ pub(crate) mod usefulness;
use chalk_ir::Mutability;
use hir_def::{
- adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, HasModule, LocalFieldId,
- VariantId,
+ adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
};
-use hir_expand::name::{name, Name};
+use hir_expand::name::Name;
use stdx::{always, never};
use crate::{
db::HirDatabase,
display::{HirDisplay, HirDisplayError, HirFormatter},
infer::BindingMode,
+ lang_items::is_box,
InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
};
@@ -405,13 +405,6 @@ where
}
}
-fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool {
- let owned_box = name![owned_box].to_smol_str();
- let krate = adt.module(db.upcast()).krate();
- let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from);
- Some(adt) == box_adt
-}
-
pub(crate) trait PatternFoldable: Sized {
fn fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self {
self.super_fold_with(folder)
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 112eb5bd84..874a54fc3e 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -19,10 +19,11 @@ use std::sync::Arc;
use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
use hir_def::{
body::Body,
- builtin_type::BuiltinType,
+ builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
data::{ConstData, StaticData},
expr::{BindingAnnotation, ExprId, PatId},
lang_item::LangItemTarget,
+ layout::Integer,
path::{path, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::TypeRef,
@@ -70,8 +71,26 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
DefWithBodyId::VariantId(v) => {
ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() {
- Either::Left(builtin) => BuiltinType::Int(builtin),
- Either::Right(builtin) => BuiltinType::Uint(builtin),
+ hir_def::layout::IntegerType::Pointer(signed) => match signed {
+ true => BuiltinType::Int(BuiltinInt::Isize),
+ false => BuiltinType::Uint(BuiltinUint::Usize),
+ },
+ hir_def::layout::IntegerType::Fixed(size, signed) => match signed {
+ true => BuiltinType::Int(match size {
+ Integer::I8 => BuiltinInt::I8,
+ Integer::I16 => BuiltinInt::I16,
+ Integer::I32 => BuiltinInt::I32,
+ Integer::I64 => BuiltinInt::I64,
+ Integer::I128 => BuiltinInt::I128,
+ }),
+ false => BuiltinType::Uint(match size {
+ Integer::I8 => BuiltinUint::U8,
+ Integer::I16 => BuiltinUint::U16,
+ Integer::I32 => BuiltinUint::U32,
+ Integer::I64 => BuiltinUint::U64,
+ Integer::I128 => BuiltinUint::U128,
+ }),
+ },
});
}
}
diff --git a/crates/hir-ty/src/lang_items.rs b/crates/hir-ty/src/lang_items.rs
new file mode 100644
index 0000000000..afc54e729f
--- /dev/null
+++ b/crates/hir-ty/src/lang_items.rs
@@ -0,0 +1,20 @@
+//! Functions to detect special lang items
+
+use hir_def::{AdtId, HasModule};
+use hir_expand::name;
+
+use crate::db::HirDatabase;
+
+pub fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool {
+ let owned_box = name![owned_box].to_smol_str();
+ let krate = adt.module(db.upcast()).krate();
+ let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from);
+ Some(adt) == box_adt
+}
+
+pub fn is_unsafe_cell(adt: AdtId, db: &dyn HirDatabase) -> bool {
+ let owned_box = name![unsafe_cell].to_smol_str();
+ let krate = adt.module(db.upcast()).krate();
+ let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from);
+ Some(adt) == box_adt
+}
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
new file mode 100644
index 0000000000..3c6489fa97
--- /dev/null
+++ b/crates/hir-ty/src/layout.rs
@@ -0,0 +1,272 @@
+//! Compute the binary representation of a type
+
+use std::sync::Arc;
+
+use chalk_ir::{AdtId, TyKind};
+pub(self) use hir_def::layout::*;
+use hir_def::LocalFieldId;
+use stdx::never;
+
+use crate::{db::HirDatabase, Interner, Substitution, Ty};
+
+use self::adt::struct_variant_idx;
+pub use self::{
+ adt::{layout_of_adt_query, layout_of_adt_recover},
+ target::current_target_data_layout_query,
+};
+
+macro_rules! user_error {
+ ($x: expr) => {
+ return Err(LayoutError::UserError(format!($x)))
+ };
+}
+
+mod adt;
+mod target;
+
+struct LayoutCx<'a> {
+ db: &'a dyn HirDatabase,
+}
+
+impl LayoutCalculator for LayoutCx<'_> {
+ type TargetDataLayoutRef = Arc<TargetDataLayout>;
+
+ fn delay_bug(&self, txt: &str) {
+ never!("{}", txt);
+ }
+
+ fn current_data_layout(&self) -> Arc<TargetDataLayout> {
+ self.db.current_target_data_layout()
+ }
+}
+
+fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
+ Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
+}
+
+fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout {
+ Layout::scalar(dl, scalar_unit(dl, value))
+}
+
+pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result<Layout, LayoutError> {
+ let dl = &*db.current_target_data_layout();
+ let cx = LayoutCx { db };
+ Ok(match ty.kind(Interner) {
+ TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?,
+ TyKind::Scalar(s) => match s {
+ chalk_ir::Scalar::Bool => Layout::scalar(
+ dl,
+ Scalar::Initialized {
+ value: Primitive::Int(Integer::I8, false),
+ valid_range: WrappingRange { start: 0, end: 1 },
+ },
+ ),
+ chalk_ir::Scalar::Char => Layout::scalar(
+ dl,
+ Scalar::Initialized {
+ value: Primitive::Int(Integer::I32, false),
+ valid_range: WrappingRange { start: 0, end: 0x10FFFF },
+ },
+ ),
+ chalk_ir::Scalar::Int(i) => scalar(
+ dl,
+ Primitive::Int(
+ match i {
+ chalk_ir::IntTy::Isize => dl.ptr_sized_integer(),
+ chalk_ir::IntTy::I8 => Integer::I8,
+ chalk_ir::IntTy::I16 => Integer::I16,
+ chalk_ir::IntTy::I32 => Integer::I32,
+ chalk_ir::IntTy::I64 => Integer::I64,
+ chalk_ir::IntTy::I128 => Integer::I128,
+ },
+ false,
+ ),
+ ),
+ chalk_ir::Scalar::Uint(i) => scalar(
+ dl,
+ Primitive::Int(
+ match i {
+ chalk_ir::UintTy::Usize => dl.ptr_sized_integer(),
+ chalk_ir::UintTy::U8 => Integer::I8,
+ chalk_ir::UintTy::U16 => Integer::I16,
+ chalk_ir::UintTy::U32 => Integer::I32,
+ chalk_ir::UintTy::U64 => Integer::I64,
+ chalk_ir::UintTy::U128 => Integer::I128,
+ },
+ true,
+ ),
+ ),
+ chalk_ir::Scalar::Float(f) => scalar(
+ dl,
+ match f {
+ chalk_ir::FloatTy::F32 => Primitive::F32,
+ chalk_ir::FloatTy::F64 => Primitive::F64,
+ },
+ ),
+ },
+ TyKind::Tuple(len, tys) => {
+ let kind = if *len == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
+
+ let fields = tys
+ .iter(Interner)
+ .map(|k| layout_of_ty(db, k.assert_ty_ref(Interner)))
+ .collect::<Result<Vec<_>, _>>()?;
+ let fields = fields.iter().collect::<Vec<_>>();
+ let fields = fields.iter().collect::<Vec<_>>();
+ cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)?
+ }
+ TyKind::Array(element, count) => {
+ let count = match count.data(Interner).value {
+ chalk_ir::ConstValue::Concrete(c) => match c.interned {
+ hir_def::type_ref::ConstScalar::Int(x) => x as u64,
+ hir_def::type_ref::ConstScalar::UInt(x) => x as u64,
+ hir_def::type_ref::ConstScalar::Unknown => {
+ user_error!("unknown const generic parameter")
+ }
+ _ => user_error!("mismatched type of const generic parameter"),
+ },
+ _ => return Err(LayoutError::HasPlaceholder),
+ };
+ let element = layout_of_ty(db, element)?;
+ let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
+
+ let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) {
+ Abi::Uninhabited
+ } else {
+ Abi::Aggregate { sized: true }
+ };
+
+ let largest_niche = if count != 0 { element.largest_niche } else { None };
+
+ Layout {
+ variants: Variants::Single { index: struct_variant_idx() },
+ fields: FieldsShape::Array { stride: element.size, count },
+ abi,
+ largest_niche,
+ align: element.align,
+ size,
+ }
+ }
+ TyKind::Slice(element) => {
+ let element = layout_of_ty(db, element)?;
+ Layout {
+ variants: Variants::Single { index: struct_variant_idx() },
+ fields: FieldsShape::Array { stride: element.size, count: 0 },
+ abi: Abi::Aggregate { sized: false },
+ largest_niche: None,
+ align: element.align,
+ size: Size::ZERO,
+ }
+ }
+ // Potentially-wide pointers.
+ TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => {
+ let mut data_ptr = scalar_unit(dl, Primitive::Pointer);
+ if matches!(ty.kind(Interner), TyKind::Ref(..)) {
+ data_ptr.valid_range_mut().start = 1;
+ }
+
+ // let pointee = tcx.normalize_erasing_regions(param_env, pointee);
+ // if pointee.is_sized(tcx.at(DUMMY_SP), param_env) {
+ // return Ok(tcx.intern_layout(LayoutS::scalar(cx, data_ptr)));
+ // }
+
+ let unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone());
+ let metadata = match unsized_part.kind(Interner) {
+ TyKind::Slice(_) | TyKind::Str => {
+ scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
+ }
+ TyKind::Dyn(..) => {
+ let mut vtable = scalar_unit(dl, Primitive::Pointer);
+ vtable.valid_range_mut().start = 1;
+ vtable
+ }
+ _ => {
+ // pointee is sized
+ return Ok(Layout::scalar(dl, data_ptr));
+ }
+ };
+
+ // Effectively a (ptr, meta) tuple.
+ cx.scalar_pair(data_ptr, metadata)
+ }
+ TyKind::FnDef(_, _) => layout_of_unit(&cx, dl)?,
+ TyKind::Str => Layout {
+ variants: Variants::Single { index: struct_variant_idx() },
+ fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 },
+ abi: Abi::Aggregate { sized: false },
+ largest_niche: None,
+ align: dl.i8_align,
+ size: Size::ZERO,
+ },
+ TyKind::Never => Layout {
+ variants: Variants::Single { index: struct_variant_idx() },
+ fields: FieldsShape::Primitive,
+ abi: Abi::Uninhabited,
+ largest_niche: None,
+ align: dl.i8_align,
+ size: Size::ZERO,
+ },
+ TyKind::Dyn(_) | TyKind::Foreign(_) => {
+ let mut unit = layout_of_unit(&cx, dl)?;
+ match unit.abi {
+ Abi::Aggregate { ref mut sized } => *sized = false,
+ _ => user_error!("bug"),
+ }
+ unit
+ }
+ TyKind::Function(_) => {
+ let mut ptr = scalar_unit(dl, Primitive::Pointer);
+ ptr.valid_range_mut().start = 1;
+ Layout::scalar(dl, ptr)
+ }
+ TyKind::Closure(_, _)
+ | TyKind::OpaqueType(_, _)
+ | TyKind::Generator(_, _)
+ | TyKind::GeneratorWitness(_, _) => return Err(LayoutError::NotImplemented),
+ TyKind::AssociatedType(_, _)
+ | TyKind::Error
+ | TyKind::Alias(_)
+ | TyKind::Placeholder(_)
+ | TyKind::BoundVar(_)
+ | TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder),
+ })
+}
+
+fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result<Layout, LayoutError> {
+ cx.univariant::<RustcEnumVariantIdx, &&Layout>(
+ &dl,
+ &[],
+ &ReprOptions::default(),
+ StructKind::AlwaysSized,
+ )
+ .ok_or(LayoutError::Unknown)
+}
+
+fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty {
+ match pointee.kind(Interner) {
+ TyKind::Adt(AdtId(adt), subst) => match adt {
+ &hir_def::AdtId::StructId(i) => {
+ let data = db.struct_data(i);
+ let mut it = data.variant_data.fields().iter().rev();
+ match it.next() {
+ Some((f, _)) => field_ty(db, i.into(), f, subst),
+ None => pointee,
+ }
+ }
+ _ => pointee,
+ },
+ _ => pointee,
+ }
+}
+
+fn field_ty(
+ db: &dyn HirDatabase,
+ def: hir_def::VariantId,
+ fd: LocalFieldId,
+ subst: &Substitution,
+) -> Ty {
+ db.field_types(def)[fd].clone().substitute(Interner, subst)
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs
new file mode 100644
index 0000000000..d9791a4b63
--- /dev/null
+++ b/crates/hir-ty/src/layout/adt.rs
@@ -0,0 +1,133 @@
+//! Compute the binary representation of structs, unions and enums
+
+use std::ops::Bound;
+
+use hir_def::{
+ adt::VariantData,
+ layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
+ AdtId, EnumVariantId, LocalEnumVariantId, VariantId,
+};
+use la_arena::RawIdx;
+use rustc_index::vec::IndexVec;
+
+use crate::{db::HirDatabase, lang_items::is_unsafe_cell, layout::field_ty, Substitution};
+
+use super::{layout_of_ty, LayoutCx};
+
+pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx {
+ RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0)))
+}
+
+pub fn layout_of_adt_query(
+ db: &dyn HirDatabase,
+ def: AdtId,
+ subst: Substitution,
+) -> Result<Layout, LayoutError> {
+ let dl = db.current_target_data_layout();
+ let cx = LayoutCx { db };
+ let handle_variant = |def: VariantId, var: &VariantData| {
+ var.fields()
+ .iter()
+ .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst)))
+ .collect::<Result<Vec<_>, _>>()
+ };
+ let (variants, is_enum, is_union, repr) = match def {
+ AdtId::StructId(s) => {
+ let data = db.struct_data(s);
+ let mut r = IndexVec::new();
+ r.push(handle_variant(s.into(), &data.variant_data)?);
+ (r, false, false, data.repr.unwrap_or_default())
+ }
+ AdtId::UnionId(id) => {
+ let data = db.union_data(id);
+ let mut r = IndexVec::new();
+ r.push(handle_variant(id.into(), &data.variant_data)?);
+ (r, false, true, data.repr.unwrap_or_default())
+ }
+ AdtId::EnumId(e) => {
+ let data = db.enum_data(e);
+ let r = data
+ .variants
+ .iter()
+ .map(|(idx, v)| {
+ handle_variant(
+ EnumVariantId { parent: e, local_id: idx }.into(),
+ &v.variant_data,
+ )
+ })
+ .collect::<Result<IndexVec<RustcEnumVariantIdx, _>, _>>()?;
+ (r, true, false, data.repr.unwrap_or_default())
+ }
+ };
+ let variants = variants.iter().map(|x| x.iter().collect::<Vec<_>>()).collect::<Vec<_>>();
+ let variants = variants.iter().map(|x| x.iter().collect()).collect();
+ if is_union {
+ cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)
+ } else {
+ cx.layout_of_struct_or_enum(
+ &repr,
+ &variants,
+ is_enum,
+ is_unsafe_cell(def, db),
+ layout_scalar_valid_range(db, def),
+ |min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)),
+ variants.iter_enumerated().filter_map(|(id, _)| {
+ let AdtId::EnumId(e) = def else { return None };
+ let d = match db
+ .const_eval_variant(EnumVariantId { parent: e, local_id: id.0 })
+ .ok()?
+ {
+ crate::consteval::ComputedExpr::Literal(l) => match l {
+ hir_def::expr::Literal::Int(i, _) => i,
+ hir_def::expr::Literal::Uint(i, _) => i as i128,
+ _ => return None,
+ },
+ _ => return None,
+ };
+ Some((id, d))
+ }),
+ // FIXME: The current code for niche-filling relies on variant indices
+ // instead of actual discriminants, so enums with
+ // explicit discriminants (RFC #2363) would misbehave and we should disable
+ // niche optimization for them.
+ // The code that do it in rustc:
+ // repr.inhibit_enum_layout_opt() || def
+ // .variants()
+ // .iter_enumerated()
+ // .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()))
+ repr.inhibit_enum_layout_opt(),
+ !is_enum
+ && variants
+ .iter()
+ .next()
+ .and_then(|x| x.last().map(|x| x.is_unsized()))
+ .unwrap_or(true),
+ )
+ .ok_or(LayoutError::SizeOverflow)
+ }
+}
+
+fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
+ let attrs = db.attrs(def.into());
+ let get = |name| {
+ let attr = attrs.by_key(name).tt_values();
+ for tree in attr {
+ if let Some(x) = tree.token_trees.first() {
+ if let Ok(x) = x.to_string().parse() {
+ return Bound::Included(x);
+ }
+ }
+ }
+ Bound::Unbounded
+ };
+ (get("rustc_layout_scalar_valid_range_start"), get("rustc_layout_scalar_valid_range_end"))
+}
+
+pub fn layout_of_adt_recover(
+ _: &dyn HirDatabase,
+ _: &[String],
+ _: &AdtId,
+ _: &Substitution,
+) -> Result<Layout, LayoutError> {
+ user_error!("infinite sized recursive type");
+}
diff --git a/crates/hir-ty/src/layout/target.rs b/crates/hir-ty/src/layout/target.rs
new file mode 100644
index 0000000000..b76274bb85
--- /dev/null
+++ b/crates/hir-ty/src/layout/target.rs
@@ -0,0 +1,46 @@
+//! Target dependent parameters needed for layouts
+
+use std::sync::Arc;
+
+use hir_def::layout::TargetDataLayout;
+
+use crate::db::HirDatabase;
+
+use super::{AbiAndPrefAlign, AddressSpace, Align, Endian, Integer, Size};
+
+pub fn current_target_data_layout_query(db: &dyn HirDatabase) -> Arc<TargetDataLayout> {
+ let crate_graph = db.crate_graph();
+ let cfg_options = &crate_graph[crate_graph.iter().next().unwrap()].cfg_options;
+ let endian = match cfg_options.get_cfg_values("target_endian").next() {
+ Some(x) if x.as_str() == "big" => Endian::Big,
+ _ => Endian::Little,
+ };
+ let pointer_size =
+ Size::from_bytes(match cfg_options.get_cfg_values("target_pointer_width").next() {
+ Some(x) => match x.as_str() {
+ "16" => 2,
+ "32" => 4,
+ _ => 8,
+ },
+ _ => 8,
+ });
+ // FIXME: These values are incorrect for many architectures, at least for aarch64 and riscv64,
+ // use `rustc +nightly -Z unstable-options --print target-spec-json` or something similar instead.
+ Arc::new(TargetDataLayout {
+ endian,
+ i1_align: AbiAndPrefAlign::new(Align::from_bytes(1).unwrap()),
+ i8_align: AbiAndPrefAlign::new(Align::from_bytes(1).unwrap()),
+ i16_align: AbiAndPrefAlign::new(Align::from_bytes(2).unwrap()),
+ i32_align: AbiAndPrefAlign::new(Align::from_bytes(4).unwrap()),
+ i64_align: AbiAndPrefAlign::new(Align::from_bytes(8).unwrap()),
+ i128_align: AbiAndPrefAlign::new(Align::from_bytes(8).unwrap()),
+ f32_align: AbiAndPrefAlign::new(Align::from_bytes(4).unwrap()),
+ f64_align: AbiAndPrefAlign::new(Align::from_bytes(8).unwrap()),
+ pointer_size,
+ pointer_align: AbiAndPrefAlign::new(Align::from_bytes(pointer_size.bytes()).unwrap()),
+ aggregate_align: AbiAndPrefAlign::new(Align::from_bytes(1).unwrap()),
+ vector_align: vec![],
+ instruction_address_space: AddressSpace(0),
+ c_enum_min_size: Integer::I32,
+ })
+}
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
new file mode 100644
index 0000000000..5d97a69501
--- /dev/null
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -0,0 +1,196 @@
+use base_db::fixture::WithFixture;
+use chalk_ir::{AdtId, TyKind};
+use hir_def::{
+ db::DefDatabase,
+ layout::{Layout, LayoutError},
+};
+
+use crate::{test_db::TestDB, Interner, Substitution};
+
+use super::layout_of_ty;
+
+fn eval_goal(ra_fixture: &str) -> Result<Layout, LayoutError> {
+ let (db, file_id) = TestDB::with_single_file(ra_fixture);
+ let module_id = db.module_for_file(file_id);
+ let def_map = module_id.def_map(&db);
+ let scope = &def_map[module_id.local_id].scope;
+ let adt_id = scope
+ .declarations()
+ .into_iter()
+ .find_map(|x| match x {
+ hir_def::ModuleDefId::AdtId(x) => {
+ let name = match x {
+ hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_string(),
+ hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_string(),
+ hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_string(),
+ };
+ if name == "Goal" {
+ Some(x)
+ } else {
+ None
+ }
+ }
+ _ => None,
+ })
+ .unwrap();
+ let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner);
+ layout_of_ty(&db, &goal_ty)
+}
+
+fn check_size_and_align(ra_fixture: &str, size: u64, align: u64) {
+ let l = eval_goal(ra_fixture).unwrap();
+ assert_eq!(l.size.bytes(), size);
+ assert_eq!(l.align.abi.bytes(), align);
+}
+
+fn check_fail(ra_fixture: &str, e: LayoutError) {
+ let r = eval_goal(ra_fixture);
+ assert_eq!(r, Err(e));
+}
+
+macro_rules! size_and_align {
+ (minicore: $($x:tt),*;$($t:tt)*) => {
+ {
+ #[allow(dead_code)]
+ $($t)*
+ check_size_and_align(
+ &format!("//- minicore: {}\n{}", stringify!($($x),*), stringify!($($t)*)),
+ ::std::mem::size_of::<Goal>() as u64,
+ ::std::mem::align_of::<Goal>() as u64,
+ );
+ }
+ };
+ ($($t:tt)*) => {
+ {
+ #[allow(dead_code)]
+ $($t)*
+ check_size_and_align(
+ stringify!($($t)*),
+ ::std::mem::size_of::<Goal>() as u64,
+ ::std::mem::align_of::<Goal>() as u64,
+ );
+ }
+ };
+}
+
+#[test]
+fn hello_world() {
+ size_and_align! {
+ struct Goal(i32);
+ }
+}
+
+#[test]
+fn field_order_optimization() {
+ size_and_align! {
+ struct Goal(u8, i32, u8);
+ }
+ size_and_align! {
+ #[repr(C)]
+ struct Goal(u8, i32, u8);
+ }
+}
+
+#[test]
+fn recursive() {
+ size_and_align! {
+ struct Goal {
+ left: &'static Goal,
+ right: &'static Goal,
+ }
+ }
+ size_and_align! {
+ struct BoxLike<T: ?Sized>(*mut T);
+ struct Goal(BoxLike<Goal>);
+ }
+ check_fail(
+ r#"struct Goal(Goal);"#,
+ LayoutError::UserError("infinite sized recursive type".to_string()),
+ );
+ check_fail(
+ r#"
+ struct Foo<T>(Foo<T>);
+ struct Goal(Foo<i32>);
+ "#,
+ LayoutError::UserError("infinite sized recursive type".to_string()),
+ );
+}
+
+#[test]
+fn generic() {
+ size_and_align! {
+ struct Pair<A, B>(A, B);
+ struct Goal(Pair<Pair<i32, u8>, i64>);
+ }
+ size_and_align! {
+ struct X<const N: usize> {
+ field1: [i32; N],
+ field2: [u8; N],
+ }
+ struct Goal(X<1000>);
+ }
+}
+
+#[test]
+fn enums() {
+ size_and_align! {
+ enum Goal {
+ Quit,
+ Move { x: i32, y: i32 },
+ ChangeColor(i32, i32, i32),
+ }
+ }
+}
+
+#[test]
+fn primitives() {
+ size_and_align! {
+ struct Goal(i32, i128, isize, usize, f32, f64, bool, char);
+ }
+}
+
+#[test]
+fn tuple() {
+ size_and_align! {
+ struct Goal((), (i32, u64, bool));
+ }
+}
+
+#[test]
+fn non_zero() {
+ size_and_align! {
+ minicore: non_zero, option;
+ use core::num::NonZeroU8;
+ struct Goal(Option<NonZeroU8>);
+ }
+}
+
+#[test]
+fn niche_optimization() {
+ size_and_align! {
+ minicore: option;
+ struct Goal(Option<&'static i32>);
+ }
+ size_and_align! {
+ minicore: option;
+ struct Goal(Option<Option<bool>>);
+ }
+}
+
+#[test]
+fn enums_with_discriminants() {
+ size_and_align! {
+ enum Goal {
+ A = 1000,
+ B = 2000,
+ C = 3000,
+ }
+ }
+ size_and_align! {
+ enum Goal {
+ A = 254,
+ B,
+ C, // implicitly becomes 256, so we need two bytes
+ }
+ }
+}
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 39514fc44e..2a41cafba9 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -27,6 +27,8 @@ pub mod display;
pub mod method_resolution;
pub mod primitive;
pub mod traits;
+pub mod layout;
+pub mod lang_items;
#[cfg(test)]
mod tests;
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 5f36ce62f8..316f3938c6 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -39,12 +39,13 @@ use arrayvec::ArrayVec;
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
use either::Either;
use hir_def::{
- adt::{ReprData, VariantData},
+ adt::VariantData,
body::{BodyDiagnostic, SyntheticSyntax},
expr::{BindingAnnotation, LabelId, Pat, PatId},
generics::{TypeOrConstParamData, TypeParamProvenance},
item_tree::ItemTreeNode,
lang_item::LangItemTarget,
+ layout::{Layout, LayoutError, ReprOptions},
nameres::{self, diagnostics::DefDiagnostic},
per_ns::PerNs,
resolver::{HasResolver, Resolver},
@@ -59,6 +60,7 @@ use hir_ty::{
all_super_traits, autoderef,
consteval::{unknown_const_as_generic, ComputedExpr, ConstEvalError, ConstExt},
diagnostics::BodyValidationDiagnostic,
+ layout::layout_of_ty,
method_resolution::{self, TyFingerprint},
primitive::UintTy,
traits::FnTrait,
@@ -844,6 +846,10 @@ impl Field {
self.parent.variant_data(db).fields()[self.id].name.clone()
}
+ pub fn index(&self) -> usize {
+ u32::from(self.id.into_raw()) as usize
+ }
+
/// Returns the type as in the signature of the struct (i.e., with
/// placeholder types for type parameters). Only use this in the context of
/// the field definition.
@@ -859,6 +865,10 @@ impl Field {
Type::new(db, var_id, ty)
}
+ pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+ layout_of_ty(db, &self.ty(db).ty)
+ }
+
pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef {
self.parent
}
@@ -900,7 +910,7 @@ impl Struct {
Type::from_def(db, self.id)
}
- pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprData> {
+ pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> {
db.struct_data(self.id).repr.clone()
}
@@ -984,8 +994,30 @@ impl Enum {
Type::new_for_crate(
self.id.lookup(db.upcast()).container.krate(),
TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() {
- Either::Left(builtin) => hir_def::builtin_type::BuiltinType::Int(builtin),
- Either::Right(builtin) => hir_def::builtin_type::BuiltinType::Uint(builtin),
+ hir_def::layout::IntegerType::Pointer(sign) => match sign {
+ true => hir_def::builtin_type::BuiltinType::Int(
+ hir_def::builtin_type::BuiltinInt::Isize,
+ ),
+ false => hir_def::builtin_type::BuiltinType::Uint(
+ hir_def::builtin_type::BuiltinUint::Usize,
+ ),
+ },
+ hir_def::layout::IntegerType::Fixed(i, sign) => match sign {
+ true => hir_def::builtin_type::BuiltinType::Int(match i {
+ hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8,
+ hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16,
+ hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32,
+ hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64,
+ hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128,
+ }),
+ false => hir_def::builtin_type::BuiltinType::Uint(match i {
+ hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8,
+ hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16,
+ hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32,
+ hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64,
+ hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128,
+ }),
+ },
}),
)
}
@@ -1076,6 +1108,13 @@ impl Adt {
})
}
+ pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+ if db.generic_params(self.into()).iter().count() != 0 {
+ return Err(LayoutError::HasPlaceholder);
+ }
+ db.layout_of_adt(self.into(), Substitution::empty(Interner))
+ }
+
/// Turns this ADT into a type. Any type parameters of the ADT will be
/// turned into unknown types, which is good for e.g. finding the most
/// general set of completions, but will not look very nice when printed.
@@ -3033,7 +3072,7 @@ impl Type {
let adt = adt_id.into();
match adt {
- Adt::Struct(s) => matches!(s.repr(db), Some(ReprData { packed: true, .. })),
+ Adt::Struct(s) => s.repr(db).unwrap_or_default().pack.is_some(),
_ => false,
}
}
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index fb00a40f96..f37c9f4a6d 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -2,7 +2,9 @@
use std::fmt::Display;
use either::Either;
-use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
+use hir::{
+ Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo,
+};
use ide_db::{
base_db::SourceDatabase,
defs::Definition,
@@ -388,10 +390,30 @@ pub(super) fn definition(
let mod_path = definition_mod_path(db, &def);
let (label, docs) = match def {
Definition::Macro(it) => label_and_docs(db, it),
- Definition::Field(it) => label_and_docs(db, it),
+ Definition::Field(it) => label_and_layout_info_and_docs(db, it, |&it| {
+ let var_def = it.parent_def(db);
+ let id = it.index();
+ let layout = it.layout(db).ok()?;
+ let offset = match var_def {
+ hir::VariantDef::Struct(s) => {
+ let layout = Adt::from(s).layout(db).ok()?;
+ layout.fields.offset(id)
+ }
+ _ => return None,
+ };
+ Some(format!(
+ "size = {}, align = {}, offset = {}",
+ layout.size.bytes(),
+ layout.align.abi.bytes(),
+ offset.bytes()
+ ))
+ }),
Definition::Module(it) => label_and_docs(db, it),
Definition::Function(it) => label_and_docs(db, it),
- Definition::Adt(it) => label_and_docs(db, it),
+ Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
+ let layout = it.layout(db).ok()?;
+ Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
+ }),
Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
if !it.parent_enum(db).is_data_carrying(db) {
match it.eval(db) {
@@ -489,6 +511,25 @@ where
(label, docs)
}
+fn label_and_layout_info_and_docs<D, E, V>(
+ db: &RootDatabase,
+ def: D,
+ value_extractor: E,
+) -> (String, Option<hir::Documentation>)
+where
+ D: HasAttrs + HirDisplay,
+ E: Fn(&D) -> Option<V>,
+ V: Display,
+{
+ let label = if let Some(value) = value_extractor(&def) {
+ format!("{} // {}", def.display(db), value)
+ } else {
+ def.display(db).to_string()
+ };
+ let docs = def.attrs(db).docs();
+ (label, docs)
+}
+
fn label_value_and_docs<D, E, V>(
db: &RootDatabase,
def: D,
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index f8be4cfb04..f82fd6d028 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -523,6 +523,27 @@ fn main() { }
}
#[test]
+fn hover_field_offset() {
+ // Hovering over the field when instantiating
+ check(
+ r#"
+struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }
+"#,
+ expect![[r#"
+ *field_a*
+
+ ```rust
+ test::Foo
+ ```
+
+ ```rust
+ field_a: u8 // size = 1, align = 1, offset = 4
+ ```
+ "#]],
+ );
+}
+
+#[test]
fn hover_shows_struct_field_info() {
// Hovering over the field when instantiating
check(
@@ -534,16 +555,16 @@ fn main() {
}
"#,
expect![[r#"
- *field_a*
+ *field_a*
- ```rust
- test::Foo
- ```
+ ```rust
+ test::Foo
+ ```
- ```rust
- field_a: u32
- ```
- "#]],
+ ```rust
+ field_a: u32 // size = 4, align = 4, offset = 0
+ ```
+ "#]],
);
// Hovering over the field in the definition
@@ -556,16 +577,16 @@ fn main() {
}
"#,
expect![[r#"
- *field_a*
+ *field_a*
- ```rust
- test::Foo
- ```
+ ```rust
+ test::Foo
+ ```
- ```rust
- field_a: u32
- ```
- "#]],
+ ```rust
+ field_a: u32 // size = 4, align = 4, offset = 0
+ ```
+ "#]],
);
}
@@ -1508,30 +1529,30 @@ struct Bar;
fn foo() { let bar = Ba$0r; }
"#,
- expect![[r##"
- *Bar*
+ expect![[r#"
+ *Bar*
- ```rust
- test
- ```
+ ```rust
+ test
+ ```
- ```rust
- struct Bar
- ```
+ ```rust
+ struct Bar // size = 0, align = 1
+ ```
- ---
+ ---
- This is an example
- multiline doc
+ This is an example
+ multiline doc
- # Example
+ # Example
- ```
- let five = 5;
+ ```
+ let five = 5;
- assert_eq!(6, my_crate::add_one(5));
- ```
- "##]],
+ assert_eq!(6, my_crate::add_one(5));
+ ```
+ "#]],
);
}
@@ -1545,20 +1566,20 @@ struct Bar;
fn foo() { let bar = Ba$0r; }
"#,
expect![[r#"
- *Bar*
+ *Bar*
- ```rust
- test
- ```
+ ```rust
+ test
+ ```
- ```rust
- struct Bar
- ```
+ ```rust
+ struct Bar // size = 0, align = 1
+ ```
- ---
+ ---
- bar docs
- "#]],
+ bar docs
+ "#]],
);
}
@@ -1574,22 +1595,22 @@ struct Bar;
fn foo() { let bar = Ba$0r; }
"#,
expect![[r#"
- *Bar*
+ *Bar*
- ```rust
- test
- ```
+ ```rust
+ test
+ ```
- ```rust
- struct Bar
- ```
+ ```rust
+ struct Bar // size = 0, align = 1
+ ```
- ---
+ ---
- bar docs 0
- bar docs 1
- bar docs 2
- "#]],
+ bar docs 0
+ bar docs 1
+ bar docs 2
+ "#]],
);
}
@@ -1602,20 +1623,20 @@ pub struct Foo;
pub struct B$0ar
"#,
expect![[r#"
- *Bar*
+ *Bar*
- ```rust
- test
- ```
+ ```rust
+ test
+ ```
- ```rust
- pub struct Bar
- ```
+ ```rust
+ pub struct Bar // size = 0, align = 1
+ ```
- ---
+ ---
- [external](https://www.google.com)
- "#]],
+ [external](https://www.google.com)
+ "#]],
);
}
@@ -1629,20 +1650,20 @@ pub struct Foo;
pub struct B$0ar
"#,
expect![[r#"
- *Bar*
+ *Bar*
- ```rust
- test
- ```
+ ```rust
+ test
+ ```
- ```rust
- pub struct Bar
- ```
+ ```rust
+ pub struct Bar // size = 0, align = 1
+ ```
- ---
+ ---
- [baz](Baz)
- "#]],
+ [baz](Baz)
+ "#]],
);
}
@@ -2960,7 +2981,7 @@ fn main() {
```
```rust
- f: i32
+ f: i32 // size = 4, align = 4, offset = 0
```
"#]],
);
@@ -4203,20 +4224,20 @@ pub fn gimme() -> theitem::TheItem {
}
"#,
expect![[r#"
- *[`TheItem`]*
+ *[`TheItem`]*
- ```rust
- test::theitem
- ```
+ ```rust
+ test::theitem
+ ```
- ```rust
- pub struct TheItem
- ```
+ ```rust
+ pub struct TheItem // size = 0, align = 1
+ ```
- ---
+ ---
- This is the item. Cool!
- "#]],
+ This is the item. Cool!
+ "#]],
);
}
@@ -4351,20 +4372,20 @@ mod string {
}
"#,
expect![[r#"
- *String*
+ *String*
- ```rust
- main
- ```
+ ```rust
+ main
+ ```
- ```rust
- struct String
- ```
+ ```rust
+ struct String // size = 0, align = 1
+ ```
- ---
+ ---
- Custom `String` type.
- "#]],
+ Custom `String` type.
+ "#]],
)
}
@@ -5025,7 +5046,7 @@ foo_macro!(
```
```rust
- pub struct Foo
+ pub struct Foo // size = 0, align = 1
```
---
@@ -5040,7 +5061,7 @@ fn hover_intra_in_attr() {
check(
r#"
#[doc = "Doc comment for [`Foo$0`]"]
-pub struct Foo;
+pub struct Foo(i32);
"#,
expect![[r#"
*[`Foo`]*
@@ -5050,7 +5071,7 @@ pub struct Foo;
```
```rust
- pub struct Foo
+ pub struct Foo // size = 4, align = 4
```
---
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 013fd6b4fd..750b64fea6 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -30,6 +30,7 @@
//! index: sized
//! iterator: option
//! iterators: iterator, fn
+//! non_zero:
//! option:
//! ord: eq, option
//! pin:
@@ -704,6 +705,15 @@ mod macros {
}
// endregion:derive
+// region:non_zero
+pub mod num {
+ #[repr(transparent)]
+ #[rustc_layout_scalar_valid_range_start(1)]
+ #[rustc_nonnull_optimization_guaranteed]
+ pub struct NonZeroU8(u8);
+}
+// endregion:non_zero
+
// region:bool_impl
#[lang = "bool"]
impl bool {
diff --git a/lib/la-arena/src/map.rs b/lib/la-arena/src/map.rs
index 5f347e2745..b9d491da3c 100644
--- a/lib/la-arena/src/map.rs
+++ b/lib/la-arena/src/map.rs
@@ -86,6 +86,14 @@ impl<T, V> ArenaMap<Idx<T>, V> {
self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
}
+ /// Returns an iterator over the arena indexes and values in the map.
+ pub fn iter_mut(&mut self) -> impl Iterator<Item = (Idx<T>, &mut V)> {
+ self.v
+ .iter_mut()
+ .enumerate()
+ .filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_mut()?)))
+ }
+
/// Gets the given key's corresponding entry in the map for in-place manipulation.
pub fn entry(&mut self, idx: Idx<T>) -> Entry<'_, Idx<T>, V> {
let idx = Self::to_idx(idx);