Skip to content

Commit

Permalink
Support manual impl of fn traits in mir interpreter
Browse files Browse the repository at this point in the history
  • Loading branch information
HKalbasi committed Jun 22, 2023
1 parent 3a4f9a1 commit 99a4f2e
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 4 deletions.
24 changes: 24 additions & 0 deletions crates/hir-ty/src/consteval/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1550,6 +1550,30 @@ fn closures() {
);
}

#[test]
fn manual_fn_trait_impl() {
check_number(
r#"
//- minicore: fn, copy
struct S(i32);
impl FnOnce<(i32, i32)> for S {
type Output = i32;
extern "rust-call" fn call_once(self, arg: (i32, i32)) -> i32 {
arg.0 + arg.1 + self.0
}
}
const GOAL: i32 = {
let s = S(1);
s(2, 3)
};
"#,
6,
);
}

#[test]
fn closure_and_impl_fn() {
check_number(
Expand Down
1 change: 1 addition & 0 deletions crates/hir-ty/src/consteval/tests/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ fn transmute() {
fn const_eval_select() {
check_number(
r#"
//- minicore: fn
extern "rust-intrinsic" {
pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET
where
Expand Down
39 changes: 36 additions & 3 deletions crates/hir-ty/src/mir/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1799,7 +1799,7 @@ impl Evaluator<'_> {
match def {
CallableDefId::FunctionId(def) => {
if let Some(_) = self.detect_fn_trait(def) {
self.exec_fn_trait(&args, destination, locals, span)?;
self.exec_fn_trait(def, args, generic_args, locals, destination, span)?;
return Ok(());
}
self.exec_fn_with_args(def, args, generic_args, locals, destination, span)?;
Expand Down Expand Up @@ -1921,9 +1921,11 @@ impl Evaluator<'_> {

fn exec_fn_trait(
&mut self,
def: FunctionId,
args: &[IntervalAndTy],
destination: Interval,
generic_args: Substitution,
locals: &Locals<'_>,
destination: Interval,
span: MirSpan,
) -> Result<()> {
let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?;
Expand Down Expand Up @@ -1958,7 +1960,38 @@ impl Evaluator<'_> {
span,
)?;
}
x => not_supported!("Call FnTrait methods with type {x:?}"),
_ => {
// try to execute the manual impl of `FnTrait` for structs (nightly feature used in std)
let arg0 = func;
let args = &args[1..];
let arg1 = {
let ty = TyKind::Tuple(
args.len(),
Substitution::from_iter(Interner, args.iter().map(|x| x.ty.clone())),
)
.intern(Interner);
let layout = self.layout(&ty)?;
let result = self.make_by_layout(
layout.size.bytes_usize(),
&layout,
None,
args.iter().map(|x| IntervalOrOwned::Borrowed(x.interval)),
)?;
// FIXME: there is some leak here
let size = layout.size.bytes_usize();
let addr = self.heap_allocate(size, layout.align.abi.bytes() as usize);
self.write_memory(addr, &result)?;
IntervalAndTy { interval: Interval { addr, size }, ty }
};
return self.exec_fn_with_args(
def,
&[arg0.clone(), arg1],
generic_args,
locals,
destination,
span,
);
}
}
Ok(())
}
Expand Down
18 changes: 17 additions & 1 deletion crates/hir-ty/src/mir/eval/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,23 @@ impl Evaluator<'_> {
let addr = tuple.interval.addr.offset(offset);
args.push(IntervalAndTy::new(addr, field, self, locals)?);
}
self.exec_fn_trait(&args, destination, locals, span)
if let Some(target) = self.db.lang_item(self.crate_id, LangItem::FnOnce) {
if let Some(def) = target
.as_trait()
.and_then(|x| self.db.trait_data(x).method_by_name(&name![call_once]))
{
return self.exec_fn_trait(
def,
&args,
// FIXME: wrong for manual impls of `FnOnce`
Substitution::empty(Interner),
locals,
destination,
span,
);
}
}
not_supported!("FnOnce was not available for executing const_eval_select");
}
_ => not_supported!("unknown intrinsic {name}"),
}
Expand Down

0 comments on commit 99a4f2e

Please sign in to comment.