Merge commit 'baee6b338b' into sync-from-ra

This commit is contained in:
Laurențiu Nicola 2023-08-07 12:03:15 +03:00
parent 0155385b57
commit aa55ce9567
139 changed files with 4248 additions and 1042 deletions

View file

@ -36,6 +36,9 @@ impl Evaluator<'_> {
destination: Interval,
span: MirSpan,
) -> Result<bool> {
if self.not_special_fn_cache.borrow().contains(&def) {
return Ok(false);
}
let function_data = self.db.function_data(def);
let is_intrinsic = match &function_data.abi {
Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
@ -124,9 +127,88 @@ impl Evaluator<'_> {
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);
}
// Return early to prevent caching clone as non special fn.
return Ok(false);
}
}
self.not_special_fn_cache.borrow_mut().insert(def);
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,
@ -618,12 +700,15 @@ impl Evaluator<'_> {
else {
return Err(MirEvalError::TypeError("type_name generic arg is not provided"));
};
let Ok(ty_name) = ty.display_source_code(
let ty_name = match ty.display_source_code(
self.db,
locals.body.owner.module(self.db.upcast()),
true,
) else {
not_supported!("fail in generating type_name using source code display");
) {
Ok(ty_name) => ty_name,
// Fallback to human readable display in case of `Err`. Ideally we want to use `display_source_code` to
// render full paths.
Err(_) => ty.display(self.db).to_string(),
};
let len = ty_name.len();
let addr = self.heap_allocate(len, 1)?;
@ -679,7 +764,22 @@ impl Evaluator<'_> {
let ans = lhs.wrapping_add(rhs);
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
}
"wrapping_sub" | "unchecked_sub" | "ptr_offset_from_unsigned" | "ptr_offset_from" => {
"ptr_offset_from_unsigned" | "ptr_offset_from" => {
let [lhs, rhs] = args else {
return Err(MirEvalError::TypeError("wrapping_sub args are not provided"));
};
let lhs = i128::from_le_bytes(pad16(lhs.get(self)?, false));
let rhs = i128::from_le_bytes(pad16(rhs.get(self)?, false));
let ans = lhs.wrapping_sub(rhs);
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError("ptr_offset_from generic arg is not provided"));
};
let size = self.size_of_sized(ty, locals, "ptr_offset_from arg")? as i128;
let ans = ans / size;
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
}
"wrapping_sub" | "unchecked_sub" => {
let [lhs, rhs] = args else {
return Err(MirEvalError::TypeError("wrapping_sub args are not provided"));
};
@ -1057,7 +1157,14 @@ impl Evaluator<'_> {
_span: MirSpan,
) -> Result<()> {
// We are a single threaded runtime with no UB checking and no optimization, so
// we can implement these as normal functions.
// we can implement atomic intrinsics as normal functions.
if name.starts_with("singlethreadfence_") || name.starts_with("fence_") {
return Ok(());
}
// The rest of atomic intrinsics have exactly one generic arg
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else {
return Err(MirEvalError::TypeError("atomic intrinsic generic arg is not provided"));
};

View file

@ -182,6 +182,50 @@ fn main() {
);
}
#[test]
fn drop_struct_field() {
check_pass(
r#"
//- minicore: drop, add, option, cell, builtin_impls
use core::cell::Cell;
fn should_not_reach() {
_ // FIXME: replace this function with panic when that works
}
struct X<'a>(&'a Cell<i32>);
impl<'a> Drop for X<'a> {
fn drop(&mut self) {
self.0.set(self.0.get() + 1)
}
}
struct Tuple<'a>(X<'a>, X<'a>, X<'a>);
fn main() {
let s = Cell::new(0);
{
let x0 = X(&s);
let xt = Tuple(x0, X(&s), X(&s));
let x1 = xt.1;
if s.get() != 0 {
should_not_reach();
}
drop(xt.0);
if s.get() != 1 {
should_not_reach();
}
}
// FIXME: this should be 3
if s.get() != 2 {
should_not_reach();
}
}
"#,
);
}
#[test]
fn drop_in_place() {
check_pass(
@ -613,6 +657,50 @@ fn main() {
);
}
#[test]
fn self_with_capital_s() {
check_pass(
r#"
//- minicore: fn, add, copy
struct S1;
impl S1 {
fn f() {
Self;
}
}
struct S2 {
f1: i32,
}
impl S2 {
fn f() {
Self { f1: 5 };
}
}
struct S3(i32);
impl S3 {
fn f() {
Self(2);
Self;
let this = Self;
this(2);
}
}
fn main() {
S1::f();
S2::f();
S3::f();
}
"#,
);
}
#[test]
fn syscalls() {
check_pass(