diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index 0b6a51fe76..0e4b033638 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -13,7 +13,7 @@ use hir_expand::{ }; use intern::Interned; use la_arena::{Arena, ArenaMap}; -use rustc_abi::{Integer, IntegerType}; +use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; use syntax::ast::{self, HasName, HasVisibility}; use crate::{ @@ -22,7 +22,6 @@ use crate::{ db::DefDatabase, item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId}, lang_item::LangItem, - layout::{Align, ReprFlags, ReprOptions}, nameres::diagnostics::DefDiagnostic, src::HasChildSource, src::HasSource, diff --git a/crates/hir-def/src/layout.rs b/crates/hir-def/src/layout.rs deleted file mode 100644 index 873936b5b7..0000000000 --- a/crates/hir-def/src/layout.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! 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, TargetDataLayoutErrors, 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; -pub type TagEncoding = rustc_abi::TagEncoding; -pub type Variants = rustc_abi::Variants; - -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, - TargetLayoutNotAvailable, - HasPlaceholder, - HasErrorType, - NotImplemented, - Unknown, -} diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index ec9a8e8006..5a0c1b66b9 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -29,7 +29,6 @@ pub mod item_tree; pub mod data; pub mod generics; pub mod lang_item; -pub mod layout; pub mod hir; pub use self::hir::type_ref; @@ -46,6 +45,8 @@ pub mod visibility; pub mod find_path; pub mod import_map; +pub use rustc_abi as layout; + #[cfg(test)] mod test_db; #[cfg(test)] diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs index c67c29818f..8cd287f7f3 100644 --- a/crates/hir-def/src/path.rs +++ b/crates/hir-def/src/path.rs @@ -9,14 +9,12 @@ use std::{ use crate::{ body::LowerCtx, lang_item::LangItemTarget, - type_ref::{ConstRefOrPath, LifetimeRef}, + type_ref::{ConstRefOrPath, LifetimeRef, TypeBound, TypeRef}, }; use hir_expand::name::Name; use intern::Interned; use syntax::ast; -use crate::type_ref::{TypeBound, TypeRef}; - pub use hir_expand::mod_path::{path, ModPath, PathKind}; #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index bd2dc0bc13..c415689399 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -5,11 +5,9 @@ use std::sync::Arc; use base_db::{impl_intern_key, salsa, CrateId, Upcast}; use hir_def::{ - db::DefDatabase, - hir::ExprId, - layout::{Layout, LayoutError, TargetDataLayout}, - AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId, - ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId, + db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstId, ConstParamId, + DefWithBodyId, EnumVariantId, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, + TypeOrConstParamId, VariantId, }; use la_arena::ArenaMap; use smallvec::SmallVec; @@ -17,6 +15,7 @@ use smallvec::SmallVec; use crate::{ chalk_db, consteval::ConstEvalError, + layout::{Layout, LayoutError}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index d90ca77b55..597bf4b9a8 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -18,11 +18,11 @@ use std::{convert::identity, ops::Index}; use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags}; use either::Either; -use hir_def::hir::LabelId; use hir_def::{ body::Body, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, data::{ConstData, StaticData}, + hir::LabelId, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId}, lang_item::{LangItem, LangItemTarget}, layout::Integer, diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 23cad5e6fd..512836afbf 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -4,16 +4,19 @@ use base_db::CrateId; use chalk_ir::{AdtId, TyKind}; use hir_def::{ layout::{ - Abi, FieldsShape, Integer, Layout, LayoutCalculator, LayoutError, Primitive, ReprOptions, - RustcEnumVariantIdx, Scalar, Size, StructKind, TargetDataLayout, Variants, WrappingRange, + Abi, FieldsShape, Integer, LayoutCalculator, LayoutS, Primitive, ReprOptions, Scalar, Size, + StructKind, TargetDataLayout, WrappingRange, }, - LocalFieldId, + LocalEnumVariantId, LocalFieldId, }; +use la_arena::{Idx, RawIdx}; use stdx::never; -use crate::{consteval::try_const_usize, db::HirDatabase, Interner, Substitution, Ty}; +use crate::{ + consteval::try_const_usize, db::HirDatabase, layout::adt::struct_variant_idx, Interner, + Substitution, Ty, +}; -use self::adt::struct_variant_idx; pub use self::{ adt::{layout_of_adt_query, layout_of_adt_recover}, target::target_data_layout_query, @@ -28,6 +31,34 @@ macro_rules! user_error { mod adt; mod target; +#[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 = LayoutS; +pub type TagEncoding = hir_def::layout::TagEncoding; +pub type Variants = hir_def::layout::Variants; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum LayoutError { + UserError(String), + SizeOverflow, + TargetLayoutNotAvailable, + HasPlaceholder, + HasErrorType, + NotImplemented, + Unknown, +} + struct LayoutCx<'a> { krate: CrateId, target: &'a TargetDataLayout, @@ -45,14 +76,6 @@ impl<'a> LayoutCalculator for LayoutCx<'a> { } } -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, krate: CrateId) -> Result { let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) }; let cx = LayoutCx { krate, target: &target }; @@ -287,5 +310,13 @@ fn field_ty( db.field_types(def)[fd].clone().substitute(Interner, subst) } +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)) +} + #[cfg(test)] mod tests; diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 3f3017deee..9dbf9b2419 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -1,16 +1,21 @@ //! Compute the binary representation of structs, unions and enums -use std::ops::Bound; +use std::{cmp, ops::Bound}; use hir_def::{ data::adt::VariantData, - layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx}, + layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout}, AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId, }; use la_arena::RawIdx; use smallvec::SmallVec; -use crate::{db::HirDatabase, lang_items::is_unsafe_cell, layout::field_ty, Substitution}; +use crate::{ + db::HirDatabase, + lang_items::is_unsafe_cell, + layout::{field_ty, Layout, LayoutError, RustcEnumVariantIdx}, + Substitution, +}; use super::{layout_of_ty, LayoutCx}; @@ -73,7 +78,7 @@ pub fn layout_of_adt_query( is_enum, is_unsafe_cell(db, def), layout_scalar_valid_range(db, def), - |min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)), + |min, max| 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 = @@ -125,3 +130,50 @@ pub fn layout_of_adt_recover( ) -> Result { user_error!("infinite sized recursive type"); } + +/// 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) + }) +} diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index a0ffab5518..3cd0cd399c 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -2,12 +2,14 @@ use std::collections::HashMap; use base_db::fixture::WithFixture; use chalk_ir::{AdtId, TyKind}; -use hir_def::{ - db::DefDatabase, - layout::{Layout, LayoutError}, -}; +use hir_def::db::DefDatabase; -use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution}; +use crate::{ + db::HirDatabase, + layout::{Layout, LayoutError}, + test_db::TestDB, + Interner, Substitution, +}; use super::layout_of_ty; diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 8c911b7f7b..b39718fbdc 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -10,7 +10,7 @@ use chalk_ir::{ use hir_def::{ builtin_type::BuiltinType, lang_item::{lang_attr, LangItem}, - layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding, Variants}, + layout::{TagEncoding, Variants}, AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, VariantId, }; use intern::Interned; @@ -21,7 +21,7 @@ use crate::{ db::HirDatabase, from_placeholder_idx, infer::{normalize, PointerCast}, - layout::layout_of_ty, + layout::{layout_of_ty, Layout, LayoutError, RustcEnumVariantIdx}, mapping::from_chalk, method_resolution::{is_dyn_method, lookup_impl_method}, traits::FnTrait, diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 78a2d90f7f..1f88cc57d0 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -11,7 +11,6 @@ use hir_def::{ RecordFieldPat, RecordLitField, }, lang_item::{LangItem, LangItemTarget}, - layout::LayoutError, path::Path, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, AdtId, DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, LocalFieldId, TraitId, @@ -26,7 +25,7 @@ use crate::{ display::HirDisplay, infer::{CaptureKind, CapturedItem, TypeMismatch}, inhabitedness::is_ty_uninhabited_from, - layout::layout_of_ty, + layout::{layout_of_ty, LayoutError}, mapping::ToChalk, static_lifetime, utils::generics, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index e161c94a0e..3e568a4021 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -45,7 +45,7 @@ use hir_def::{ hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, item_tree::ItemTreeNode, lang_item::{LangItem, LangItemTarget}, - layout::{Layout, LayoutError, ReprOptions}, + layout::ReprOptions, nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin}, per_ns::PerNs, resolver::{HasResolver, Resolver}, @@ -61,7 +61,7 @@ use hir_ty::{ consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, display::HexifiedConst, - layout::layout_of_ty, + layout::{layout_of_ty, Layout, LayoutError}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, primitive::UintTy,