Add manual implementation of clone for tuples in mir interpreter
This commit is contained in:
parent
17cc813e92
commit
bd2a8ca507
@ -1428,14 +1428,14 @@ struct X(i32, Z, i64)
|
||||
#[derive(Clone)]
|
||||
struct Y {
|
||||
field1: i32,
|
||||
field2: u8,
|
||||
field2: ((i32, u8), i64),
|
||||
}
|
||||
|
||||
const GOAL: u8 = {
|
||||
let x = X(2, Z::Foo(Y { field1: 4, field2: 5 }), 8);
|
||||
let x = X(2, Z::Foo(Y { field1: 4, field2: ((32, 5), 12) }), 8);
|
||||
let x = x.clone();
|
||||
let Z::Foo(t) = x.1;
|
||||
t.field2
|
||||
t.field2.0 .1
|
||||
};
|
||||
"#,
|
||||
5,
|
||||
@ -1632,6 +1632,34 @@ extern "rust-call" fn call_once(self, arg: (i32, i32)) -> i32 {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closure_capture_unsized_type() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: fn, copy, slice, index, coerce_unsized
|
||||
fn f<T: A>(x: &<T as A>::Ty) -> &<T as A>::Ty {
|
||||
let c = || &*x;
|
||||
c()
|
||||
}
|
||||
|
||||
trait A {
|
||||
type Ty;
|
||||
}
|
||||
|
||||
impl A for i32 {
|
||||
type Ty = [u8];
|
||||
}
|
||||
|
||||
const GOAL: u8 = {
|
||||
let k: &[u8] = &[1, 2, 3];
|
||||
let k = f::<i32>(k);
|
||||
k[0] + k[1] + k[2]
|
||||
}
|
||||
"#,
|
||||
6,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closure_and_impl_fn() {
|
||||
check_number(
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
use crate::{
|
||||
consteval::try_const_usize, db::HirDatabase, infer::normalize, layout::adt::struct_variant_idx,
|
||||
utils::ClosureSubst, Interner, Substitution, TraitEnvironment, Ty,
|
||||
utils::ClosureSubst, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty,
|
||||
};
|
||||
|
||||
pub use self::{
|
||||
@ -279,7 +279,15 @@ pub fn layout_of_ty_query(
|
||||
// return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
|
||||
// }
|
||||
|
||||
let unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone());
|
||||
let mut unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone());
|
||||
if let TyKind::AssociatedType(id, subst) = unsized_part.kind(Interner) {
|
||||
unsized_part = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
|
||||
associated_ty_id: *id,
|
||||
substitution: subst.clone(),
|
||||
}))
|
||||
.intern(Interner);
|
||||
}
|
||||
unsized_part = normalize(db, trait_env.clone(), unsized_part);
|
||||
let metadata = match unsized_part.kind(Interner) {
|
||||
TyKind::Slice(_) | TyKind::Str => {
|
||||
scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
|
||||
@ -362,8 +370,16 @@ pub fn layout_of_ty_query(
|
||||
return Err(LayoutError::NotImplemented)
|
||||
}
|
||||
TyKind::Error => return Err(LayoutError::HasErrorType),
|
||||
TyKind::AssociatedType(_, _)
|
||||
| TyKind::Alias(_)
|
||||
TyKind::AssociatedType(id, subst) => {
|
||||
// Try again with `TyKind::Alias` to normalize the associated type.
|
||||
let ty = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
|
||||
associated_ty_id: *id,
|
||||
substitution: subst.clone(),
|
||||
}))
|
||||
.intern(Interner);
|
||||
return db.layout_of_ty(ty, trait_env);
|
||||
}
|
||||
TyKind::Alias(_)
|
||||
| TyKind::Placeholder(_)
|
||||
| TyKind::BoundVar(_)
|
||||
| TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder),
|
||||
|
@ -313,6 +313,7 @@ pub enum MirEvalError {
|
||||
InvalidVTableId(usize),
|
||||
CoerceUnsizedError(Ty),
|
||||
LangItemNotFound(LangItem),
|
||||
BrokenLayout(Layout),
|
||||
}
|
||||
|
||||
impl MirEvalError {
|
||||
@ -399,6 +400,7 @@ pub fn pretty_print(
|
||||
| MirEvalError::TargetDataLayoutNotAvailable
|
||||
| MirEvalError::CoerceUnsizedError(_)
|
||||
| MirEvalError::LangItemNotFound(_)
|
||||
| MirEvalError::BrokenLayout(_)
|
||||
| MirEvalError::InvalidVTableId(_) => writeln!(f, "{:?}", err)?,
|
||||
}
|
||||
Ok(())
|
||||
@ -433,6 +435,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Self::CoerceUnsizedError(arg0) => {
|
||||
f.debug_tuple("CoerceUnsizedError").field(arg0).finish()
|
||||
}
|
||||
Self::BrokenLayout(arg0) => f.debug_tuple("BrokenLayout").field(arg0).finish(),
|
||||
Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
|
||||
Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
|
||||
Self::InvalidConst(arg0) => {
|
||||
@ -1541,12 +1544,18 @@ fn make_by_layout(
|
||||
) -> Result<Vec<u8>> {
|
||||
let mut result = vec![0; size];
|
||||
if let Some((offset, size, value)) = tag {
|
||||
result[offset..offset + size].copy_from_slice(&value.to_le_bytes()[0..size]);
|
||||
match result.get_mut(offset..offset + size) {
|
||||
Some(it) => it.copy_from_slice(&value.to_le_bytes()[0..size]),
|
||||
None => return Err(MirEvalError::BrokenLayout(variant_layout.clone())),
|
||||
}
|
||||
}
|
||||
for (i, op) in values.enumerate() {
|
||||
let offset = variant_layout.fields.offset(i).bytes_usize();
|
||||
let op = op.get(&self)?;
|
||||
result[offset..offset + op.len()].copy_from_slice(op);
|
||||
match result.get_mut(offset..offset + op.len()) {
|
||||
Some(it) => it.copy_from_slice(op),
|
||||
None => return Err(MirEvalError::BrokenLayout(variant_layout.clone())),
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -124,9 +124,85 @@ pub(super) fn detect_and_exec_special_function(
|
||||
destination.write_from_bytes(self, &result)?;
|
||||
return Ok(true);
|
||||
}
|
||||
if let ItemContainerId::TraitId(t) = def.lookup(self.db.upcast()).container {
|
||||
if self.db.lang_attr(t.into()) == Some(LangItem::Clone) {
|
||||
let [self_ty] = generic_args.as_slice(Interner) else {
|
||||
not_supported!("wrong generic arg count for clone");
|
||||
};
|
||||
let Some(self_ty) = self_ty.ty(Interner) else {
|
||||
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(..)) {
|
||||
self.exec_clone(def, args, self_ty.clone(), locals, destination, span)?;
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Clone has special impls for tuples and function pointers
|
||||
fn exec_clone(
|
||||
&mut self,
|
||||
def: FunctionId,
|
||||
args: &[IntervalAndTy],
|
||||
self_ty: Ty,
|
||||
locals: &Locals,
|
||||
destination: Interval,
|
||||
span: MirSpan,
|
||||
) -> Result<()> {
|
||||
match self_ty.kind(Interner) {
|
||||
TyKind::Function(_) => {
|
||||
let [arg] = args else {
|
||||
not_supported!("wrong arg count for clone");
|
||||
};
|
||||
let addr = Address::from_bytes(arg.get(self)?)?;
|
||||
return destination
|
||||
.write_from_interval(self, Interval { addr, size: destination.size });
|
||||
}
|
||||
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,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.exec_fn_with_args(
|
||||
def,
|
||||
args,
|
||||
Substitution::from1(Interner, self_ty),
|
||||
locals,
|
||||
destination,
|
||||
None,
|
||||
span,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_alloc_fn(
|
||||
&mut self,
|
||||
alloc_fn: &str,
|
||||
|
Loading…
Reference in New Issue
Block a user