diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index c144f0b7dbc5b..f686da8e2b34c 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -443,6 +443,7 @@ language_item_table! { FieldBase, sym::field_base, field_base, Target::AssocTy, GenericRequirement::Exact(0); FieldType, sym::field_type, field_type, Target::AssocTy, GenericRequirement::Exact(0); FieldOffset, sym::field_offset, field_offset, Target::AssocConst, GenericRequirement::Exact(0); + AlignType, sym::align_type, align_type, Target::Struct, GenericRequirement::Exact(1); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 30bf8dd7c2206..611cee1b213c2 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -374,6 +374,7 @@ symbols! { align, align_of, align_of_val, + align_type, alignment, all, alloc, diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 391f50edf23fa..5cb10121d55df 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -686,10 +686,25 @@ fn layout_of_uncached<'tcx>( !tcx.type_of(last_field.did).instantiate_identity().is_sized(tcx, typing_env) }); + let mut repr = def.repr(); + + // FIXME: should this have a flag on the adtdef? + if tcx.is_lang_item(def.did(), hir::LangItem::AlignType) { + let align_value = extract_const_value(cx, ty, args.const_at(0))? + .try_to_target_usize(tcx) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; + + // FIXME: actual error message + let align = abi::Align::from_bytes(align_value) + .map_err(|_| error(cx, LayoutError::Unknown(ty)))?; + + repr.align = Some(align); + } + let layout = cx .calc .layout_of_struct_or_enum( - &def.repr(), + &repr, &variants, def.is_enum(), is_special_no_niche, diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index a987970c9bcc3..66daa7662271d 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1533,3 +1533,16 @@ pub const unsafe fn conjure_zst() -> T { MaybeUninit::uninit().assume_init() } } + +/// Zero-sized type with an alignment from its const generic parameter. +/// +/// ## Layout: +/// If `N` is a valid alignment then the following are guaranteed: +/// * `size_of::>() == 0` +/// * `align_of::>() == N` +/// +/// Otherwise, `Align` does not have a valid layout and compilation will fail. +#[unstable(feature = "align_type", issue = "none")] +#[lang = "align_type"] +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct Align; diff --git a/tests/ui/layout/align-type/cycle.rs b/tests/ui/layout/align-type/cycle.rs new file mode 100644 index 0000000000000..e7502eec01fb4 --- /dev/null +++ b/tests/ui/layout/align-type/cycle.rs @@ -0,0 +1,10 @@ +// test that layout cycles involving Align error and don't ICE +#![feature(align_type)] + +use std::mem::Align; + +struct Evil { + align: Align<{align_of::()}>, //~ ERROR cycle detected +} + +fn main() {} diff --git a/tests/ui/layout/align-type/cycle.stderr b/tests/ui/layout/align-type/cycle.stderr new file mode 100644 index 0000000000000..10c4e56ab60a6 --- /dev/null +++ b/tests/ui/layout/align-type/cycle.stderr @@ -0,0 +1,30 @@ +error[E0391]: cycle detected when evaluating type-level constant + --> $DIR/cycle.rs:7:18 + | +LL | align: Align<{align_of::()}>, + | ^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires const-evaluating + checking `Evil::align::{constant#0}`... + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL +note: ...which requires simplifying constant for the type system `core::mem::SizedTypeProperties::ALIGN`... + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL +note: ...which requires const-evaluating + checking `core::mem::SizedTypeProperties::ALIGN`... + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + = note: ...which requires computing layout of `Evil`... + = note: ...which requires computing layout of `core::mem::Align<{align_of::()}>`... +note: ...which requires normalizing `core::mem::Align<{align_of::()}>`... + --> $DIR/cycle.rs:7:18 + | +LL | align: Align<{align_of::()}>, + | ^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires evaluating type-level constant, completing the cycle +note: cycle used when checking that `Evil` is well-formed + --> $DIR/cycle.rs:7:18 + | +LL | align: Align<{align_of::()}>, + | ^^^^^^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/layout/align-type/invalid-alignment.rs b/tests/ui/layout/align-type/invalid-alignment.rs new file mode 100644 index 0000000000000..2612a51159725 --- /dev/null +++ b/tests/ui/layout/align-type/invalid-alignment.rs @@ -0,0 +1,18 @@ +// test that invalid alignments put into Align fail +#![feature(align_type)] + +use std::mem::Align; + +const MAX_SUPPORTED_ALIGN: usize = 1 << 29; + +const _: () = { + // not power of two + align_of::>(); //~? ERROR unknown layout +}; + +const _: () = { + // too big + align_of::>(); //~? ERROR unknown layout +}; + +fn main() {} diff --git a/tests/ui/layout/align-type/invalid-alignment.stderr b/tests/ui/layout/align-type/invalid-alignment.stderr new file mode 100644 index 0000000000000..fee31fde3a5ba --- /dev/null +++ b/tests/ui/layout/align-type/invalid-alignment.stderr @@ -0,0 +1,13 @@ +error[E0080]: the type `Align<3>` has an unknown layout + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + | + = note: evaluation of ` as std::mem::SizedTypeProperties>::ALIGN` failed here + +error[E0080]: the type `Align<1073741824>` has an unknown layout + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + | + = note: evaluation of ` as std::mem::SizedTypeProperties>::ALIGN` failed here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/layout/align-type/invalid-unused.rs b/tests/ui/layout/align-type/invalid-unused.rs new file mode 100644 index 0000000000000..a2e5f02576f40 --- /dev/null +++ b/tests/ui/layout/align-type/invalid-unused.rs @@ -0,0 +1,21 @@ +// FIXME: should this pass? +//@ check-pass +#![feature(align_type)] + +use std::marker::PhantomData; +use std::mem::Align; + +struct TypeFromTheTernaryDimension { + align: Align<3>, +} + +struct InsidePhantom { + phantom: PhantomData>, +} + +const _: () = { + assert!(size_of::() == 0); + assert!(align_of::() == 1); +}; + +fn main() {} diff --git a/tests/ui/layout/align-type/simple.rs b/tests/ui/layout/align-type/simple.rs new file mode 100644 index 0000000000000..36dd18967b9c7 --- /dev/null +++ b/tests/ui/layout/align-type/simple.rs @@ -0,0 +1,53 @@ +// test that reasonable usage of Align works as expected +//@ check-pass +#![feature(align_type)] + +use std::mem::Align; + +struct ContainsJustAlign { + align: Align<8> +} + +struct WithAlignType { + a: [u8; 4], + b: u32, + c: *const (), + + align: Align<16> +} + +#[repr(align(16))] +struct WithReprAlign { + a: [u8; 4], + b: u32, + c: *const (), +} + +const XKCD_CERTIFIED_RANDOM_NUMBER: usize = 4; +const MAX_SUPPORTED_ALIGN: usize = 1 << 29; + +const _: () = { + // FIXME: should this fail? + assert!(size_of::>() == 0); + assert!(align_of::>() == 1); + + assert!(size_of::>() == 0); + assert!(align_of::>() == 1); + + assert!(size_of::>() == 0); + assert!(align_of::>() == 64); + + assert!(size_of::>() == 0); + assert!(align_of::>() == XKCD_CERTIFIED_RANDOM_NUMBER); + + assert!(size_of::>() == 0); + assert!(align_of::>() == MAX_SUPPORTED_ALIGN); + + assert!(size_of::() == 0); + assert!(align_of::() == 8); + + assert!(size_of::() == size_of::()); + assert!(align_of::() == size_of::()); +}; + +fn main() {}