Skip to content

Commit 4a2ceef

Browse files
committed
Auto merge of rust-lang#15135 - HKalbasi:mir, r=HKalbasi
Fix some unsizing problems in mir
2 parents b092b45 + 6d2d138 commit 4a2ceef

File tree

5 files changed

+134
-50
lines changed

5 files changed

+134
-50
lines changed

crates/hir-ty/src/consteval/tests.rs

+44-5
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,21 @@ fn casts() {
166166
check_number(
167167
r#"
168168
//- minicore: coerce_unsized, index, slice
169+
struct X {
170+
unsize_field: [u8],
171+
}
172+
169173
const GOAL: usize = {
170174
let a = [10, 20, 3, 15];
171175
let x: &[i32] = &a;
172-
let y: *const [i32] = x;
173-
let z = y as *const [u8]; // slice fat pointer cast don't touch metadata
174-
let q = z as *const str;
175-
let p = q as *const [u8];
176-
let w = unsafe { &*z };
176+
let x: *const [i32] = x;
177+
let x = x as *const [u8]; // slice fat pointer cast don't touch metadata
178+
let x = x as *const str;
179+
let x = x as *const X;
180+
let x = x as *const [i16];
181+
let x = x as *const X;
182+
let x = x as *const [u8];
183+
let w = unsafe { &*x };
177184
w.len()
178185
};
179186
"#,
@@ -1873,6 +1880,38 @@ fn dyn_trait() {
18731880
);
18741881
}
18751882

1883+
#[test]
1884+
fn coerce_unsized() {
1885+
check_number(
1886+
r#"
1887+
//- minicore: coerce_unsized, deref_mut, slice, index, transmute, non_null
1888+
use core::ops::{Deref, DerefMut, CoerceUnsized};
1889+
use core::{marker::Unsize, mem::transmute, ptr::NonNull};
1890+
1891+
struct ArcInner<T: ?Sized> {
1892+
strong: usize,
1893+
weak: usize,
1894+
data: T,
1895+
}
1896+
1897+
pub struct Arc<T: ?Sized> {
1898+
inner: NonNull<ArcInner<T>>,
1899+
}
1900+
1901+
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Arc<U>> for Arc<T> {}
1902+
1903+
const GOAL: usize = {
1904+
let x = transmute::<usize, Arc<[i32; 3]>>(12);
1905+
let y: Arc<[i32]> = x;
1906+
let z = transmute::<Arc<[i32]>, (usize, usize)>(y);
1907+
z.1
1908+
};
1909+
1910+
"#,
1911+
3,
1912+
);
1913+
}
1914+
18761915
#[test]
18771916
fn boxes() {
18781917
check_number(

crates/hir-ty/src/layout/tests.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -369,11 +369,11 @@ fn tuple() {
369369
}
370370

371371
#[test]
372-
fn non_zero() {
372+
fn non_zero_and_non_null() {
373373
size_and_align! {
374-
minicore: non_zero, option;
375-
use core::num::NonZeroU8;
376-
struct Goal(Option<NonZeroU8>);
374+
minicore: non_zero, non_null, option;
375+
use core::{num::NonZeroU8, ptr::NonNull};
376+
struct Goal(Option<NonZeroU8>, Option<NonNull<i32>>);
377377
}
378378
}
379379

crates/hir-ty/src/mir/eval.rs

+66-35
Original file line numberDiff line numberDiff line change
@@ -1263,50 +1263,81 @@ impl Evaluator<'_> {
12631263
current_ty: &Ty,
12641264
target_ty: &Ty,
12651265
) -> Result<IntervalOrOwned> {
1266-
use IntervalOrOwned::*;
12671266
fn for_ptr(x: &TyKind) -> Option<Ty> {
12681267
match x {
12691268
TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()),
12701269
_ => None,
12711270
}
12721271
}
1273-
Ok(match self.coerce_unsized_look_through_fields(target_ty, for_ptr)? {
1274-
ty => match &ty.data(Interner).kind {
1275-
TyKind::Slice(_) => {
1276-
match self.coerce_unsized_look_through_fields(current_ty, for_ptr)? {
1277-
ty => match &ty.data(Interner).kind {
1278-
TyKind::Array(_, size) => {
1279-
let len = match try_const_usize(self.db, size) {
1280-
None => not_supported!(
1281-
"unevaluatble len of array in coerce unsized"
1282-
),
1283-
Some(x) => x as usize,
1284-
};
1285-
let mut r = Vec::with_capacity(16);
1286-
let addr = addr.get(self)?;
1287-
r.extend(addr.iter().copied());
1288-
r.extend(len.to_le_bytes().into_iter());
1289-
Owned(r)
1290-
}
1291-
t => {
1292-
not_supported!("slice unsizing from non array type {t:?}")
1293-
}
1294-
},
1295-
}
1272+
let target_ty = self.coerce_unsized_look_through_fields(target_ty, for_ptr)?;
1273+
let current_ty = self.coerce_unsized_look_through_fields(current_ty, for_ptr)?;
1274+
1275+
self.unsizing_ptr_from_addr(target_ty, current_ty, addr)
1276+
}
1277+
1278+
/// Adds metadata to the address and create the fat pointer result of the unsizing operation.
1279+
fn unsizing_ptr_from_addr(
1280+
&mut self,
1281+
target_ty: Ty,
1282+
current_ty: Ty,
1283+
addr: Interval,
1284+
) -> Result<IntervalOrOwned> {
1285+
use IntervalOrOwned::*;
1286+
Ok(match &target_ty.data(Interner).kind {
1287+
TyKind::Slice(_) => match &current_ty.data(Interner).kind {
1288+
TyKind::Array(_, size) => {
1289+
let len = match try_const_usize(self.db, size) {
1290+
None => {
1291+
not_supported!("unevaluatble len of array in coerce unsized")
1292+
}
1293+
Some(x) => x as usize,
1294+
};
1295+
let mut r = Vec::with_capacity(16);
1296+
let addr = addr.get(self)?;
1297+
r.extend(addr.iter().copied());
1298+
r.extend(len.to_le_bytes().into_iter());
1299+
Owned(r)
12961300
}
1297-
TyKind::Dyn(_) => match &current_ty.data(Interner).kind {
1298-
TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
1299-
let vtable = self.vtable_map.id(ty.clone());
1300-
let mut r = Vec::with_capacity(16);
1301-
let addr = addr.get(self)?;
1302-
r.extend(addr.iter().copied());
1303-
r.extend(vtable.to_le_bytes().into_iter());
1304-
Owned(r)
1301+
t => {
1302+
not_supported!("slice unsizing from non array type {t:?}")
1303+
}
1304+
},
1305+
TyKind::Dyn(_) => {
1306+
let vtable = self.vtable_map.id(current_ty.clone());
1307+
let mut r = Vec::with_capacity(16);
1308+
let addr = addr.get(self)?;
1309+
r.extend(addr.iter().copied());
1310+
r.extend(vtable.to_le_bytes().into_iter());
1311+
Owned(r)
1312+
}
1313+
TyKind::Adt(id, target_subst) => match &current_ty.data(Interner).kind {
1314+
TyKind::Adt(current_id, current_subst) => {
1315+
if id != current_id {
1316+
not_supported!("unsizing struct with different type");
13051317
}
1306-
_ => not_supported!("dyn unsizing from non pointers"),
1307-
},
1308-
_ => not_supported!("unknown unsized cast"),
1318+
let id = match id.0 {
1319+
AdtId::StructId(s) => s,
1320+
AdtId::UnionId(_) => not_supported!("unsizing unions"),
1321+
AdtId::EnumId(_) => not_supported!("unsizing enums"),
1322+
};
1323+
let Some((last_field, _)) = self.db.struct_data(id).variant_data.fields().iter().rev().next() else {
1324+
not_supported!("unsizing struct without field");
1325+
};
1326+
let target_last_field = self.db.field_types(id.into())[last_field]
1327+
.clone()
1328+
.substitute(Interner, target_subst);
1329+
let current_last_field = self.db.field_types(id.into())[last_field]
1330+
.clone()
1331+
.substitute(Interner, current_subst);
1332+
return self.unsizing_ptr_from_addr(
1333+
target_last_field,
1334+
current_last_field,
1335+
addr,
1336+
);
1337+
}
1338+
_ => not_supported!("unsizing struct with non adt type"),
13091339
},
1340+
_ => not_supported!("unknown unsized cast"),
13101341
})
13111342
}
13121343

crates/hir-ty/src/mir/lower.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -1742,17 +1742,17 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
17421742
(TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => {
17431743
CastKind::Pointer(if a == b {
17441744
PointerCast::MutToConstPointer
1745-
} else if matches!(a.kind(Interner), TyKind::Slice(_) | TyKind::Str)
1746-
&& matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Str)
1745+
} else if matches!(b.kind(Interner), TyKind::Slice(_))
1746+
&& matches!(a.kind(Interner), TyKind::Array(_, _))
1747+
|| matches!(b.kind(Interner), TyKind::Dyn(_))
17471748
{
1748-
// slice to slice cast is no-op (metadata is not touched), so we use this
1749-
PointerCast::MutToConstPointer
1750-
} else if matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
17511749
PointerCast::Unsize
17521750
} else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) {
17531751
PointerCast::ArrayToPointer
17541752
} else {
1755-
// cast between two sized pointer, like *const i32 to *const i8. There is no specific variant
1753+
// cast between two sized pointer, like *const i32 to *const i8, or two unsized pointer, like
1754+
// slice to slice, slice to str, ... . These are no-ops (even in the unsized case, no metadata
1755+
// will be touched) but there is no specific variant
17561756
// for it in `PointerCast` so we use `MutToConstPointer`
17571757
PointerCast::MutToConstPointer
17581758
})

crates/test-utils/src/minicore.rs

+14
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
//! iterator: option
3838
//! iterators: iterator, fn
3939
//! manually_drop: drop
40+
//! non_null:
4041
//! non_zero:
4142
//! option: panic
4243
//! ord: eq, option
@@ -386,6 +387,19 @@ pub mod ptr {
386387
type Metadata;
387388
}
388389
// endregion:pointee
390+
// region:non_null
391+
#[rustc_layout_scalar_valid_range_start(1)]
392+
#[rustc_nonnull_optimization_guaranteed]
393+
pub struct NonNull<T: ?Sized> {
394+
pointer: *const T,
395+
}
396+
// region:coerce_unsized
397+
impl<T: ?Sized, U: ?Sized> crate::ops::CoerceUnsized<NonNull<U>> for NonNull<T> where
398+
T: crate::marker::Unsize<U>
399+
{
400+
}
401+
// endregion:coerce_unsized
402+
// endregion:non_null
389403
}
390404

391405
pub mod ops {

0 commit comments

Comments
 (0)