Support closure in clone shim

This commit is contained in:
hkalbasi 2023-08-08 01:49:34 +03:30
parent b96e4f2f4a
commit 31c30933cf
2 changed files with 73 additions and 21 deletions

View File

@ -1453,6 +1453,30 @@ fn from(E1(x): E1) -> Self {
);
}
#[test]
fn closure_clone() {
check_number(
r#"
//- minicore: clone, fn
struct S(u8);
impl Clone for S(u8) {
fn clone(&self) -> S {
S(self.0 + 5)
}
}
const GOAL: u8 = {
let s = S(3);
let cl = move || s;
let cl = cl.clone();
cl().0
}
"#,
8,
);
}
#[test]
fn builtin_derive_macro() {
check_number(

View File

@ -136,7 +136,10 @@ pub(super) fn detect_and_exec_special_function(
not_supported!("wrong generic arg kind for clone");
};
// Clone has special impls for tuples and function pointers
if matches!(self_ty.kind(Interner), TyKind::Function(_) | TyKind::Tuple(..)) {
if matches!(
self_ty.kind(Interner),
TyKind::Function(_) | TyKind::Tuple(..) | TyKind::Closure(..)
) {
self.exec_clone(def, args, self_ty.clone(), locals, destination, span)?;
return Ok(true);
}
@ -167,32 +170,26 @@ fn exec_clone(
return destination
.write_from_interval(self, Interval { addr, size: destination.size });
}
TyKind::Closure(id, subst) => {
let [arg] = args else {
not_supported!("wrong arg count for clone");
};
let addr = Address::from_bytes(arg.get(self)?)?;
let (closure_owner, _) = self.db.lookup_intern_closure((*id).into());
let infer = self.db.infer(closure_owner);
let (captures, _) = infer.closure_info(id);
let layout = self.layout(&self_ty)?;
let ty_iter = captures.iter().map(|c| c.ty(subst));
self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?;
}
TyKind::Tuple(_, subst) => {
let [arg] = args else {
not_supported!("wrong arg count for clone");
};
let addr = Address::from_bytes(arg.get(self)?)?;
let layout = self.layout(&self_ty)?;
for (i, ty) in subst.iter(Interner).enumerate() {
let ty = ty.assert_ty_ref(Interner);
let size = self.layout(ty)?.size.bytes_usize();
let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
let arg = IntervalAndTy {
interval: Interval { addr: tmp, size: self.ptr_size() },
ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone())
.intern(Interner),
};
let offset = layout.fields.offset(i).bytes_usize();
self.write_memory(tmp, &addr.offset(offset).to_bytes())?;
self.exec_clone(
def,
&[arg],
ty.clone(),
locals,
destination.slice(offset..offset + size),
span,
)?;
}
let ty_iter = subst.iter(Interner).map(|ga| ga.assert_ty_ref(Interner).clone());
self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?;
}
_ => {
self.exec_fn_with_args(
@ -209,6 +206,37 @@ fn exec_clone(
Ok(())
}
fn exec_clone_for_fields(
&mut self,
ty_iter: impl Iterator<Item = Ty>,
layout: Arc<Layout>,
addr: Address,
def: FunctionId,
locals: &Locals,
destination: Interval,
span: MirSpan,
) -> Result<()> {
for (i, ty) in ty_iter.enumerate() {
let size = self.layout(&ty)?.size.bytes_usize();
let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
let arg = IntervalAndTy {
interval: Interval { addr: tmp, size: self.ptr_size() },
ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone()).intern(Interner),
};
let offset = layout.fields.offset(i).bytes_usize();
self.write_memory(tmp, &addr.offset(offset).to_bytes())?;
self.exec_clone(
def,
&[arg],
ty,
locals,
destination.slice(offset..offset + size),
span,
)?;
}
Ok(())
}
fn exec_alloc_fn(
&mut self,
alloc_fn: &str,