Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ symbols! {
align,
align_of,
align_of_val,
align_type,
alignment,
all,
alloc,
Expand Down
17 changes: 16 additions & 1 deletion compiler/rustc_ty_utils/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My instinct is no, because everything other than layout should just treat it as a normal type with a generic-dependent layout, never switch on some type flag to detect it.

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,
Expand Down
13 changes: 13 additions & 0 deletions library/core/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1533,3 +1533,16 @@ pub const unsafe fn conjure_zst<T>() -> 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::<Align<N>>() == 0`
/// * `align_of::<Align<N>>() == N`
///
/// Otherwise, `Align<N>` 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<const N: usize>;
10 changes: 10 additions & 0 deletions tests/ui/layout/align-type/cycle.rs
Original file line number Diff line number Diff line change
@@ -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::<Evil>()}>, //~ ERROR cycle detected
}

fn main() {}
30 changes: 30 additions & 0 deletions tests/ui/layout/align-type/cycle.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
error[E0391]: cycle detected when evaluating type-level constant
--> $DIR/cycle.rs:7:18
|
LL | align: Align<{align_of::<Evil>()}>,
| ^^^^^^^^^^^^^^^^^^^^
|
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::<Evil>()}>`...
note: ...which requires normalizing `core::mem::Align<{align_of::<Evil>()}>`...
--> $DIR/cycle.rs:7:18
|
LL | align: Align<{align_of::<Evil>()}>,
| ^^^^^^^^^^^^^^^^^^^^
= 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::<Evil>()}>,
| ^^^^^^^^^^^^^^^^^^^^
= 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`.
18 changes: 18 additions & 0 deletions tests/ui/layout/align-type/invalid-alignment.rs
Original file line number Diff line number Diff line change
@@ -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::<Align<3>>(); //~? ERROR unknown layout
};

const _: () = {
// too big
align_of::<Align<{MAX_SUPPORTED_ALIGN * 2}>>(); //~? ERROR unknown layout
};

fn main() {}
13 changes: 13 additions & 0 deletions tests/ui/layout/align-type/invalid-alignment.stderr
Original file line number Diff line number Diff line change
@@ -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 `<std::mem::Align<3> 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 `<std::mem::Align<1073741824> as std::mem::SizedTypeProperties>::ALIGN` failed here

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0080`.
21 changes: 21 additions & 0 deletions tests/ui/layout/align-type/invalid-unused.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// FIXME: should this pass?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My instinct is that it definitely shouldn't pass, but maybe it doesn't fail elegantly -- perhaps it fails in the same way that [u32; usize::MAX / 4] fails, for example.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, types that are too large don't fail in these positions either

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, because it's just a check build or something?

If it's consistent with malformed arrays that's probably fine -- for now at least.

//@ check-pass
#![feature(align_type)]

use std::marker::PhantomData;
use std::mem::Align;

struct TypeFromTheTernaryDimension {
align: Align<3>,
}

struct InsidePhantom {
phantom: PhantomData<Align<9999>>,
}

const _: () = {
assert!(size_of::<InsidePhantom>() == 0);
assert!(align_of::<InsidePhantom>() == 1);
};

fn main() {}
53 changes: 53 additions & 0 deletions tests/ui/layout/align-type/simple.rs
Original file line number Diff line number Diff line change
@@ -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?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, same as #[repr(align(0))] and mem::Alignment::new(0).unwrap().

(And how https://doc.rust-lang.org/std/primitive.usize.html#method.is_power_of_two does not treat zero as a power of two.)

assert!(size_of::<Align<0>>() == 0);
assert!(align_of::<Align<0>>() == 1);

assert!(size_of::<Align<1>>() == 0);
assert!(align_of::<Align<1>>() == 1);

assert!(size_of::<Align<64>>() == 0);
assert!(align_of::<Align<64>>() == 64);

assert!(size_of::<Align<XKCD_CERTIFIED_RANDOM_NUMBER>>() == 0);
assert!(align_of::<Align<XKCD_CERTIFIED_RANDOM_NUMBER>>() == XKCD_CERTIFIED_RANDOM_NUMBER);

assert!(size_of::<Align<MAX_SUPPORTED_ALIGN>>() == 0);
assert!(align_of::<Align<MAX_SUPPORTED_ALIGN>>() == MAX_SUPPORTED_ALIGN);

assert!(size_of::<ContainsJustAlign>() == 0);
assert!(align_of::<ContainsJustAlign>() == 8);

assert!(size_of::<WithAlignType>() == size_of::<WithReprAlign>());
assert!(align_of::<WithAlignType>() == size_of::<WithReprAlign>());
};

fn main() {}
Loading