mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
441 lines
15 KiB
Rust
441 lines
15 KiB
Rust
//! Compute the binary representation of a type
|
|
|
|
use std::fmt;
|
|
|
|
use hir_def::{
|
|
AdtId, LocalFieldId, StructId,
|
|
layout::{LayoutCalculatorError, LayoutData},
|
|
};
|
|
use la_arena::{Idx, RawIdx};
|
|
|
|
use rustc_abi::{
|
|
AddressSpace, Float, Integer, LayoutCalculator, Primitive, ReprOptions, Scalar, StructKind,
|
|
TargetDataLayout, WrappingRange,
|
|
};
|
|
use rustc_index::IndexVec;
|
|
use rustc_type_ir::{
|
|
FloatTy, IntTy, UintTy,
|
|
inherent::{IntoKind, SliceLike},
|
|
};
|
|
use triomphe::Arc;
|
|
|
|
use crate::{
|
|
TraitEnvironment,
|
|
consteval_nextsolver::try_const_usize,
|
|
db::HirDatabase,
|
|
next_solver::{
|
|
DbInterner, GenericArgs, ParamEnv, SolverDefId, Ty, TyKind, TypingMode,
|
|
infer::{DbInternerInferExt, traits::ObligationCause},
|
|
mapping::{ChalkToNextSolver, convert_binder_to_early_binder},
|
|
project::solve_normalize::deeply_normalize,
|
|
},
|
|
};
|
|
|
|
pub(crate) use self::adt::{layout_of_adt_cycle_result, layout_of_adt_ns_cycle_result};
|
|
pub use self::{
|
|
adt::{layout_of_adt_ns_query, layout_of_adt_query},
|
|
target::target_data_layout_query,
|
|
};
|
|
|
|
pub(crate) mod adt;
|
|
pub(crate) mod target;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct RustcEnumVariantIdx(pub usize);
|
|
|
|
impl rustc_index::Idx for RustcEnumVariantIdx {
|
|
fn new(idx: usize) -> Self {
|
|
RustcEnumVariantIdx(idx)
|
|
}
|
|
|
|
fn index(self) -> usize {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct RustcFieldIdx(pub LocalFieldId);
|
|
|
|
impl RustcFieldIdx {
|
|
pub fn new(idx: usize) -> Self {
|
|
RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32)))
|
|
}
|
|
}
|
|
|
|
impl rustc_index::Idx for RustcFieldIdx {
|
|
fn new(idx: usize) -> Self {
|
|
RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32)))
|
|
}
|
|
|
|
fn index(self) -> usize {
|
|
u32::from(self.0.into_raw()) as usize
|
|
}
|
|
}
|
|
|
|
pub type Layout = LayoutData<RustcFieldIdx, RustcEnumVariantIdx>;
|
|
pub type TagEncoding = hir_def::layout::TagEncoding<RustcEnumVariantIdx>;
|
|
pub type Variants = hir_def::layout::Variants<RustcFieldIdx, RustcEnumVariantIdx>;
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
pub enum LayoutError {
|
|
// FIXME: Remove more variants once they get added to LayoutCalculatorError
|
|
BadCalc(LayoutCalculatorError<()>),
|
|
HasErrorConst,
|
|
HasErrorType,
|
|
HasPlaceholder,
|
|
InvalidSimdType,
|
|
NotImplemented,
|
|
RecursiveTypeWithoutIndirection,
|
|
TargetLayoutNotAvailable,
|
|
Unknown,
|
|
UserReprTooSmall,
|
|
}
|
|
|
|
impl std::error::Error for LayoutError {}
|
|
impl fmt::Display for LayoutError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
LayoutError::BadCalc(err) => err.fallback_fmt(f),
|
|
LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"),
|
|
LayoutError::HasErrorType => write!(f, "type contains an error"),
|
|
LayoutError::HasPlaceholder => write!(f, "type contains placeholders"),
|
|
LayoutError::InvalidSimdType => write!(f, "invalid simd type definition"),
|
|
LayoutError::NotImplemented => write!(f, "not implemented"),
|
|
LayoutError::RecursiveTypeWithoutIndirection => {
|
|
write!(f, "recursive type without indirection")
|
|
}
|
|
LayoutError::TargetLayoutNotAvailable => write!(f, "target layout not available"),
|
|
LayoutError::Unknown => write!(f, "unknown"),
|
|
LayoutError::UserReprTooSmall => {
|
|
write!(f, "the `#[repr]` hint is too small to hold the discriminants of the enum")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<F> From<LayoutCalculatorError<F>> for LayoutError {
|
|
fn from(err: LayoutCalculatorError<F>) -> Self {
|
|
LayoutError::BadCalc(err.without_payload())
|
|
}
|
|
}
|
|
|
|
struct LayoutCx<'a> {
|
|
calc: LayoutCalculator<&'a TargetDataLayout>,
|
|
}
|
|
|
|
impl<'a> LayoutCx<'a> {
|
|
fn new(target: &'a TargetDataLayout) -> Self {
|
|
Self { calc: LayoutCalculator::new(target) }
|
|
}
|
|
}
|
|
|
|
// FIXME: move this to the `rustc_abi`.
|
|
fn layout_of_simd_ty<'db>(
|
|
db: &'db dyn HirDatabase,
|
|
id: StructId,
|
|
repr_packed: bool,
|
|
args: &GenericArgs<'db>,
|
|
env: Arc<TraitEnvironment>,
|
|
dl: &TargetDataLayout,
|
|
) -> Result<Arc<Layout>, LayoutError> {
|
|
// Supported SIMD vectors are homogeneous ADTs with exactly one array field:
|
|
//
|
|
// * #[repr(simd)] struct S([T; 4])
|
|
//
|
|
// where T is a primitive scalar (integer/float/pointer).
|
|
let fields = db.field_types_ns(id.into());
|
|
let mut fields = fields.iter();
|
|
let Some(TyKind::Array(e_ty, e_len)) = fields
|
|
.next()
|
|
.filter(|_| fields.next().is_none())
|
|
.map(|f| (*f.1).instantiate(DbInterner::new_with(db, None, None), args).kind())
|
|
else {
|
|
return Err(LayoutError::InvalidSimdType);
|
|
};
|
|
|
|
let e_len = try_const_usize(db, &e_len).ok_or(LayoutError::HasErrorConst)? as u64;
|
|
let e_ly = db.layout_of_ty_ns(e_ty, env)?;
|
|
|
|
let cx = LayoutCx::new(dl);
|
|
Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?))
|
|
}
|
|
|
|
pub fn layout_of_ty_query(
|
|
db: &dyn HirDatabase,
|
|
ty: crate::Ty,
|
|
trait_env: Arc<TraitEnvironment>,
|
|
) -> Result<Arc<Layout>, LayoutError> {
|
|
let krate = trait_env.krate;
|
|
let interner = DbInterner::new_with(db, Some(krate), trait_env.block);
|
|
db.layout_of_ty_ns(ty.to_nextsolver(interner), trait_env)
|
|
}
|
|
|
|
pub fn layout_of_ty_ns_query<'db>(
|
|
db: &'db dyn HirDatabase,
|
|
ty: Ty<'db>,
|
|
trait_env: Arc<TraitEnvironment>,
|
|
) -> Result<Arc<Layout>, LayoutError> {
|
|
let krate = trait_env.krate;
|
|
let interner = DbInterner::new_with(db, Some(krate), trait_env.block);
|
|
let Ok(target) = db.target_data_layout(krate) else {
|
|
return Err(LayoutError::TargetLayoutNotAvailable);
|
|
};
|
|
let dl = &*target;
|
|
let cx = LayoutCx::new(dl);
|
|
let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis);
|
|
let cause = ObligationCause::dummy();
|
|
let ty = deeply_normalize(infer_ctxt.at(&cause, ParamEnv::empty()), ty).unwrap_or(ty);
|
|
let result = match ty.kind() {
|
|
TyKind::Adt(def, args) => {
|
|
match def.inner().id {
|
|
hir_def::AdtId::StructId(s) => {
|
|
let data = db.struct_signature(s);
|
|
let repr = data.repr.unwrap_or_default();
|
|
if repr.simd() {
|
|
return layout_of_simd_ty(db, s, repr.packed(), &args, trait_env, &target);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
return db.layout_of_adt_ns(def.inner().id, args, trait_env);
|
|
}
|
|
TyKind::Bool => Layout::scalar(
|
|
dl,
|
|
Scalar::Initialized {
|
|
value: Primitive::Int(Integer::I8, false),
|
|
valid_range: WrappingRange { start: 0, end: 1 },
|
|
},
|
|
),
|
|
TyKind::Char => Layout::scalar(
|
|
dl,
|
|
Scalar::Initialized {
|
|
value: Primitive::Int(Integer::I32, false),
|
|
valid_range: WrappingRange { start: 0, end: 0x10FFFF },
|
|
},
|
|
),
|
|
TyKind::Int(i) => Layout::scalar(
|
|
dl,
|
|
scalar_unit(
|
|
dl,
|
|
Primitive::Int(
|
|
match i {
|
|
IntTy::Isize => dl.ptr_sized_integer(),
|
|
IntTy::I8 => Integer::I8,
|
|
IntTy::I16 => Integer::I16,
|
|
IntTy::I32 => Integer::I32,
|
|
IntTy::I64 => Integer::I64,
|
|
IntTy::I128 => Integer::I128,
|
|
},
|
|
true,
|
|
),
|
|
),
|
|
),
|
|
TyKind::Uint(i) => Layout::scalar(
|
|
dl,
|
|
scalar_unit(
|
|
dl,
|
|
Primitive::Int(
|
|
match i {
|
|
UintTy::Usize => dl.ptr_sized_integer(),
|
|
UintTy::U8 => Integer::I8,
|
|
UintTy::U16 => Integer::I16,
|
|
UintTy::U32 => Integer::I32,
|
|
UintTy::U64 => Integer::I64,
|
|
UintTy::U128 => Integer::I128,
|
|
},
|
|
false,
|
|
),
|
|
),
|
|
),
|
|
TyKind::Float(f) => Layout::scalar(
|
|
dl,
|
|
scalar_unit(
|
|
dl,
|
|
Primitive::Float(match f {
|
|
FloatTy::F16 => Float::F16,
|
|
FloatTy::F32 => Float::F32,
|
|
FloatTy::F64 => Float::F64,
|
|
FloatTy::F128 => Float::F128,
|
|
}),
|
|
),
|
|
),
|
|
TyKind::Tuple(tys) => {
|
|
let kind =
|
|
if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
|
|
|
|
let fields = tys
|
|
.iter()
|
|
.map(|k| db.layout_of_ty_ns(k, trait_env.clone()))
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
|
|
let fields = fields.iter().collect::<IndexVec<_, _>>();
|
|
cx.calc.univariant(&fields, &ReprOptions::default(), kind)?
|
|
}
|
|
TyKind::Array(element, count) => {
|
|
let count = try_const_usize(db, &count).ok_or(LayoutError::HasErrorConst)? as u64;
|
|
let element = db.layout_of_ty_ns(element, trait_env)?;
|
|
cx.calc.array_like::<_, _, ()>(&element, Some(count))?
|
|
}
|
|
TyKind::Slice(element) => {
|
|
let element = db.layout_of_ty_ns(element, trait_env)?;
|
|
cx.calc.array_like::<_, _, ()>(&element, None)?
|
|
}
|
|
TyKind::Str => {
|
|
let element = scalar_unit(dl, Primitive::Int(Integer::I8, false));
|
|
cx.calc.array_like::<_, _, ()>(&Layout::scalar(dl, element), None)?
|
|
}
|
|
// Potentially-wide pointers.
|
|
TyKind::Ref(_, pointee, _) | TyKind::RawPtr(pointee, _) => {
|
|
let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
|
|
if matches!(ty.kind(), TyKind::Ref(..)) {
|
|
data_ptr.valid_range_mut().start = 1;
|
|
}
|
|
|
|
// FIXME(next-solver)
|
|
// let pointee = tcx.normalize_erasing_regions(param_env, pointee);
|
|
// if pointee.is_sized(tcx.at(DUMMY_SP), param_env) {
|
|
// return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
|
|
// }
|
|
|
|
let unsized_part = struct_tail_erasing_lifetimes(db, pointee);
|
|
// FIXME(next-solver)
|
|
/*
|
|
if let TyKind::AssociatedType(id, subst) = unsized_part.kind(Interner) {
|
|
unsized_part = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
|
|
associated_ty_id: *id,
|
|
substitution: subst.clone(),
|
|
}))
|
|
.intern(Interner);
|
|
}
|
|
unsized_part = normalize(db, trait_env, unsized_part);
|
|
*/
|
|
let metadata = match unsized_part.kind() {
|
|
TyKind::Slice(_) | TyKind::Str => {
|
|
scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
|
|
}
|
|
TyKind::Dynamic(..) => {
|
|
let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
|
|
vtable.valid_range_mut().start = 1;
|
|
vtable
|
|
}
|
|
_ => {
|
|
// pointee is sized
|
|
return Ok(Arc::new(Layout::scalar(dl, data_ptr)));
|
|
}
|
|
};
|
|
|
|
// Effectively a (ptr, meta) tuple.
|
|
LayoutData::scalar_pair(dl, data_ptr, metadata)
|
|
}
|
|
TyKind::Never => LayoutData::never_type(dl),
|
|
TyKind::FnDef(..) => LayoutData::unit(dl, true),
|
|
TyKind::Dynamic(..) | TyKind::Foreign(_) => LayoutData::unit(dl, false),
|
|
TyKind::FnPtr(..) => {
|
|
let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space));
|
|
ptr.valid_range_mut().start = 1;
|
|
Layout::scalar(dl, ptr)
|
|
}
|
|
TyKind::Closure(c, args) => {
|
|
let id = match c {
|
|
SolverDefId::InternedClosureId(id) => id,
|
|
_ => unreachable!(),
|
|
};
|
|
let def = db.lookup_intern_closure(id);
|
|
let infer = db.infer(def.0);
|
|
let (captures, _) = infer.closure_info(&id.into());
|
|
let fields = captures
|
|
.iter()
|
|
.map(|it| {
|
|
let ty =
|
|
convert_binder_to_early_binder(interner, it.ty.to_nextsolver(interner))
|
|
.instantiate(interner, args);
|
|
db.layout_of_ty_ns(ty, trait_env.clone())
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
|
|
let fields = fields.iter().collect::<IndexVec<_, _>>();
|
|
cx.calc.univariant(&fields, &ReprOptions::default(), StructKind::AlwaysSized)?
|
|
}
|
|
|
|
TyKind::Coroutine(_, _)
|
|
| TyKind::CoroutineWitness(_, _)
|
|
| TyKind::CoroutineClosure(_, _) => {
|
|
return Err(LayoutError::NotImplemented);
|
|
}
|
|
|
|
TyKind::Pat(_, _) | TyKind::UnsafeBinder(_) => {
|
|
return Err(LayoutError::NotImplemented);
|
|
}
|
|
|
|
TyKind::Error(_) => return Err(LayoutError::HasErrorType),
|
|
TyKind::Placeholder(_)
|
|
| TyKind::Bound(..)
|
|
| TyKind::Infer(..)
|
|
| TyKind::Param(..)
|
|
| TyKind::Alias(..) => {
|
|
return Err(LayoutError::HasPlaceholder);
|
|
}
|
|
};
|
|
Ok(Arc::new(result))
|
|
}
|
|
|
|
pub(crate) fn layout_of_ty_cycle_result(
|
|
_: &dyn HirDatabase,
|
|
_: crate::Ty,
|
|
_: Arc<TraitEnvironment>,
|
|
) -> Result<Arc<Layout>, LayoutError> {
|
|
Err(LayoutError::RecursiveTypeWithoutIndirection)
|
|
}
|
|
|
|
pub(crate) fn layout_of_ty_ns_cycle_result<'db>(
|
|
_: &dyn HirDatabase,
|
|
_: Ty<'db>,
|
|
_: Arc<TraitEnvironment>,
|
|
) -> Result<Arc<Layout>, LayoutError> {
|
|
Err(LayoutError::RecursiveTypeWithoutIndirection)
|
|
}
|
|
|
|
fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> {
|
|
match pointee.kind() {
|
|
TyKind::Adt(def, args) => {
|
|
let struct_id = match def.inner().id {
|
|
AdtId::StructId(id) => id,
|
|
_ => return pointee,
|
|
};
|
|
let data = struct_id.fields(db);
|
|
let mut it = data.fields().iter().rev();
|
|
match it.next() {
|
|
Some((f, _)) => {
|
|
let last_field_ty = field_ty(db, struct_id.into(), f, &args);
|
|
struct_tail_erasing_lifetimes(db, last_field_ty)
|
|
}
|
|
None => pointee,
|
|
}
|
|
}
|
|
TyKind::Tuple(tys) => {
|
|
if let Some(last_field_ty) = tys.iter().last() {
|
|
struct_tail_erasing_lifetimes(db, last_field_ty)
|
|
} else {
|
|
pointee
|
|
}
|
|
}
|
|
_ => pointee,
|
|
}
|
|
}
|
|
|
|
fn field_ty<'a>(
|
|
db: &'a dyn HirDatabase,
|
|
def: hir_def::VariantId,
|
|
fd: LocalFieldId,
|
|
args: &GenericArgs<'a>,
|
|
) -> Ty<'a> {
|
|
db.field_types_ns(def)[fd].instantiate(DbInterner::new_with(db, None, None), args)
|
|
}
|
|
|
|
fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
|
|
Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|