Skip to content

Commit 4125fea

Browse files
committed
Auto merge of rust-lang#15222 - HKalbasi:mir, r=HKalbasi
Fix size_of_val and support min_align_of_val
2 parents 54c2ee9 + 171ae2e commit 4125fea

File tree

7 files changed

+221
-89
lines changed

7 files changed

+221
-89
lines changed

crates/hir-expand/src/name.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,10 @@ pub mod known {
291291
alloc,
292292
iter,
293293
ops,
294+
fmt,
294295
future,
295296
result,
297+
string,
296298
boxed,
297299
option,
298300
prelude,
@@ -320,6 +322,7 @@ pub mod known {
320322
RangeToInclusive,
321323
RangeTo,
322324
Range,
325+
String,
323326
Neg,
324327
Not,
325328
None,
@@ -330,6 +333,7 @@ pub mod known {
330333
iter_mut,
331334
len,
332335
is_empty,
336+
as_str,
333337
new,
334338
// Builtin macros
335339
asm,
@@ -343,6 +347,7 @@ pub mod known {
343347
core_panic,
344348
env,
345349
file,
350+
format,
346351
format_args_nl,
347352
format_args,
348353
global_asm,

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

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2456,20 +2456,6 @@ fn const_trait_assoc() {
24562456
);
24572457
}
24582458

2459-
#[test]
2460-
fn panic_messages() {
2461-
check_fail(
2462-
r#"
2463-
//- minicore: panic
2464-
const GOAL: u8 = {
2465-
let x: u16 = 2;
2466-
panic!("hello");
2467-
};
2468-
"#,
2469-
|e| e == ConstEvalError::MirEvalError(MirEvalError::Panic("hello".to_string())),
2470-
);
2471-
}
2472-
24732459
#[test]
24742460
fn exec_limits() {
24752461
check_fail(

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

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,50 @@ fn size_of_val() {
4343
"#,
4444
12,
4545
);
46+
check_number(
47+
r#"
48+
//- minicore: coerce_unsized, transmute
49+
use core::mem::transmute;
50+
51+
extern "rust-intrinsic" {
52+
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
53+
}
54+
55+
struct X {
56+
x: i64,
57+
y: u8,
58+
t: [i32],
59+
}
60+
61+
const GOAL: usize = unsafe {
62+
let y: &X = transmute([0usize, 3]);
63+
size_of_val(y)
64+
};
65+
"#,
66+
24,
67+
);
68+
check_number(
69+
r#"
70+
//- minicore: coerce_unsized, transmute
71+
use core::mem::transmute;
72+
73+
extern "rust-intrinsic" {
74+
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
75+
}
76+
77+
struct X {
78+
x: i32,
79+
y: i64,
80+
t: [u8],
81+
}
82+
83+
const GOAL: usize = unsafe {
84+
let y: &X = transmute([0usize, 15]);
85+
size_of_val(y)
86+
};
87+
"#,
88+
32,
89+
);
4690
check_number(
4791
r#"
4892
//- minicore: coerce_unsized, fmt, builtin_impls
@@ -74,6 +118,37 @@ fn size_of_val() {
74118
);
75119
}
76120

121+
#[test]
122+
fn min_align_of_val() {
123+
check_number(
124+
r#"
125+
//- minicore: coerce_unsized
126+
extern "rust-intrinsic" {
127+
pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;
128+
}
129+
130+
struct X(i32, u8);
131+
132+
const GOAL: usize = min_align_of_val(&X(1, 2));
133+
"#,
134+
4,
135+
);
136+
check_number(
137+
r#"
138+
//- minicore: coerce_unsized
139+
extern "rust-intrinsic" {
140+
pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;
141+
}
142+
143+
const GOAL: usize = {
144+
let x: &[i32] = &[1, 2, 3];
145+
min_align_of_val(x)
146+
};
147+
"#,
148+
4,
149+
);
150+
}
151+
77152
#[test]
78153
fn transmute() {
79154
check_number(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ pub fn layout_of_adt_query(
105105
&& variants
106106
.iter()
107107
.next()
108-
.and_then(|x| x.last().map(|x| x.is_unsized()))
108+
.and_then(|x| x.last().map(|x| !x.is_unsized()))
109109
.unwrap_or(true),
110110
)
111111
.ok_or(LayoutError::SizeOverflow)?

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

Lines changed: 100 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
44
use std::cmp;
55

6+
use chalk_ir::TyKind;
7+
use hir_def::resolver::HasResolver;
8+
use hir_expand::mod_path::ModPath;
9+
610
use super::*;
711

812
mod simd;
@@ -186,44 +190,24 @@ impl Evaluator<'_> {
186190
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_string())),
187191
PanicFmt => {
188192
let message = (|| {
189-
let arguments_struct =
190-
self.db.lang_item(self.crate_id, LangItem::FormatArguments)?.as_struct()?;
191-
let arguments_layout = self
192-
.layout_adt(arguments_struct.into(), Substitution::empty(Interner))
193-
.ok()?;
194-
let arguments_field_pieces =
195-
self.db.struct_data(arguments_struct).variant_data.field(&name![pieces])?;
196-
let pieces_offset = arguments_layout
197-
.fields
198-
.offset(u32::from(arguments_field_pieces.into_raw()) as usize)
199-
.bytes_usize();
200-
let ptr_size = self.ptr_size();
201-
let arg = args.next()?;
202-
let pieces_array_addr =
203-
Address::from_bytes(&arg[pieces_offset..pieces_offset + ptr_size]).ok()?;
204-
let pieces_array_len = usize::from_le_bytes(
205-
(&arg[pieces_offset + ptr_size..pieces_offset + 2 * ptr_size])
206-
.try_into()
207-
.ok()?,
208-
);
209-
let mut message = "".to_string();
210-
for i in 0..pieces_array_len {
211-
let piece_ptr_addr = pieces_array_addr.offset(2 * i * ptr_size);
212-
let piece_addr =
213-
Address::from_bytes(self.read_memory(piece_ptr_addr, ptr_size).ok()?)
214-
.ok()?;
215-
let piece_len = usize::from_le_bytes(
216-
self.read_memory(piece_ptr_addr.offset(ptr_size), ptr_size)
217-
.ok()?
218-
.try_into()
219-
.ok()?,
220-
);
221-
let piece_data = self.read_memory(piece_addr, piece_len).ok()?;
222-
message += &std::string::String::from_utf8_lossy(piece_data);
223-
}
224-
Some(message)
193+
let x = self.db.crate_def_map(self.crate_id).crate_root();
194+
let resolver = x.resolver(self.db.upcast());
195+
let Some(format_fn) = resolver.resolve_path_in_value_ns_fully(
196+
self.db.upcast(),
197+
&hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments(
198+
hir_expand::mod_path::PathKind::Abs,
199+
[name![std], name![fmt], name![format]].into_iter(),
200+
)),
201+
) else {
202+
not_supported!("std::fmt::format not found");
203+
};
204+
let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { not_supported!("std::fmt::format is not a function") };
205+
let message_string = self.interpret_mir(&*self.db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.cloned())?;
206+
let addr = Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?;
207+
let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]);
208+
Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?).into_owned())
225209
})()
226-
.unwrap_or_else(|| "<format-args-evaluation-failed>".to_string());
210+
.unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}"));
227211
Err(MirEvalError::Panic(message))
228212
}
229213
SliceLen => {
@@ -544,6 +528,13 @@ impl Evaluator<'_> {
544528
let size = self.size_of_sized(ty, locals, "size_of arg")?;
545529
destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size])
546530
}
531+
"min_align_of" | "pref_align_of" => {
532+
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
533+
return Err(MirEvalError::TypeError("align_of generic arg is not provided"));
534+
};
535+
let align = self.layout(ty)?.align.abi.bytes();
536+
destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size])
537+
}
547538
"size_of_val" => {
548539
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner))
549540
else {
@@ -552,33 +543,28 @@ impl Evaluator<'_> {
552543
let [arg] = args else {
553544
return Err(MirEvalError::TypeError("size_of_val args are not provided"));
554545
};
555-
let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2);
556-
let size = match ty.kind(Interner) {
557-
TyKind::Str => return destination.write_from_interval(self, metadata),
558-
TyKind::Slice(inner) => {
559-
let len = from_bytes!(usize, metadata.get(self)?);
560-
len * self.size_of_sized(inner, locals, "slice inner type")?
561-
}
562-
TyKind::Dyn(_) => self.size_of_sized(
563-
self.vtable_map.ty_of_bytes(metadata.get(self)?)?,
564-
locals,
565-
"dyn concrete type",
566-
)?,
567-
_ => self.size_of_sized(
568-
ty,
569-
locals,
570-
"unsized type other than str, slice, and dyn",
571-
)?,
572-
};
573-
destination.write_from_bytes(self, &size.to_le_bytes())
546+
if let Some((size, _)) = self.size_align_of(ty, locals)? {
547+
destination.write_from_bytes(self, &size.to_le_bytes())
548+
} else {
549+
let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2);
550+
let (size, _) = self.size_align_of_unsized(ty, metadata, locals)?;
551+
destination.write_from_bytes(self, &size.to_le_bytes())
552+
}
574553
}
575-
"min_align_of" | "pref_align_of" => {
576-
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner))
577-
else {
578-
return Err(MirEvalError::TypeError("align_of generic arg is not provided"));
554+
"min_align_of_val" => {
555+
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
556+
return Err(MirEvalError::TypeError("min_align_of_val generic arg is not provided"));
579557
};
580-
let align = self.layout(ty)?.align.abi.bytes();
581-
destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size])
558+
let [arg] = args else {
559+
return Err(MirEvalError::TypeError("min_align_of_val args are not provided"));
560+
};
561+
if let Some((_, align)) = self.size_align_of(ty, locals)? {
562+
destination.write_from_bytes(self, &align.to_le_bytes())
563+
} else {
564+
let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2);
565+
let (_, align) = self.size_align_of_unsized(ty, metadata, locals)?;
566+
destination.write_from_bytes(self, &align.to_le_bytes())
567+
}
582568
}
583569
"needs_drop" => {
584570
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner))
@@ -905,6 +891,58 @@ impl Evaluator<'_> {
905891
}
906892
}
907893

894+
fn size_align_of_unsized(
895+
&mut self,
896+
ty: &Ty,
897+
metadata: Interval,
898+
locals: &Locals<'_>,
899+
) -> Result<(usize, usize)> {
900+
Ok(match ty.kind(Interner) {
901+
TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1),
902+
TyKind::Slice(inner) => {
903+
let len = from_bytes!(usize, metadata.get(self)?);
904+
let (size, align) = self.size_align_of_sized(inner, locals, "slice inner type")?;
905+
(size * len, align)
906+
}
907+
TyKind::Dyn(_) => self.size_align_of_sized(
908+
self.vtable_map.ty_of_bytes(metadata.get(self)?)?,
909+
locals,
910+
"dyn concrete type",
911+
)?,
912+
TyKind::Adt(id, subst) => {
913+
let id = id.0;
914+
let layout = self.layout_adt(id, subst.clone())?;
915+
let id = match id {
916+
AdtId::StructId(s) => s,
917+
_ => not_supported!("unsized enum or union"),
918+
};
919+
let field_types = &self.db.field_types(id.into());
920+
let last_field_ty =
921+
field_types.iter().rev().next().unwrap().1.clone().substitute(Interner, subst);
922+
let sized_part_size =
923+
layout.fields.offset(field_types.iter().count() - 1).bytes_usize();
924+
let sized_part_align = layout.align.abi.bytes() as usize;
925+
let (unsized_part_size, unsized_part_align) =
926+
self.size_align_of_unsized(&last_field_ty, metadata, locals)?;
927+
let align = sized_part_align.max(unsized_part_align) as isize;
928+
let size = (sized_part_size + unsized_part_size) as isize;
929+
// Must add any necessary padding to `size`
930+
// (to make it a multiple of `align`) before returning it.
931+
//
932+
// Namely, the returned size should be, in C notation:
933+
//
934+
// `size + ((size & (align-1)) ? align : 0)`
935+
//
936+
// emulated via the semi-standard fast bit trick:
937+
//
938+
// `(size + (align-1)) & -align`
939+
let size = (size + (align - 1)) & (-align);
940+
(size as usize, align as usize)
941+
}
942+
_ => not_supported!("unsized type other than str, slice, struct and dyn"),
943+
})
944+
}
945+
908946
fn exec_atomic_intrinsic(
909947
&mut self,
910948
name: &str,

0 commit comments

Comments
 (0)