Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/layout/tests.rs')
-rw-r--r--crates/hir-ty/src/layout/tests.rs208
1 files changed, 208 insertions, 0 deletions
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
new file mode 100644
index 0000000000..53838cf41d
--- /dev/null
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -0,0 +1,208 @@
+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, minicore: &str) -> Result<Layout, LayoutError> {
+ // using unstable cargo features failed, fall back to using plain rustc
+ let mut cmd = std::process::Command::new("rustc");
+ cmd.args(["-Z", "unstable-options", "--print", "target-spec-json"]).env("RUSTC_BOOTSTRAP", "1");
+ let output = cmd.output().unwrap();
+ assert!(output.status.success(), "{}", output.status);
+ let stdout = String::from_utf8(output.stdout).unwrap();
+ let target_data_layout =
+ stdout.split_once(r#""data-layout": ""#).unwrap().1.split_once('"').unwrap().0.to_owned();
+
+ let ra_fixture = format!(
+ "{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\n{ra_fixture}",
+ );
+
+ 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()
+ .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_smol_str(),
+ hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(),
+ hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(),
+ };
+ (name == "Goal").then_some(x)
+ }
+ _ => None,
+ })
+ .unwrap();
+ let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner);
+ layout_of_ty(&db, &goal_ty, module_id.krate())
+}
+
+#[track_caller]
+fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
+ let l = eval_goal(ra_fixture, minicore).unwrap();
+ assert_eq!(l.size.bytes(), size);
+ assert_eq!(l.align.abi.bytes(), align);
+}
+
+#[track_caller]
+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(
+ stringify!($($t)*),
+ &format!("//- minicore: {}\n", stringify!($($x),*)),
+ ::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
+ }
+ }
+}