mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-28 04:45:05 +00:00
Support function pointer MIR lowering
This commit is contained in:
parent
1b85b43e6f
commit
a063f000ff
14 changed files with 468 additions and 164 deletions
|
@ -343,6 +343,8 @@ pub mod known {
|
||||||
feature,
|
feature,
|
||||||
// known methods of lang items
|
// known methods of lang items
|
||||||
call_once,
|
call_once,
|
||||||
|
call_mut,
|
||||||
|
call,
|
||||||
eq,
|
eq,
|
||||||
ne,
|
ne,
|
||||||
ge,
|
ge,
|
||||||
|
|
|
@ -906,6 +906,108 @@ fn or_pattern() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_pointer() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
fn add2(x: u8) -> u8 {
|
||||||
|
x + 2
|
||||||
|
}
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let plus2 = add2;
|
||||||
|
plus2(3)
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
fn add2(x: u8) -> u8 {
|
||||||
|
x + 2
|
||||||
|
}
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let plus2: fn(u8) -> u8 = add2;
|
||||||
|
plus2(3)
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, index, slice
|
||||||
|
fn add2(x: u8) -> u8 {
|
||||||
|
x + 2
|
||||||
|
}
|
||||||
|
fn mult3(x: u8) -> u8 {
|
||||||
|
x * 3
|
||||||
|
}
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let x = [add2, mult3];
|
||||||
|
x[0](1) + x[1](5)
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
18,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_traits() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: fn
|
||||||
|
fn add2(x: u8) -> u8 {
|
||||||
|
x + 2
|
||||||
|
}
|
||||||
|
fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
const GOAL: u8 = call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3);
|
||||||
|
"#,
|
||||||
|
15,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: fn
|
||||||
|
fn add2(x: u8) -> u8 {
|
||||||
|
x + 2
|
||||||
|
}
|
||||||
|
fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let add2: fn(u8) -> u8 = add2;
|
||||||
|
call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3)
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
15,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: fn
|
||||||
|
fn add2(x: u8) -> u8 {
|
||||||
|
x + 2
|
||||||
|
}
|
||||||
|
fn call(f: &&&&&impl Fn(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
const GOAL: u8 = call(&&&&&add2, 3);
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn array_and_index() {
|
fn array_and_index() {
|
||||||
check_number(
|
check_number(
|
||||||
|
|
|
@ -38,9 +38,9 @@ use stdx::{always, never};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
|
db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
|
||||||
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
|
lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, AliasEq, AliasTy, Const,
|
||||||
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
|
DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId,
|
||||||
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This lint has a false positive here. See the link below for details.
|
// This lint has a false positive here. See the link below for details.
|
||||||
|
@ -273,6 +273,13 @@ pub struct Adjustment {
|
||||||
pub target: Ty,
|
pub target: Ty,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Adjustment {
|
||||||
|
pub fn borrow(m: Mutability, ty: Ty) -> Self {
|
||||||
|
let ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
|
||||||
|
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Adjust {
|
pub enum Adjust {
|
||||||
/// Go from ! to any type.
|
/// Go from ! to any type.
|
||||||
|
|
|
@ -51,11 +51,12 @@ fn success(
|
||||||
pub(super) struct CoerceMany {
|
pub(super) struct CoerceMany {
|
||||||
expected_ty: Ty,
|
expected_ty: Ty,
|
||||||
final_ty: Option<Ty>,
|
final_ty: Option<Ty>,
|
||||||
|
expressions: Vec<ExprId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoerceMany {
|
impl CoerceMany {
|
||||||
pub(super) fn new(expected: Ty) -> Self {
|
pub(super) fn new(expected: Ty) -> Self {
|
||||||
CoerceMany { expected_ty: expected, final_ty: None }
|
CoerceMany { expected_ty: expected, final_ty: None, expressions: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the "expected type" with which this coercion was
|
/// Returns the "expected type" with which this coercion was
|
||||||
|
@ -125,8 +126,15 @@ impl CoerceMany {
|
||||||
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
|
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
|
||||||
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
|
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
|
||||||
if let (Ok(result1), Ok(result2)) = (result1, result2) {
|
if let (Ok(result1), Ok(result2)) = (result1, result2) {
|
||||||
ctx.table.register_infer_ok(result1);
|
ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
|
||||||
ctx.table.register_infer_ok(result2);
|
for &e in &self.expressions {
|
||||||
|
ctx.write_expr_adj(e, result1.value.0.clone());
|
||||||
|
}
|
||||||
|
ctx.table.register_infer_ok(InferOk { value: (), goals: result2.goals });
|
||||||
|
if let Some(expr) = expr {
|
||||||
|
ctx.write_expr_adj(expr, result2.value.0);
|
||||||
|
self.expressions.push(expr);
|
||||||
|
}
|
||||||
return self.final_ty = Some(target_ty);
|
return self.final_ty = Some(target_ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,6 +156,9 @@ impl CoerceMany {
|
||||||
}
|
}
|
||||||
cov_mark::hit!(coerce_merge_fail_fallback);
|
cov_mark::hit!(coerce_merge_fail_fallback);
|
||||||
}
|
}
|
||||||
|
if let Some(expr) = expr {
|
||||||
|
self.expressions.push(expr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ use crate::{
|
||||||
method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
|
method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
|
||||||
primitive::{self, UintTy},
|
primitive::{self, UintTy},
|
||||||
static_lifetime, to_chalk_trait_id,
|
static_lifetime, to_chalk_trait_id,
|
||||||
|
traits::FnTrait,
|
||||||
utils::{generics, Generics},
|
utils::{generics, Generics},
|
||||||
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
|
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
|
||||||
Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt,
|
Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt,
|
||||||
|
@ -385,16 +386,32 @@ impl<'a> InferenceContext<'a> {
|
||||||
|| res.is_none();
|
|| res.is_none();
|
||||||
let (param_tys, ret_ty) = match res {
|
let (param_tys, ret_ty) = match res {
|
||||||
Some((func, params, ret_ty)) => {
|
Some((func, params, ret_ty)) => {
|
||||||
let adjustments = auto_deref_adjust_steps(&derefs);
|
let mut adjustments = auto_deref_adjust_steps(&derefs);
|
||||||
// FIXME: Handle call adjustments for Fn/FnMut
|
if let Some(fn_x) = func {
|
||||||
self.write_expr_adj(*callee, adjustments);
|
match fn_x {
|
||||||
if let Some((trait_, func)) = func {
|
FnTrait::FnOnce => (),
|
||||||
|
FnTrait::FnMut => adjustments.push(Adjustment::borrow(
|
||||||
|
Mutability::Mut,
|
||||||
|
derefed_callee.clone(),
|
||||||
|
)),
|
||||||
|
FnTrait::Fn => adjustments.push(Adjustment::borrow(
|
||||||
|
Mutability::Not,
|
||||||
|
derefed_callee.clone(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
let trait_ = fn_x
|
||||||
|
.get_id(self.db, self.trait_env.krate)
|
||||||
|
.expect("We just used it");
|
||||||
|
let trait_data = self.db.trait_data(trait_);
|
||||||
|
if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
|
||||||
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
|
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
|
||||||
.push(callee_ty.clone())
|
.push(callee_ty.clone())
|
||||||
.push(TyBuilder::tuple_with(params.iter().cloned()))
|
.push(TyBuilder::tuple_with(params.iter().cloned()))
|
||||||
.build();
|
.build();
|
||||||
self.write_method_resolution(tgt_expr, func, subst.clone());
|
self.write_method_resolution(tgt_expr, func, subst.clone());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
self.write_expr_adj(*callee, adjustments);
|
||||||
(params, ret_ty)
|
(params, ret_ty)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -8,16 +8,15 @@ use chalk_ir::{
|
||||||
};
|
};
|
||||||
use chalk_solve::infer::ParameterEnaVariableExt;
|
use chalk_solve::infer::ParameterEnaVariableExt;
|
||||||
use ena::unify::UnifyKey;
|
use ena::unify::UnifyKey;
|
||||||
use hir_def::{FunctionId, TraitId};
|
|
||||||
use hir_expand::name;
|
use hir_expand::name;
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
|
||||||
use super::{InferOk, InferResult, InferenceContext, TypeError};
|
use super::{InferOk, InferResult, InferenceContext, TypeError};
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar,
|
db::HirDatabase, fold_tys, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq,
|
||||||
Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment,
|
AliasTy, BoundVar, Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance,
|
||||||
InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution,
|
InEnvironment, InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt,
|
||||||
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
|
Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'a> InferenceContext<'a> {
|
impl<'a> InferenceContext<'a> {
|
||||||
|
@ -631,7 +630,7 @@ impl<'a> InferenceTable<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
ty: &Ty,
|
ty: &Ty,
|
||||||
num_args: usize,
|
num_args: usize,
|
||||||
) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
|
) -> Option<(Option<FnTrait>, Vec<Ty>, Ty)> {
|
||||||
match ty.callable_sig(self.db) {
|
match ty.callable_sig(self.db) {
|
||||||
Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())),
|
Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())),
|
||||||
None => self.callable_sig_from_fn_trait(ty, num_args),
|
None => self.callable_sig_from_fn_trait(ty, num_args),
|
||||||
|
@ -642,7 +641,7 @@ impl<'a> InferenceTable<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
ty: &Ty,
|
ty: &Ty,
|
||||||
num_args: usize,
|
num_args: usize,
|
||||||
) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
|
) -> Option<(Option<FnTrait>, Vec<Ty>, Ty)> {
|
||||||
let krate = self.trait_env.krate;
|
let krate = self.trait_env.krate;
|
||||||
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
|
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
|
||||||
let trait_data = self.db.trait_data(fn_once_trait);
|
let trait_data = self.db.trait_data(fn_once_trait);
|
||||||
|
@ -676,19 +675,28 @@ impl<'a> InferenceTable<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let trait_env = self.trait_env.env.clone();
|
let trait_env = self.trait_env.env.clone();
|
||||||
|
let mut trait_ref = projection.trait_ref(self.db);
|
||||||
let obligation = InEnvironment {
|
let obligation = InEnvironment {
|
||||||
goal: projection.trait_ref(self.db).cast(Interner),
|
goal: trait_ref.clone().cast(Interner),
|
||||||
environment: trait_env,
|
environment: trait_env.clone(),
|
||||||
};
|
};
|
||||||
let canonical = self.canonicalize(obligation.clone());
|
let canonical = self.canonicalize(obligation.clone());
|
||||||
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
|
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
|
||||||
self.register_obligation(obligation.goal);
|
self.register_obligation(obligation.goal);
|
||||||
let return_ty = self.normalize_projection_ty(projection);
|
let return_ty = self.normalize_projection_ty(projection);
|
||||||
Some((
|
for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
|
||||||
Some(fn_once_trait).zip(trait_data.method_by_name(&name!(call_once))),
|
let fn_x_trait = fn_x.get_id(self.db, krate)?;
|
||||||
arg_tys,
|
trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
|
||||||
return_ty,
|
let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> = InEnvironment {
|
||||||
))
|
goal: trait_ref.clone().cast(Interner),
|
||||||
|
environment: trait_env.clone(),
|
||||||
|
};
|
||||||
|
let canonical = self.canonicalize(obligation.clone());
|
||||||
|
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
|
||||||
|
return Some((Some(fn_x), arg_tys, return_ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable!("It should at least implement FnOnce at this point");
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -576,10 +576,14 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn callable_sig_from_fnonce(
|
pub fn callable_sig_from_fnonce(
|
||||||
self_ty: &Ty,
|
mut self_ty: &Ty,
|
||||||
env: Arc<TraitEnvironment>,
|
env: Arc<TraitEnvironment>,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
) -> Option<CallableSig> {
|
) -> Option<CallableSig> {
|
||||||
|
if let Some((ty, _, _)) = self_ty.as_reference() {
|
||||||
|
// This will happen when it implements fn or fn mut, since we add a autoborrow adjustment
|
||||||
|
self_ty = ty;
|
||||||
|
}
|
||||||
let krate = env.krate;
|
let krate = env.krate;
|
||||||
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
|
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
|
||||||
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
|
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::{
|
||||||
autoderef::{self, AutoderefKind},
|
autoderef::{self, AutoderefKind},
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
from_chalk_trait_id, from_foreign_def_id,
|
from_chalk_trait_id, from_foreign_def_id,
|
||||||
infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
|
infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast},
|
||||||
primitive::{FloatTy, IntTy, UintTy},
|
primitive::{FloatTy, IntTy, UintTy},
|
||||||
static_lifetime, to_chalk_trait_id,
|
static_lifetime, to_chalk_trait_id,
|
||||||
utils::all_super_traits,
|
utils::all_super_traits,
|
||||||
|
@ -600,9 +600,9 @@ impl ReceiverAdjustments {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(m) = self.autoref {
|
if let Some(m) = self.autoref {
|
||||||
ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
|
let a = Adjustment::borrow(m, ty);
|
||||||
adjust
|
ty = a.target.clone();
|
||||||
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() });
|
adjust.push(a);
|
||||||
}
|
}
|
||||||
if self.unsize_array {
|
if self.unsize_array {
|
||||||
ty = 'x: {
|
ty = 'x: {
|
||||||
|
|
|
@ -11,7 +11,7 @@ use hir_def::{
|
||||||
builtin_type::BuiltinType,
|
builtin_type::BuiltinType,
|
||||||
lang_item::{lang_attr, LangItem},
|
lang_item::{lang_attr, LangItem},
|
||||||
layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding, Variants},
|
layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding, Variants},
|
||||||
AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, Lookup, VariantId,
|
AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, VariantId,
|
||||||
};
|
};
|
||||||
use intern::Interned;
|
use intern::Interned;
|
||||||
use la_arena::ArenaMap;
|
use la_arena::ArenaMap;
|
||||||
|
@ -24,8 +24,9 @@ use crate::{
|
||||||
layout::layout_of_ty,
|
layout::layout_of_ty,
|
||||||
mapping::from_chalk,
|
mapping::from_chalk,
|
||||||
method_resolution::lookup_impl_method,
|
method_resolution::lookup_impl_method,
|
||||||
CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, TraitEnvironment, Ty,
|
traits::FnTrait,
|
||||||
TyBuilder, TyExt,
|
CallableDefId, Const, ConstScalar, FnDefId, Interner, MemoryMap, Substitution,
|
||||||
|
TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -33,11 +34,37 @@ use super::{
|
||||||
Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator, UnOp,
|
Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator, UnOp,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct VTableMap {
|
||||||
|
ty_to_id: HashMap<Ty, usize>,
|
||||||
|
id_to_ty: Vec<Ty>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VTableMap {
|
||||||
|
fn id(&mut self, ty: Ty) -> usize {
|
||||||
|
if let Some(x) = self.ty_to_id.get(&ty) {
|
||||||
|
return *x;
|
||||||
|
}
|
||||||
|
let id = self.id_to_ty.len();
|
||||||
|
self.id_to_ty.push(ty.clone());
|
||||||
|
self.ty_to_id.insert(ty, id);
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ty(&self, id: usize) -> Result<&Ty> {
|
||||||
|
self.id_to_ty.get(id).ok_or(MirEvalError::InvalidVTableId(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Evaluator<'a> {
|
pub struct Evaluator<'a> {
|
||||||
db: &'a dyn HirDatabase,
|
db: &'a dyn HirDatabase,
|
||||||
trait_env: Arc<TraitEnvironment>,
|
trait_env: Arc<TraitEnvironment>,
|
||||||
stack: Vec<u8>,
|
stack: Vec<u8>,
|
||||||
heap: Vec<u8>,
|
heap: Vec<u8>,
|
||||||
|
/// We don't really have function pointers, i.e. pointers to some assembly instructions that we can run. Instead, we
|
||||||
|
/// store the type as an interned id in place of function and vtable pointers, and we recover back the type at the
|
||||||
|
/// time of use.
|
||||||
|
vtable_map: VTableMap,
|
||||||
crate_id: CrateId,
|
crate_id: CrateId,
|
||||||
// FIXME: This is a workaround, see the comment on `interpret_mir`
|
// FIXME: This is a workaround, see the comment on `interpret_mir`
|
||||||
assert_placeholder_ty_is_unused: bool,
|
assert_placeholder_ty_is_unused: bool,
|
||||||
|
@ -147,6 +174,7 @@ pub enum MirEvalError {
|
||||||
ExecutionLimitExceeded,
|
ExecutionLimitExceeded,
|
||||||
StackOverflow,
|
StackOverflow,
|
||||||
TargetDataLayoutNotAvailable,
|
TargetDataLayoutNotAvailable,
|
||||||
|
InvalidVTableId(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for MirEvalError {
|
impl std::fmt::Debug for MirEvalError {
|
||||||
|
@ -168,6 +196,7 @@ impl std::fmt::Debug for MirEvalError {
|
||||||
Self::MirLowerError(arg0, arg1) => {
|
Self::MirLowerError(arg0, arg1) => {
|
||||||
f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
|
f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
|
||||||
}
|
}
|
||||||
|
Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
|
||||||
Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
|
Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
|
||||||
Self::InvalidConst(arg0) => {
|
Self::InvalidConst(arg0) => {
|
||||||
let data = &arg0.data(Interner);
|
let data = &arg0.data(Interner);
|
||||||
|
@ -240,6 +269,7 @@ impl Evaluator<'_> {
|
||||||
Evaluator {
|
Evaluator {
|
||||||
stack: vec![0],
|
stack: vec![0],
|
||||||
heap: vec![0],
|
heap: vec![0],
|
||||||
|
vtable_map: VTableMap::default(),
|
||||||
db,
|
db,
|
||||||
trait_env,
|
trait_env,
|
||||||
crate_id,
|
crate_id,
|
||||||
|
@ -461,108 +491,16 @@ impl Evaluator<'_> {
|
||||||
} => {
|
} => {
|
||||||
let fn_ty = self.operand_ty(func, &locals)?;
|
let fn_ty = self.operand_ty(func, &locals)?;
|
||||||
match &fn_ty.data(Interner).kind {
|
match &fn_ty.data(Interner).kind {
|
||||||
|
TyKind::Function(_) => {
|
||||||
|
let bytes = self.eval_operand(func, &locals)?;
|
||||||
|
self.exec_fn_pointer(bytes, destination, args, &locals)?;
|
||||||
|
}
|
||||||
TyKind::FnDef(def, generic_args) => {
|
TyKind::FnDef(def, generic_args) => {
|
||||||
let def: CallableDefId = from_chalk(self.db, *def);
|
self.exec_fn_def(*def, generic_args, destination, args, &locals)?;
|
||||||
let generic_args = self.subst_filler(generic_args, &locals);
|
|
||||||
match def {
|
|
||||||
CallableDefId::FunctionId(def) => {
|
|
||||||
let arg_bytes = args
|
|
||||||
.iter()
|
|
||||||
.map(|x| {
|
|
||||||
Ok(self
|
|
||||||
.eval_operand(x, &locals)?
|
|
||||||
.get(&self)?
|
|
||||||
.to_owned())
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>>>()?
|
|
||||||
.into_iter();
|
|
||||||
let function_data = self.db.function_data(def);
|
|
||||||
let is_intrinsic = match &function_data.abi {
|
|
||||||
Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
|
|
||||||
None => match def.lookup(self.db.upcast()).container {
|
|
||||||
hir_def::ItemContainerId::ExternBlockId(block) => {
|
|
||||||
let id = block.lookup(self.db.upcast()).id;
|
|
||||||
id.item_tree(self.db.upcast())[id.value]
|
|
||||||
.abi
|
|
||||||
.as_deref()
|
|
||||||
== Some("rust-intrinsic")
|
|
||||||
}
|
}
|
||||||
_ => false,
|
x => not_supported!("unknown function type {x:?}"),
|
||||||
},
|
|
||||||
};
|
|
||||||
let result = if is_intrinsic {
|
|
||||||
self.exec_intrinsic(
|
|
||||||
function_data
|
|
||||||
.name
|
|
||||||
.as_text()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.as_str(),
|
|
||||||
arg_bytes,
|
|
||||||
generic_args,
|
|
||||||
&locals,
|
|
||||||
)?
|
|
||||||
} else if let Some(x) = self.detect_lang_function(def) {
|
|
||||||
self.exec_lang_item(x, arg_bytes)?
|
|
||||||
} else {
|
|
||||||
let (imp, generic_args) = lookup_impl_method(
|
|
||||||
self.db,
|
|
||||||
self.trait_env.clone(),
|
|
||||||
def,
|
|
||||||
generic_args.clone(),
|
|
||||||
);
|
|
||||||
let generic_args =
|
|
||||||
self.subst_filler(&generic_args, &locals);
|
|
||||||
let def = imp.into();
|
|
||||||
let mir_body = self
|
|
||||||
.db
|
|
||||||
.mir_body(def)
|
|
||||||
.map_err(|e| MirEvalError::MirLowerError(imp, e))?;
|
|
||||||
self.interpret_mir(&mir_body, arg_bytes, generic_args)
|
|
||||||
.map_err(|e| {
|
|
||||||
MirEvalError::InFunction(imp, Box::new(e))
|
|
||||||
})?
|
|
||||||
};
|
|
||||||
let dest_addr = self.place_addr(destination, &locals)?;
|
|
||||||
self.write_memory(dest_addr, &result)?;
|
|
||||||
}
|
|
||||||
CallableDefId::StructId(id) => {
|
|
||||||
let (size, variant_layout, tag) = self.layout_of_variant(
|
|
||||||
id.into(),
|
|
||||||
generic_args.clone(),
|
|
||||||
&locals,
|
|
||||||
)?;
|
|
||||||
let result = self.make_by_layout(
|
|
||||||
size,
|
|
||||||
&variant_layout,
|
|
||||||
tag,
|
|
||||||
args,
|
|
||||||
&locals,
|
|
||||||
)?;
|
|
||||||
let dest_addr = self.place_addr(destination, &locals)?;
|
|
||||||
self.write_memory(dest_addr, &result)?;
|
|
||||||
}
|
|
||||||
CallableDefId::EnumVariantId(id) => {
|
|
||||||
let (size, variant_layout, tag) = self.layout_of_variant(
|
|
||||||
id.into(),
|
|
||||||
generic_args.clone(),
|
|
||||||
&locals,
|
|
||||||
)?;
|
|
||||||
let result = self.make_by_layout(
|
|
||||||
size,
|
|
||||||
&variant_layout,
|
|
||||||
tag,
|
|
||||||
args,
|
|
||||||
&locals,
|
|
||||||
)?;
|
|
||||||
let dest_addr = self.place_addr(destination, &locals)?;
|
|
||||||
self.write_memory(dest_addr, &result)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
current_block_idx =
|
|
||||||
target.expect("broken mir, function without target");
|
|
||||||
}
|
|
||||||
_ => not_supported!("unknown function type"),
|
|
||||||
}
|
}
|
||||||
|
current_block_idx = target.expect("broken mir, function without target");
|
||||||
}
|
}
|
||||||
Terminator::SwitchInt { discr, targets } => {
|
Terminator::SwitchInt { discr, targets } => {
|
||||||
let val = u128::from_le_bytes(pad16(
|
let val = u128::from_le_bytes(pad16(
|
||||||
|
@ -808,6 +746,16 @@ impl Evaluator<'_> {
|
||||||
not_supported!("creating pointer from exposed address")
|
not_supported!("creating pointer from exposed address")
|
||||||
}
|
}
|
||||||
CastKind::Pointer(cast) => match cast {
|
CastKind::Pointer(cast) => match cast {
|
||||||
|
PointerCast::ReifyFnPointer => {
|
||||||
|
let current_ty = self.operand_ty(operand, locals)?;
|
||||||
|
if let TyKind::FnDef(_, _) = ¤t_ty.data(Interner).kind {
|
||||||
|
let id = self.vtable_map.id(current_ty);
|
||||||
|
let ptr_size = self.ptr_size();
|
||||||
|
Owned(id.to_le_bytes()[0..ptr_size].to_vec())
|
||||||
|
} else {
|
||||||
|
not_supported!("ReifyFnPointer cast of a non FnDef type");
|
||||||
|
}
|
||||||
|
}
|
||||||
PointerCast::Unsize => {
|
PointerCast::Unsize => {
|
||||||
let current_ty = self.operand_ty(operand, locals)?;
|
let current_ty = self.operand_ty(operand, locals)?;
|
||||||
match &target_ty.data(Interner).kind {
|
match &target_ty.data(Interner).kind {
|
||||||
|
@ -920,7 +868,7 @@ impl Evaluator<'_> {
|
||||||
size: usize, // Not neccessarily equal to variant_layout.size
|
size: usize, // Not neccessarily equal to variant_layout.size
|
||||||
variant_layout: &Layout,
|
variant_layout: &Layout,
|
||||||
tag: Option<(usize, usize, i128)>,
|
tag: Option<(usize, usize, i128)>,
|
||||||
values: &Vec<Operand>,
|
values: &[Operand],
|
||||||
locals: &Locals<'_>,
|
locals: &Locals<'_>,
|
||||||
) -> Result<Vec<u8>> {
|
) -> Result<Vec<u8>> {
|
||||||
let mut result = vec![0; size];
|
let mut result = vec![0; size];
|
||||||
|
@ -1140,6 +1088,20 @@ impl Evaluator<'_> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn detect_fn_trait(&self, def: FunctionId) -> Option<FnTrait> {
|
||||||
|
use LangItem::*;
|
||||||
|
let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let l = lang_attr(self.db.upcast(), parent)?;
|
||||||
|
match l {
|
||||||
|
FnOnce => Some(FnTrait::FnOnce),
|
||||||
|
FnMut => Some(FnTrait::FnMut),
|
||||||
|
Fn => Some(FnTrait::Fn),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result<MemoryMap> {
|
fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result<MemoryMap> {
|
||||||
// FIXME: support indirect references
|
// FIXME: support indirect references
|
||||||
let mut mm = MemoryMap::default();
|
let mut mm = MemoryMap::default();
|
||||||
|
@ -1228,7 +1190,134 @@ impl Evaluator<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn exec_lang_item(
|
fn exec_fn_pointer(
|
||||||
|
&mut self,
|
||||||
|
bytes: Interval,
|
||||||
|
destination: &Place,
|
||||||
|
args: &[Operand],
|
||||||
|
locals: &Locals<'_>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let id = from_bytes!(usize, bytes.get(self)?);
|
||||||
|
let next_ty = self.vtable_map.ty(id)?.clone();
|
||||||
|
if let TyKind::FnDef(def, generic_args) = &next_ty.data(Interner).kind {
|
||||||
|
self.exec_fn_def(*def, generic_args, destination, args, &locals)?;
|
||||||
|
} else {
|
||||||
|
return Err(MirEvalError::TypeError("function pointer to non function"));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exec_fn_def(
|
||||||
|
&mut self,
|
||||||
|
def: FnDefId,
|
||||||
|
generic_args: &Substitution,
|
||||||
|
destination: &Place,
|
||||||
|
args: &[Operand],
|
||||||
|
locals: &Locals<'_>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let def: CallableDefId = from_chalk(self.db, def);
|
||||||
|
let generic_args = self.subst_filler(generic_args, &locals);
|
||||||
|
match def {
|
||||||
|
CallableDefId::FunctionId(def) => {
|
||||||
|
let dest_addr = self.place_addr(destination, &locals)?;
|
||||||
|
if let Some(x) = self.detect_fn_trait(def) {
|
||||||
|
self.exec_fn_trait(x, &args, destination, locals)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let arg_bytes = args
|
||||||
|
.iter()
|
||||||
|
.map(|x| Ok(self.eval_operand(x, &locals)?.get(&self)?.to_owned()))
|
||||||
|
.collect::<Result<Vec<_>>>()?
|
||||||
|
.into_iter();
|
||||||
|
let function_data = self.db.function_data(def);
|
||||||
|
let is_intrinsic = match &function_data.abi {
|
||||||
|
Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
|
||||||
|
None => match def.lookup(self.db.upcast()).container {
|
||||||
|
hir_def::ItemContainerId::ExternBlockId(block) => {
|
||||||
|
let id = block.lookup(self.db.upcast()).id;
|
||||||
|
id.item_tree(self.db.upcast())[id.value].abi.as_deref()
|
||||||
|
== Some("rust-intrinsic")
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let result = if is_intrinsic {
|
||||||
|
self.exec_intrinsic(
|
||||||
|
function_data.name.as_text().unwrap_or_default().as_str(),
|
||||||
|
arg_bytes,
|
||||||
|
generic_args,
|
||||||
|
&locals,
|
||||||
|
)?
|
||||||
|
} else if let Some(x) = self.detect_lang_function(def) {
|
||||||
|
self.exec_lang_item(x, arg_bytes)?
|
||||||
|
} else {
|
||||||
|
let (imp, generic_args) = lookup_impl_method(
|
||||||
|
self.db,
|
||||||
|
self.trait_env.clone(),
|
||||||
|
def,
|
||||||
|
generic_args.clone(),
|
||||||
|
);
|
||||||
|
let generic_args = self.subst_filler(&generic_args, &locals);
|
||||||
|
let def = imp.into();
|
||||||
|
let mir_body =
|
||||||
|
self.db.mir_body(def).map_err(|e| MirEvalError::MirLowerError(imp, e))?;
|
||||||
|
self.interpret_mir(&mir_body, arg_bytes, generic_args)
|
||||||
|
.map_err(|e| MirEvalError::InFunction(imp, Box::new(e)))?
|
||||||
|
};
|
||||||
|
self.write_memory(dest_addr, &result)?;
|
||||||
|
}
|
||||||
|
CallableDefId::StructId(id) => {
|
||||||
|
let (size, variant_layout, tag) =
|
||||||
|
self.layout_of_variant(id.into(), generic_args.clone(), &locals)?;
|
||||||
|
let result = self.make_by_layout(size, &variant_layout, tag, args, &locals)?;
|
||||||
|
let dest_addr = self.place_addr(destination, &locals)?;
|
||||||
|
self.write_memory(dest_addr, &result)?;
|
||||||
|
}
|
||||||
|
CallableDefId::EnumVariantId(id) => {
|
||||||
|
let (size, variant_layout, tag) =
|
||||||
|
self.layout_of_variant(id.into(), generic_args.clone(), &locals)?;
|
||||||
|
let result = self.make_by_layout(size, &variant_layout, tag, args, &locals)?;
|
||||||
|
let dest_addr = self.place_addr(destination, &locals)?;
|
||||||
|
self.write_memory(dest_addr, &result)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exec_fn_trait(
|
||||||
|
&mut self,
|
||||||
|
ft: FnTrait,
|
||||||
|
args: &[Operand],
|
||||||
|
destination: &Place,
|
||||||
|
locals: &Locals<'_>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?;
|
||||||
|
let ref_func_ty = self.operand_ty(func, locals)?;
|
||||||
|
let func_ty = match ft {
|
||||||
|
FnTrait::FnOnce => ref_func_ty,
|
||||||
|
FnTrait::FnMut | FnTrait::Fn => match ref_func_ty.as_reference() {
|
||||||
|
Some(x) => x.0.clone(),
|
||||||
|
None => return Err(MirEvalError::TypeError("fn trait with non-reference arg")),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
match &func_ty.data(Interner).kind {
|
||||||
|
TyKind::FnDef(def, subst) => {
|
||||||
|
self.exec_fn_def(*def, subst, destination, &args[1..], locals)?;
|
||||||
|
}
|
||||||
|
TyKind::Function(_) => {
|
||||||
|
let mut func_data = self.eval_operand(func, locals)?;
|
||||||
|
if let FnTrait::FnMut | FnTrait::Fn = ft {
|
||||||
|
let addr = Address::from_bytes(func_data.get(self)?)?;
|
||||||
|
func_data = Interval { addr, size: self.ptr_size() };
|
||||||
|
}
|
||||||
|
self.exec_fn_pointer(func_data, destination, &args[1..], locals)?;
|
||||||
|
}
|
||||||
|
x => not_supported!("Call {ft:?} trait methods with type {x:?}"),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exec_lang_item(
|
||||||
&self,
|
&self,
|
||||||
x: LangItem,
|
x: LangItem,
|
||||||
mut args: std::vec::IntoIter<Vec<u8>>,
|
mut args: std::vec::IntoIter<Vec<u8>>,
|
||||||
|
|
|
@ -298,7 +298,7 @@ impl MirLowerCtx<'_> {
|
||||||
);
|
);
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
ValueNs::StructId(_) => {
|
ValueNs::FunctionId(_) | ValueNs::StructId(_) => {
|
||||||
// It's probably a unit struct or a zero sized function, so no action is needed.
|
// It's probably a unit struct or a zero sized function, so no action is needed.
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
|
@ -445,36 +445,36 @@ impl MirLowerCtx<'_> {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Expr::Call { callee, args, .. } => {
|
Expr::Call { callee, args, .. } => {
|
||||||
|
if let Some((func_id, generic_args)) =
|
||||||
|
self.infer.method_resolution(expr_id) {
|
||||||
|
let ty = chalk_ir::TyKind::FnDef(
|
||||||
|
CallableDefId::FunctionId(func_id).to_chalk(self.db),
|
||||||
|
generic_args,
|
||||||
|
)
|
||||||
|
.intern(Interner);
|
||||||
|
let func = Operand::from_bytes(vec![], ty);
|
||||||
|
return self.lower_call_and_args(
|
||||||
|
func,
|
||||||
|
iter::once(*callee).chain(args.iter().copied()),
|
||||||
|
place,
|
||||||
|
current,
|
||||||
|
self.is_uninhabited(expr_id),
|
||||||
|
);
|
||||||
|
}
|
||||||
let callee_ty = self.expr_ty_after_adjustments(*callee);
|
let callee_ty = self.expr_ty_after_adjustments(*callee);
|
||||||
match &callee_ty.data(Interner).kind {
|
match &callee_ty.data(Interner).kind {
|
||||||
chalk_ir::TyKind::FnDef(..) => {
|
chalk_ir::TyKind::FnDef(..) => {
|
||||||
let func = Operand::from_bytes(vec![], callee_ty.clone());
|
let func = Operand::from_bytes(vec![], callee_ty.clone());
|
||||||
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
|
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
|
||||||
}
|
}
|
||||||
TyKind::Scalar(_)
|
chalk_ir::TyKind::Function(_) => {
|
||||||
| TyKind::Tuple(_, _)
|
let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
|
||||||
| TyKind::Array(_, _)
|
return Ok(None);
|
||||||
| TyKind::Adt(_, _)
|
};
|
||||||
| TyKind::Str
|
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
|
||||||
| TyKind::Foreign(_)
|
|
||||||
| TyKind::Slice(_) => {
|
|
||||||
return Err(MirLowerError::TypeError("function call on data type"))
|
|
||||||
}
|
}
|
||||||
TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
|
TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
|
||||||
TyKind::AssociatedType(_, _)
|
_ => return Err(MirLowerError::TypeError("function call on bad type")),
|
||||||
| TyKind::Raw(_, _)
|
|
||||||
| TyKind::Ref(_, _, _)
|
|
||||||
| TyKind::OpaqueType(_, _)
|
|
||||||
| TyKind::Never
|
|
||||||
| TyKind::Closure(_, _)
|
|
||||||
| TyKind::Generator(_, _)
|
|
||||||
| TyKind::GeneratorWitness(_, _)
|
|
||||||
| TyKind::Placeholder(_)
|
|
||||||
| TyKind::Dyn(_)
|
|
||||||
| TyKind::Alias(_)
|
|
||||||
| TyKind::Function(_)
|
|
||||||
| TyKind::BoundVar(_)
|
|
||||||
| TyKind::InferenceVar(_, _) => not_supported!("dynamic function call"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::MethodCall { receiver, args, .. } => {
|
Expr::MethodCall { receiver, args, .. } => {
|
||||||
|
|
|
@ -301,9 +301,9 @@ impl<'a> MirPrettyCtx<'a> {
|
||||||
w!(self, ")");
|
w!(self, ")");
|
||||||
}
|
}
|
||||||
Rvalue::Cast(ck, op, ty) => {
|
Rvalue::Cast(ck, op, ty) => {
|
||||||
w!(self, "Discriminant({ck:?}");
|
w!(self, "Cast({ck:?}, ");
|
||||||
self.operand(op);
|
self.operand(op);
|
||||||
w!(self, "{})", ty.display(self.db));
|
w!(self, ", {})", ty.display(self.db));
|
||||||
}
|
}
|
||||||
Rvalue::CheckedBinaryOp(b, o1, o2) => {
|
Rvalue::CheckedBinaryOp(b, o1, o2) => {
|
||||||
self.operand(o1);
|
self.operand(o1);
|
||||||
|
|
|
@ -396,10 +396,40 @@ fn test() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn coerce_fn_item_to_fn_ptr_in_array() {
|
||||||
|
check_no_mismatches(
|
||||||
|
r"
|
||||||
|
fn foo(x: u32) -> isize { 1 }
|
||||||
|
fn bar(x: u32) -> isize { 1 }
|
||||||
|
fn test() {
|
||||||
|
let f = [foo, bar];
|
||||||
|
// ^^^ adjustments: Pointer(ReifyFnPointer)
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn coerce_fn_items_in_match_arms() {
|
fn coerce_fn_items_in_match_arms() {
|
||||||
cov_mark::check!(coerce_fn_reification);
|
cov_mark::check!(coerce_fn_reification);
|
||||||
|
|
||||||
|
check_no_mismatches(
|
||||||
|
r"
|
||||||
|
fn foo1(x: u32) -> isize { 1 }
|
||||||
|
fn foo2(x: u32) -> isize { 2 }
|
||||||
|
fn foo3(x: u32) -> isize { 3 }
|
||||||
|
fn test() {
|
||||||
|
let x = match 1 {
|
||||||
|
1 => foo1,
|
||||||
|
// ^^^^ adjustments: Pointer(ReifyFnPointer)
|
||||||
|
2 => foo2,
|
||||||
|
// ^^^^ adjustments: Pointer(ReifyFnPointer)
|
||||||
|
_ => foo3,
|
||||||
|
// ^^^^ adjustments: Pointer(ReifyFnPointer)
|
||||||
|
};
|
||||||
|
x;
|
||||||
|
}",
|
||||||
|
);
|
||||||
check_types(
|
check_types(
|
||||||
r"
|
r"
|
||||||
fn foo1(x: u32) -> isize { 1 }
|
fn foo1(x: u32) -> isize { 1 }
|
||||||
|
|
|
@ -11,6 +11,7 @@ use hir_def::{
|
||||||
lang_item::{LangItem, LangItemTarget},
|
lang_item::{LangItem, LangItemTarget},
|
||||||
TraitId,
|
TraitId,
|
||||||
};
|
};
|
||||||
|
use hir_expand::name::{name, Name};
|
||||||
use stdx::panic_context;
|
use stdx::panic_context;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -187,6 +188,14 @@ impl FnTrait {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn method_name(&self) -> Name {
|
||||||
|
match self {
|
||||||
|
FnTrait::FnOnce => name!(call_once),
|
||||||
|
FnTrait::FnMut => name!(call_mut),
|
||||||
|
FnTrait::Fn => name!(call),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
|
pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
|
||||||
let target = db.lang_item(krate, self.lang_item())?;
|
let target = db.lang_item(krate, self.lang_item())?;
|
||||||
match target {
|
match target {
|
||||||
|
|
|
@ -631,6 +631,31 @@ fn f(inp: (Foo, Foo, Foo, Foo)) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fn_traits() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: fn
|
||||||
|
fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 {
|
||||||
|
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||||
|
x(2)
|
||||||
|
}
|
||||||
|
fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 {
|
||||||
|
x(2)
|
||||||
|
//^ 💡 error: cannot mutate immutable variable `x`
|
||||||
|
}
|
||||||
|
fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 {
|
||||||
|
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||||
|
x(2)
|
||||||
|
}
|
||||||
|
fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
|
||||||
|
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||||
|
x(2)
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn respect_allow_unused_mut() {
|
fn respect_allow_unused_mut() {
|
||||||
// FIXME: respect
|
// FIXME: respect
|
||||||
|
|
Loading…
Reference in a new issue