Skip to content

Commit bd2a8ca

Browse files
committed
Add manual implementation of clone for tuples in mir interpreter
1 parent 17cc813 commit bd2a8ca

File tree

4 files changed

+138
-9
lines changed

4 files changed

+138
-9
lines changed

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

+31-3
Original file line numberDiff line numberDiff line change
@@ -1428,14 +1428,14 @@ fn builtin_derive_macro() {
14281428
#[derive(Clone)]
14291429
struct Y {
14301430
field1: i32,
1431-
field2: u8,
1431+
field2: ((i32, u8), i64),
14321432
}
14331433
14341434
const GOAL: u8 = {
1435-
let x = X(2, Z::Foo(Y { field1: 4, field2: 5 }), 8);
1435+
let x = X(2, Z::Foo(Y { field1: 4, field2: ((32, 5), 12) }), 8);
14361436
let x = x.clone();
14371437
let Z::Foo(t) = x.1;
1438-
t.field2
1438+
t.field2.0 .1
14391439
};
14401440
"#,
14411441
5,
@@ -1632,6 +1632,34 @@ const GOAL: i32 = {
16321632
);
16331633
}
16341634

1635+
#[test]
1636+
fn closure_capture_unsized_type() {
1637+
check_number(
1638+
r#"
1639+
//- minicore: fn, copy, slice, index, coerce_unsized
1640+
fn f<T: A>(x: &<T as A>::Ty) -> &<T as A>::Ty {
1641+
let c = || &*x;
1642+
c()
1643+
}
1644+
1645+
trait A {
1646+
type Ty;
1647+
}
1648+
1649+
impl A for i32 {
1650+
type Ty = [u8];
1651+
}
1652+
1653+
const GOAL: u8 = {
1654+
let k: &[u8] = &[1, 2, 3];
1655+
let k = f::<i32>(k);
1656+
k[0] + k[1] + k[2]
1657+
}
1658+
"#,
1659+
6,
1660+
);
1661+
}
1662+
16351663
#[test]
16361664
fn closure_and_impl_fn() {
16371665
check_number(

crates/hir-ty/src/layout.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use triomphe::Arc;
1414

1515
use crate::{
1616
consteval::try_const_usize, db::HirDatabase, infer::normalize, layout::adt::struct_variant_idx,
17-
utils::ClosureSubst, Interner, Substitution, TraitEnvironment, Ty,
17+
utils::ClosureSubst, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty,
1818
};
1919

2020
pub use self::{
@@ -279,7 +279,15 @@ pub fn layout_of_ty_query(
279279
// return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
280280
// }
281281

282-
let unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone());
282+
let mut unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone());
283+
if let TyKind::AssociatedType(id, subst) = unsized_part.kind(Interner) {
284+
unsized_part = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
285+
associated_ty_id: *id,
286+
substitution: subst.clone(),
287+
}))
288+
.intern(Interner);
289+
}
290+
unsized_part = normalize(db, trait_env.clone(), unsized_part);
283291
let metadata = match unsized_part.kind(Interner) {
284292
TyKind::Slice(_) | TyKind::Str => {
285293
scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
@@ -362,8 +370,16 @@ pub fn layout_of_ty_query(
362370
return Err(LayoutError::NotImplemented)
363371
}
364372
TyKind::Error => return Err(LayoutError::HasErrorType),
365-
TyKind::AssociatedType(_, _)
366-
| TyKind::Alias(_)
373+
TyKind::AssociatedType(id, subst) => {
374+
// Try again with `TyKind::Alias` to normalize the associated type.
375+
let ty = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
376+
associated_ty_id: *id,
377+
substitution: subst.clone(),
378+
}))
379+
.intern(Interner);
380+
return db.layout_of_ty(ty, trait_env);
381+
}
382+
TyKind::Alias(_)
367383
| TyKind::Placeholder(_)
368384
| TyKind::BoundVar(_)
369385
| TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder),

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

+11-2
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ pub enum MirEvalError {
313313
InvalidVTableId(usize),
314314
CoerceUnsizedError(Ty),
315315
LangItemNotFound(LangItem),
316+
BrokenLayout(Layout),
316317
}
317318

318319
impl MirEvalError {
@@ -399,6 +400,7 @@ impl MirEvalError {
399400
| MirEvalError::TargetDataLayoutNotAvailable
400401
| MirEvalError::CoerceUnsizedError(_)
401402
| MirEvalError::LangItemNotFound(_)
403+
| MirEvalError::BrokenLayout(_)
402404
| MirEvalError::InvalidVTableId(_) => writeln!(f, "{:?}", err)?,
403405
}
404406
Ok(())
@@ -433,6 +435,7 @@ impl std::fmt::Debug for MirEvalError {
433435
Self::CoerceUnsizedError(arg0) => {
434436
f.debug_tuple("CoerceUnsizedError").field(arg0).finish()
435437
}
438+
Self::BrokenLayout(arg0) => f.debug_tuple("BrokenLayout").field(arg0).finish(),
436439
Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
437440
Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
438441
Self::InvalidConst(arg0) => {
@@ -1541,12 +1544,18 @@ impl Evaluator<'_> {
15411544
) -> Result<Vec<u8>> {
15421545
let mut result = vec![0; size];
15431546
if let Some((offset, size, value)) = tag {
1544-
result[offset..offset + size].copy_from_slice(&value.to_le_bytes()[0..size]);
1547+
match result.get_mut(offset..offset + size) {
1548+
Some(it) => it.copy_from_slice(&value.to_le_bytes()[0..size]),
1549+
None => return Err(MirEvalError::BrokenLayout(variant_layout.clone())),
1550+
}
15451551
}
15461552
for (i, op) in values.enumerate() {
15471553
let offset = variant_layout.fields.offset(i).bytes_usize();
15481554
let op = op.get(&self)?;
1549-
result[offset..offset + op.len()].copy_from_slice(op);
1555+
match result.get_mut(offset..offset + op.len()) {
1556+
Some(it) => it.copy_from_slice(op),
1557+
None => return Err(MirEvalError::BrokenLayout(variant_layout.clone())),
1558+
}
15501559
}
15511560
Ok(result)
15521561
}

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

+76
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,85 @@ impl Evaluator<'_> {
124124
destination.write_from_bytes(self, &result)?;
125125
return Ok(true);
126126
}
127+
if let ItemContainerId::TraitId(t) = def.lookup(self.db.upcast()).container {
128+
if self.db.lang_attr(t.into()) == Some(LangItem::Clone) {
129+
let [self_ty] = generic_args.as_slice(Interner) else {
130+
not_supported!("wrong generic arg count for clone");
131+
};
132+
let Some(self_ty) = self_ty.ty(Interner) else {
133+
not_supported!("wrong generic arg kind for clone");
134+
};
135+
// Clone has special impls for tuples and function pointers
136+
if matches!(self_ty.kind(Interner), TyKind::Function(_) | TyKind::Tuple(..)) {
137+
self.exec_clone(def, args, self_ty.clone(), locals, destination, span)?;
138+
return Ok(true);
139+
}
140+
}
141+
}
127142
Ok(false)
128143
}
129144

145+
/// Clone has special impls for tuples and function pointers
146+
fn exec_clone(
147+
&mut self,
148+
def: FunctionId,
149+
args: &[IntervalAndTy],
150+
self_ty: Ty,
151+
locals: &Locals,
152+
destination: Interval,
153+
span: MirSpan,
154+
) -> Result<()> {
155+
match self_ty.kind(Interner) {
156+
TyKind::Function(_) => {
157+
let [arg] = args else {
158+
not_supported!("wrong arg count for clone");
159+
};
160+
let addr = Address::from_bytes(arg.get(self)?)?;
161+
return destination
162+
.write_from_interval(self, Interval { addr, size: destination.size });
163+
}
164+
TyKind::Tuple(_, subst) => {
165+
let [arg] = args else {
166+
not_supported!("wrong arg count for clone");
167+
};
168+
let addr = Address::from_bytes(arg.get(self)?)?;
169+
let layout = self.layout(&self_ty)?;
170+
for (i, ty) in subst.iter(Interner).enumerate() {
171+
let ty = ty.assert_ty_ref(Interner);
172+
let size = self.layout(ty)?.size.bytes_usize();
173+
let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
174+
let arg = IntervalAndTy {
175+
interval: Interval { addr: tmp, size: self.ptr_size() },
176+
ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone())
177+
.intern(Interner),
178+
};
179+
let offset = layout.fields.offset(i).bytes_usize();
180+
self.write_memory(tmp, &addr.offset(offset).to_bytes())?;
181+
self.exec_clone(
182+
def,
183+
&[arg],
184+
ty.clone(),
185+
locals,
186+
destination.slice(offset..offset + size),
187+
span,
188+
)?;
189+
}
190+
}
191+
_ => {
192+
self.exec_fn_with_args(
193+
def,
194+
args,
195+
Substitution::from1(Interner, self_ty),
196+
locals,
197+
destination,
198+
None,
199+
span,
200+
)?;
201+
}
202+
}
203+
Ok(())
204+
}
205+
130206
fn exec_alloc_fn(
131207
&mut self,
132208
alloc_fn: &str,

0 commit comments

Comments
 (0)