Merge commit 'aa9bc86125' into sync-from-ra

This commit is contained in:
Laurențiu Nicola 2023-06-05 12:04:23 +03:00
parent 1570299af4
commit c48062fe2a
598 changed files with 57696 additions and 17615 deletions

View file

@ -1,18 +1,25 @@
//! Compute the binary representation of structs, unions and enums
use std::ops::Bound;
use std::{cmp, ops::Bound};
use base_db::CrateId;
use hir_def::{
adt::VariantData,
layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId,
data::adt::VariantData,
layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout},
AdtId, EnumVariantId, LocalEnumVariantId, VariantId,
};
use la_arena::RawIdx;
use smallvec::SmallVec;
use triomphe::Arc;
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};
use super::LayoutCx;
pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx {
RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0)))
@ -22,29 +29,29 @@ pub fn layout_of_adt_query(
db: &dyn HirDatabase,
def: AdtId,
subst: Substitution,
) -> Result<Layout, LayoutError> {
let krate = def.module(db.upcast()).krate();
krate: CrateId,
) -> Result<Arc<Layout>, LayoutError> {
let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
let cx = LayoutCx { krate, target: &target };
let dl = cx.current_data_layout();
let handle_variant = |def: VariantId, var: &VariantData| {
var.fields()
.iter()
.map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate))
.map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), cx.krate))
.collect::<Result<Vec<_>, _>>()
};
let (variants, is_enum, is_union, repr) = match def {
let (variants, repr) = match def {
AdtId::StructId(s) => {
let data = db.struct_data(s);
let mut r = SmallVec::<[_; 1]>::new();
r.push(handle_variant(s.into(), &data.variant_data)?);
(r, false, false, data.repr.unwrap_or_default())
(r, data.repr.unwrap_or_default())
}
AdtId::UnionId(id) => {
let data = db.union_data(id);
let mut r = SmallVec::new();
r.push(handle_variant(id.into(), &data.variant_data)?);
(r, false, true, data.repr.unwrap_or_default())
(r, data.repr.unwrap_or_default())
}
AdtId::EnumId(e) => {
let data = db.enum_data(e);
@ -58,22 +65,24 @@ pub fn layout_of_adt_query(
)
})
.collect::<Result<SmallVec<_>, _>>()?;
(r, true, false, data.repr.unwrap_or_default())
(r, data.repr.unwrap_or_default())
}
};
let variants =
variants.iter().map(|x| x.iter().collect::<Vec<_>>()).collect::<SmallVec<[_; 1]>>();
let variants = variants
.iter()
.map(|x| x.iter().map(|x| &**x).collect::<Vec<_>>())
.collect::<SmallVec<[_; 1]>>();
let variants = variants.iter().map(|x| x.iter().collect()).collect();
if is_union {
cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)
let result = if matches!(def, AdtId::UnionId(..)) {
cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)?
} else {
cx.layout_of_struct_or_enum(
&repr,
&variants,
is_enum,
is_unsafe_cell(def, db),
matches!(def, AdtId::EnumId(..)),
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 =
@ -90,15 +99,16 @@ pub fn layout_of_adt_query(
// .iter_enumerated()
// .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()))
repr.inhibit_enum_layout_opt(),
!is_enum
!matches!(def, AdtId::EnumId(..))
&& variants
.iter()
.next()
.and_then(|x| x.last().map(|x| x.is_unsized()))
.unwrap_or(true),
)
.ok_or(LayoutError::SizeOverflow)
}
.ok_or(LayoutError::SizeOverflow)?
};
Ok(Arc::new(result))
}
fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
@ -122,6 +132,54 @@ pub fn layout_of_adt_recover(
_: &[String],
_: &AdtId,
_: &Substitution,
) -> Result<Layout, LayoutError> {
_: &CrateId,
) -> Result<Arc<Layout>, LayoutError> {
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)
})
}

View file

@ -1,9 +1,8 @@
//! Target dependent parameters needed for layouts
use std::sync::Arc;
use base_db::CrateId;
use hir_def::layout::TargetDataLayout;
use triomphe::Arc;
use crate::db::HirDatabase;

View file

@ -2,49 +2,55 @@ use std::collections::HashMap;
use base_db::fixture::WithFixture;
use chalk_ir::{AdtId, TyKind};
use hir_def::{
db::DefDatabase,
use hir_def::db::DefDatabase;
use triomphe::Arc;
use crate::{
db::HirDatabase,
layout::{Layout, LayoutError},
test_db::TestDB,
Interner, Substitution,
};
use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution};
use super::layout_of_ty;
mod closure;
fn current_machine_data_layout() -> String {
project_model::target_data_layout::get(None, None, &HashMap::default()).unwrap()
}
fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
let target_data_layout = current_machine_data_layout();
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,
let (db, file_ids) = TestDB::with_many_files(&ra_fixture);
let (adt_id, module_id) = file_ids
.into_iter()
.find_map(|file_id| {
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,
})?;
Some((adt_id, module_id))
})
.unwrap();
let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner);
layout_of_ty(&db, &goal_ty, module_id.krate())
db.layout_of_ty(goal_ty, module_id.krate())
}
/// A version of `eval_goal` for types that can not be expressed in ADTs, like closures and `impl Trait`
fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
let target_data_layout = current_machine_data_layout();
let ra_fixture = format!(
"{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\nfn main(){{let goal = {{{ra_fixture}}};}}",
@ -68,7 +74,7 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
let b = hir_body.bindings.iter().find(|x| x.1.name.to_smol_str() == "goal").unwrap().0;
let infer = db.infer(adt_id.into());
let goal_ty = infer.type_of_binding[b].clone();
layout_of_ty(&db, &goal_ty, module_id.krate())
db.layout_of_ty(goal_ty, module_id.krate())
}
#[track_caller]
@ -81,8 +87,8 @@ fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64)
#[track_caller]
fn check_size_and_align_expr(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
let l = eval_expr(ra_fixture, minicore).unwrap();
assert_eq!(l.size.bytes(), size);
assert_eq!(l.align.abi.bytes(), align);
assert_eq!(l.size.bytes(), size, "size mismatch");
assert_eq!(l.align.abi.bytes(), align, "align mismatch");
}
#[track_caller]
@ -118,13 +124,31 @@ macro_rules! size_and_align {
};
}
#[macro_export]
macro_rules! size_and_align_expr {
(minicore: $($x:tt),*; stmts: [$($s:tt)*] $($t:tt)*) => {
{
#[allow(dead_code)]
#[allow(unused_must_use)]
#[allow(path_statements)]
{
$($s)*
let val = { $($t)* };
$crate::layout::tests::check_size_and_align_expr(
&format!("{{ {} let val = {{ {} }}; val }}", stringify!($($s)*), stringify!($($t)*)),
&format!("//- minicore: {}\n", stringify!($($x),*)),
::std::mem::size_of_val(&val) as u64,
::std::mem::align_of_val(&val) as u64,
);
}
}
};
($($t:tt)*) => {
{
#[allow(dead_code)]
{
let val = { $($t)* };
check_size_and_align_expr(
$crate::layout::tests::check_size_and_align_expr(
stringify!($($t)*),
"",
::std::mem::size_of_val(&val) as u64,
@ -196,6 +220,44 @@ fn generic() {
}
}
#[test]
fn associated_types() {
size_and_align! {
trait Tr {
type Ty;
}
impl Tr for i32 {
type Ty = i64;
}
struct Foo<A: Tr>(<A as Tr>::Ty);
struct Bar<A: Tr>(A::Ty);
struct Goal(Foo<i32>, Bar<i32>, <i32 as Tr>::Ty);
}
check_size_and_align(
r#"
//- /b/mod.rs crate:b
pub trait Tr {
type Ty;
}
pub struct Foo<A: Tr>(<A as Tr>::Ty);
//- /a/mod.rs crate:a deps:b
use b::{Tr, Foo};
struct S;
impl Tr for S {
type Ty = i64;
}
struct Goal(Foo<S>);
"#,
"",
8,
8,
);
}
#[test]
fn return_position_impl_trait() {
size_and_align_expr! {
@ -212,6 +274,45 @@ fn return_position_impl_trait() {
fn foo() -> (impl T, impl T, impl T) { (2i64, 5i32, 7i32) }
foo()
}
size_and_align_expr! {
minicore: iterators;
stmts: []
trait Tr {}
impl Tr for i32 {}
fn foo() -> impl Iterator<Item = impl Tr> {
[1, 2, 3].into_iter()
}
let mut iter = foo();
let item = iter.next();
(iter, item)
}
size_and_align_expr! {
minicore: future;
stmts: []
use core::{future::Future, task::{Poll, Context}, pin::pin};
use std::{task::Wake, sync::Arc};
trait Tr {}
impl Tr for i32 {}
async fn f() -> impl Tr {
2
}
fn unwrap_fut<T>(inp: impl Future<Output = T>) -> Poll<T> {
// In a normal test we could use `loop {}` or `panic!()` here,
// but rustc actually runs this code.
let pinned = pin!(inp);
struct EmptyWaker;
impl Wake for EmptyWaker {
fn wake(self: Arc<Self>) {
}
}
let waker = Arc::new(EmptyWaker).into();
let mut context = Context::from_waker(&waker);
let x = pinned.poll(&mut context);
x
}
let x = unwrap_fut(f());
x
}
size_and_align_expr! {
struct Foo<T>(T, T, (T, T));
trait T {}
@ -276,6 +377,14 @@ fn niche_optimization() {
}
}
#[test]
fn const_eval() {
size_and_align! {
const X: usize = 5;
struct Goal([i32; X]);
}
}
#[test]
fn enums_with_discriminants() {
size_and_align! {

View file

@ -0,0 +1,257 @@
use crate::size_and_align_expr;
#[test]
fn zero_capture_simple() {
size_and_align_expr! {
|x: i32| x + 2
}
}
#[test]
fn move_simple() {
size_and_align_expr! {
minicore: copy;
stmts: []
let y: i32 = 5;
move |x: i32| {
x + y
}
}
}
#[test]
fn ref_simple() {
size_and_align_expr! {
minicore: copy;
stmts: [
let y: i32 = 5;
]
|x: i32| {
x + y
}
}
size_and_align_expr! {
minicore: copy;
stmts: [
let mut y: i32 = 5;
]
|x: i32| {
y = y + x;
y
}
}
size_and_align_expr! {
minicore: copy, deref_mut;
stmts: [
let y: &mut i32 = &mut 5;
]
|x: i32| {
*y += x;
}
}
size_and_align_expr! {
minicore: copy;
stmts: [
struct X(i32, i64);
let x: X = X(2, 6);
]
|| {
x
}
}
size_and_align_expr! {
minicore: copy, deref_mut;
stmts: [
struct X(i32, i64);
let x: &mut X = &mut X(2, 6);
]
|| {
(*x).0 as i64 + x.1
}
}
}
#[test]
fn ref_then_mut_then_move() {
size_and_align_expr! {
minicore: copy;
stmts: [
struct X(i32, i64);
let mut x: X = X(2, 6);
]
|| {
&x;
&mut x;
x;
}
}
}
#[test]
fn nested_closures() {
size_and_align_expr! {
|| {
|| {
|| {
let x = 2;
move || {
move || {
x
}
}
}
}
}
}
}
#[test]
fn capture_specific_fields2() {
size_and_align_expr! {
minicore: copy;
stmts: [
let x = &mut 2;
]
|| {
*x = 5;
&x;
}
}
}
#[test]
fn capture_specific_fields() {
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
y.0 + x + (y.2 .0 as i64)
}
}
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
let _ = &y;
y.0 + x + (y.2 .0 as i64)
}
}
size_and_align_expr! {
minicore: copy;
stmts: [
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
]
let y = &y;
move |x: i64| {
y.0 + x + (y.2 .0 as i64)
}
}
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
let X(a, _, (b, _)) = y;
a + x + (b as i64)
}
}
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y = &&X(2, 5, (7, 3));
move |x: i64| {
let X(a, _, (b, _)) = y;
*a + x + (*b as i64)
}
}
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
match y {
X(a, _, (b, _)) => a + x + (b as i64),
}
}
}
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
let X(a @ 2, _, (b, _)) = y else { return 5 };
a + x + (b as i64)
}
}
}
#[test]
fn match_pattern() {
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
match y {
_ => x,
}
}
}
size_and_align_expr! {
minicore: copy;
stmts: [
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
]
|x: i64| {
match y {
X(_a, _, _c) => x,
}
}
}
size_and_align_expr! {
minicore: copy;
stmts: [
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
]
|x: i64| {
match y {
_y => x,
}
}
}
size_and_align_expr! {
minicore: copy;
stmts: [
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
]
|x: i64| {
match y {
ref _y => x,
}
}
}
}
#[test]
fn ellipsis_pattern() {
size_and_align_expr! {
struct X(i8, u16, i32, u64, i128, u8);
let y: X = X(1, 2, 3, 4, 5, 6);
move |_: i64| {
let X(_a, .., _b, _c) = y;
}
}
size_and_align_expr! {
struct X { a: i32, b: u8, c: i128}
let y: X = X { a: 1, b: 2, c: 3 };
move |_: i64| {
let X { a, b, .. } = y;
_ = (a, b);
}
}
size_and_align_expr! {
let y: (&&&(i8, u16, i32, u64, i128, u8), u16, i32, u64, i128, u8) = (&&&(1, 2, 3, 4, 5, 6), 2, 3, 4, 5, 6);
move |_: i64| {
let ((_a, .., _b, _c), .., _e, _f) = y;
}
}
}