Support overloaded deref MIR lowering
This commit is contained in:
parent
9564773d5e
commit
eb4939e217
@ -609,21 +609,12 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
|
||||
fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId {
|
||||
let (try_branch, cf_continue, cf_break, try_from_residual) = 'if_chain: {
|
||||
if let Some(try_branch) = LangItem::TryTraitBranch.path(self.db, self.krate) {
|
||||
if let Some(cf_continue) =
|
||||
LangItem::ControlFlowContinue.path(self.db, self.krate)
|
||||
{
|
||||
if let Some(cf_break) =
|
||||
LangItem::ControlFlowBreak.path(self.db, self.krate)
|
||||
{
|
||||
if let Some(cf_continue) = LangItem::ControlFlowContinue.path(self.db, self.krate) {
|
||||
if let Some(cf_break) = LangItem::ControlFlowBreak.path(self.db, self.krate) {
|
||||
if let Some(try_from_residual) =
|
||||
LangItem::TryTraitFromResidual.path(self.db, self.krate)
|
||||
{
|
||||
break 'if_chain (
|
||||
try_branch,
|
||||
cf_continue,
|
||||
cf_break,
|
||||
try_from_residual,
|
||||
);
|
||||
break 'if_chain (try_branch, cf_continue, cf_break, try_from_residual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -634,15 +625,10 @@ fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExp
|
||||
let operand = self.collect_expr_opt(e.expr());
|
||||
let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr.clone());
|
||||
let expr = self.alloc_expr(
|
||||
Expr::Call {
|
||||
callee: try_branch,
|
||||
args: Box::new([operand]),
|
||||
is_assignee_expr: false,
|
||||
},
|
||||
Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false },
|
||||
syntax_ptr.clone(),
|
||||
);
|
||||
let continue_binding =
|
||||
self.alloc_binding(name![v1], BindingAnnotation::Unannotated);
|
||||
let continue_binding = self.alloc_binding(name![v1], BindingAnnotation::Unannotated);
|
||||
let continue_bpat =
|
||||
self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None });
|
||||
self.add_definition_to_binding(continue_binding, continue_bpat);
|
||||
@ -656,8 +642,7 @@ fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExp
|
||||
expr: self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone()),
|
||||
};
|
||||
let break_binding = self.alloc_binding(name![v1], BindingAnnotation::Unannotated);
|
||||
let break_bpat =
|
||||
self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
|
||||
let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
|
||||
self.add_definition_to_binding(break_binding, break_bpat);
|
||||
let break_arm = MatchArm {
|
||||
pat: self.alloc_pat_desugared(Pat::TupleStruct {
|
||||
@ -667,10 +652,8 @@ fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExp
|
||||
}),
|
||||
guard: None,
|
||||
expr: {
|
||||
let x =
|
||||
self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone());
|
||||
let callee =
|
||||
self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone());
|
||||
let x = self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone());
|
||||
let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone());
|
||||
let result = self.alloc_expr(
|
||||
Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
|
||||
syntax_ptr.clone(),
|
||||
@ -1030,8 +1013,9 @@ fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> Pat
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
ast::Pat::LiteralPat(lit) => 'b: {
|
||||
if let Some(ast_lit) = lit.literal() {
|
||||
// FIXME: rustfmt removes this label if it is a block and not a loop
|
||||
ast::Pat::LiteralPat(lit) => 'b: loop {
|
||||
break if let Some(ast_lit) = lit.literal() {
|
||||
let mut hir_lit: Literal = ast_lit.kind().into();
|
||||
if lit.minus_token().is_some() {
|
||||
let Some(h) = hir_lit.negate() else {
|
||||
@ -1045,8 +1029,8 @@ fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> Pat
|
||||
Pat::Lit(expr_id)
|
||||
} else {
|
||||
Pat::Missing
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
ast::Pat::RestPat(_) => {
|
||||
// `RestPat` requires special handling and should not be mapped
|
||||
// to a Pat. Here we are using `Pat::Missing` as a fallback for
|
||||
|
@ -166,8 +166,7 @@ fn g(i: Foo<Foo<i32>>) -> i32 {
|
||||
|
||||
#[test]
|
||||
fn overloaded_deref() {
|
||||
// FIXME: We should support this.
|
||||
check_fail(
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: deref_mut
|
||||
struct Foo;
|
||||
@ -185,9 +184,7 @@ fn deref(&self) -> &i32 {
|
||||
*y + *x
|
||||
};
|
||||
"#,
|
||||
ConstEvalError::MirLowerError(MirLowerError::NotSupported(
|
||||
"explicit overloaded deref".into(),
|
||||
)),
|
||||
10,
|
||||
);
|
||||
}
|
||||
|
||||
@ -698,7 +695,7 @@ const fn f(x: i32) -> i32 {
|
||||
}
|
||||
const GOAL: i32 = f(-1) + f(1) + f(0) + f(-5);
|
||||
"#,
|
||||
211
|
||||
211,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
@ -711,7 +708,7 @@ const fn f(x: &str) -> u8 {
|
||||
}
|
||||
const GOAL: u8 = f("foo") + f("bar");
|
||||
"#,
|
||||
11
|
||||
11,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1116,6 +1113,22 @@ fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
|
||||
"#,
|
||||
15,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, fn
|
||||
fn add2(x: u8) -> u8 {
|
||||
x + 2
|
||||
}
|
||||
fn call(f: &dyn Fn(u8) -> u8, x: u8) -> u8 {
|
||||
f(x)
|
||||
}
|
||||
fn call_mut(f: &mut dyn FnMut(u8) -> u8, x: u8) -> u8 {
|
||||
f(x)
|
||||
}
|
||||
const GOAL: u8 = call(&add2, 3) + call_mut(&mut add2, 3);
|
||||
"#,
|
||||
10,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: fn
|
||||
|
@ -57,6 +57,7 @@
|
||||
mod pat;
|
||||
mod coerce;
|
||||
mod closure;
|
||||
mod mutability;
|
||||
|
||||
/// The entry point of type inference.
|
||||
pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
|
||||
@ -99,6 +100,8 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
|
||||
|
||||
ctx.infer_body();
|
||||
|
||||
ctx.infer_mut_body();
|
||||
|
||||
Arc::new(ctx.resolve_all())
|
||||
}
|
||||
|
||||
|
@ -390,14 +390,28 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
if let Some(fn_x) = func {
|
||||
match fn_x {
|
||||
FnTrait::FnOnce => (),
|
||||
FnTrait::FnMut => adjustments.push(Adjustment::borrow(
|
||||
Mutability::Mut,
|
||||
derefed_callee.clone(),
|
||||
)),
|
||||
FnTrait::Fn => adjustments.push(Adjustment::borrow(
|
||||
Mutability::Not,
|
||||
derefed_callee.clone(),
|
||||
)),
|
||||
FnTrait::FnMut => {
|
||||
if !matches!(
|
||||
derefed_callee.kind(Interner),
|
||||
TyKind::Ref(Mutability::Mut, _, _)
|
||||
) {
|
||||
adjustments.push(Adjustment::borrow(
|
||||
Mutability::Mut,
|
||||
derefed_callee.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
FnTrait::Fn => {
|
||||
if !matches!(
|
||||
derefed_callee.kind(Interner),
|
||||
TyKind::Ref(Mutability::Not, _, _)
|
||||
) {
|
||||
adjustments.push(Adjustment::borrow(
|
||||
Mutability::Not,
|
||||
derefed_callee.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
let trait_ = fn_x
|
||||
.get_id(self.db, self.trait_env.krate)
|
||||
@ -673,6 +687,23 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
// FIXME: Note down method resolution her
|
||||
match op {
|
||||
UnaryOp::Deref => {
|
||||
if let Some(deref_trait) = self
|
||||
.db
|
||||
.lang_item(self.table.trait_env.krate, LangItem::Deref)
|
||||
.and_then(|l| l.as_trait())
|
||||
{
|
||||
if let Some(deref_fn) =
|
||||
self.db.trait_data(deref_trait).method_by_name(&name![deref])
|
||||
{
|
||||
// FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that
|
||||
// the mutability is not wrong, and will be fixed in `self.infer_mut`).
|
||||
self.write_method_resolution(
|
||||
tgt_expr,
|
||||
deref_fn,
|
||||
Substitution::empty(Interner),
|
||||
);
|
||||
}
|
||||
}
|
||||
autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty())
|
||||
}
|
||||
UnaryOp::Neg => {
|
||||
|
182
crates/hir-ty/src/infer/mutability.rs
Normal file
182
crates/hir-ty/src/infer/mutability.rs
Normal file
@ -0,0 +1,182 @@
|
||||
//! Finds if an expression is an immutable context or a mutable context, which is used in selecting
|
||||
//! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar.
|
||||
|
||||
use chalk_ir::Mutability;
|
||||
use hir_def::{
|
||||
expr::{Array, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
|
||||
lang_item::LangItem,
|
||||
};
|
||||
use hir_expand::name;
|
||||
|
||||
use crate::{lower::lower_to_chalk_mutability, Adjust, AutoBorrow, OverloadedDeref};
|
||||
|
||||
use super::InferenceContext;
|
||||
|
||||
impl<'a> InferenceContext<'a> {
|
||||
pub(crate) fn infer_mut_body(&mut self) {
|
||||
self.infer_mut_expr(self.body.body_expr, Mutability::Not);
|
||||
}
|
||||
|
||||
fn infer_mut_expr(&mut self, tgt_expr: ExprId, mut mutability: Mutability) {
|
||||
let mut v = vec![];
|
||||
let adjustments = self.result.expr_adjustments.get_mut(&tgt_expr).unwrap_or(&mut v);
|
||||
for adj in adjustments.iter_mut().rev() {
|
||||
match &mut adj.kind {
|
||||
Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => (),
|
||||
Adjust::Deref(Some(d)) => *d = OverloadedDeref(Some(mutability)),
|
||||
Adjust::Borrow(b) => match b {
|
||||
AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m) => mutability = *m,
|
||||
},
|
||||
}
|
||||
}
|
||||
self.infer_mut_expr_without_adjust(tgt_expr, mutability);
|
||||
}
|
||||
|
||||
fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
|
||||
match &self.body[tgt_expr] {
|
||||
Expr::Missing => (),
|
||||
&Expr::If { condition, then_branch, else_branch } => {
|
||||
self.infer_mut_expr(condition, Mutability::Not);
|
||||
self.infer_mut_expr(then_branch, Mutability::Not);
|
||||
if let Some(else_branch) = else_branch {
|
||||
self.infer_mut_expr(else_branch, Mutability::Not);
|
||||
}
|
||||
}
|
||||
Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)),
|
||||
Expr::Block { id: _, statements, tail, label: _ }
|
||||
| Expr::TryBlock { id: _, statements, tail }
|
||||
| Expr::Async { id: _, statements, tail }
|
||||
| Expr::Const { id: _, statements, tail }
|
||||
| Expr::Unsafe { id: _, statements, tail } => {
|
||||
for st in statements.iter() {
|
||||
match st {
|
||||
Statement::Let { pat, type_ref: _, initializer, else_branch } => {
|
||||
if let Some(i) = initializer {
|
||||
self.infer_mut_expr(*i, self.pat_bound_mutability(*pat));
|
||||
}
|
||||
if let Some(e) = else_branch {
|
||||
self.infer_mut_expr(*e, Mutability::Not);
|
||||
}
|
||||
}
|
||||
Statement::Expr { expr, has_semi: _ } => {
|
||||
self.infer_mut_expr(*expr, Mutability::Not);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(tail) = tail {
|
||||
self.infer_mut_expr(*tail, Mutability::Not);
|
||||
}
|
||||
}
|
||||
&Expr::For { iterable: c, pat: _, body, label: _ }
|
||||
| &Expr::While { condition: c, body, label: _ } => {
|
||||
self.infer_mut_expr(c, Mutability::Not);
|
||||
self.infer_mut_expr(body, Mutability::Not);
|
||||
}
|
||||
Expr::MethodCall { receiver: x, method_name: _, args, generic_args: _ }
|
||||
| Expr::Call { callee: x, args, is_assignee_expr: _ } => {
|
||||
self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*x)));
|
||||
}
|
||||
Expr::Match { expr, arms } => {
|
||||
let m = self.pat_iter_bound_mutability(arms.iter().map(|x| x.pat));
|
||||
self.infer_mut_expr(*expr, m);
|
||||
for arm in arms.iter() {
|
||||
self.infer_mut_expr(arm.expr, Mutability::Not);
|
||||
}
|
||||
}
|
||||
Expr::Yield { expr }
|
||||
| Expr::Yeet { expr }
|
||||
| Expr::Return { expr }
|
||||
| Expr::Break { expr, label: _ } => {
|
||||
if let &Some(expr) = expr {
|
||||
self.infer_mut_expr(expr, Mutability::Not);
|
||||
}
|
||||
}
|
||||
Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => {
|
||||
self.infer_mut_not_expr_iter(fields.iter().map(|x| x.expr).chain(*spread))
|
||||
}
|
||||
&Expr::Index { base, index } => {
|
||||
self.infer_mut_expr(base, mutability);
|
||||
self.infer_mut_expr(index, Mutability::Not);
|
||||
}
|
||||
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
|
||||
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
|
||||
if mutability == Mutability::Mut {
|
||||
if let Some(deref_trait) = self
|
||||
.db
|
||||
.lang_item(self.table.trait_env.krate, LangItem::DerefMut)
|
||||
.and_then(|l| l.as_trait())
|
||||
{
|
||||
if let Some(deref_fn) =
|
||||
self.db.trait_data(deref_trait).method_by_name(&name![deref_mut])
|
||||
{
|
||||
*f = deref_fn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.infer_mut_expr(*expr, mutability);
|
||||
}
|
||||
Expr::Field { expr, name: _ } => {
|
||||
self.infer_mut_expr(*expr, mutability);
|
||||
}
|
||||
Expr::UnaryOp { expr, op: _ }
|
||||
| Expr::Range { lhs: Some(expr), rhs: None, range_type: _ }
|
||||
| Expr::Range { rhs: Some(expr), lhs: None, range_type: _ }
|
||||
| Expr::Await { expr }
|
||||
| Expr::Box { expr }
|
||||
| Expr::Loop { body: expr, label: _ }
|
||||
| Expr::Cast { expr, type_ref: _ } => {
|
||||
self.infer_mut_expr(*expr, Mutability::Not);
|
||||
}
|
||||
Expr::Ref { expr, rawness: _, mutability } => {
|
||||
let mutability = lower_to_chalk_mutability(*mutability);
|
||||
self.infer_mut_expr(*expr, mutability);
|
||||
}
|
||||
Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs })
|
||||
| Expr::BinaryOp { lhs, rhs, op: _ }
|
||||
| Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => {
|
||||
self.infer_mut_expr(*lhs, Mutability::Not);
|
||||
self.infer_mut_expr(*rhs, Mutability::Not);
|
||||
}
|
||||
// not implemented
|
||||
Expr::Closure { .. } => (),
|
||||
Expr::Tuple { exprs, is_assignee_expr: _ }
|
||||
| Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) => {
|
||||
self.infer_mut_not_expr_iter(exprs.iter().copied());
|
||||
}
|
||||
// These don't need any action, as they don't have sub expressions
|
||||
Expr::Range { lhs: None, rhs: None, range_type: _ }
|
||||
| Expr::Literal(_)
|
||||
| Expr::Path(_)
|
||||
| Expr::Continue { .. }
|
||||
| Expr::Underscore => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_mut_not_expr_iter(&mut self, exprs: impl Iterator<Item = ExprId>) {
|
||||
for expr in exprs {
|
||||
self.infer_mut_expr(expr, Mutability::Not);
|
||||
}
|
||||
}
|
||||
|
||||
fn pat_iter_bound_mutability(&self, mut pat: impl Iterator<Item = PatId>) -> Mutability {
|
||||
if pat.any(|p| self.pat_bound_mutability(p) == Mutability::Mut) {
|
||||
Mutability::Mut
|
||||
} else {
|
||||
Mutability::Not
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the pat contains a `ref mut` binding. Such paths makes the context of bounded expressions
|
||||
/// mutable. For example in `let (ref mut x0, ref x1) = *x;` we need to use `DerefMut` for `*x` but in
|
||||
/// `let (ref x0, ref x1) = *x;` we should use `Deref`.
|
||||
fn pat_bound_mutability(&self, pat: PatId) -> Mutability {
|
||||
let mut r = Mutability::Not;
|
||||
self.body.walk_bindings_in_pat(pat, |b| {
|
||||
if self.body.bindings[b].mode == BindingAnnotation::RefMut {
|
||||
r = Mutability::Mut;
|
||||
}
|
||||
});
|
||||
r
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
//! This module provides a MIR interpreter, which is used in const eval.
|
||||
|
||||
use std::{borrow::Cow, collections::HashMap, iter, sync::Arc};
|
||||
use std::{borrow::Cow, collections::HashMap, iter, ops::Range, sync::Arc};
|
||||
|
||||
use base_db::CrateId;
|
||||
use chalk_ir::{
|
||||
@ -109,6 +109,10 @@ fn new(addr: Address, size: usize) -> Self {
|
||||
fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
|
||||
memory.read_memory(self.addr, self.size)
|
||||
}
|
||||
|
||||
fn slice(self, range: Range<usize>) -> Interval {
|
||||
Interval { addr: self.addr.offset(range.start), size: range.len() }
|
||||
}
|
||||
}
|
||||
|
||||
enum IntervalOrOwned {
|
||||
@ -423,7 +427,6 @@ fn interpret_mir(
|
||||
args: impl Iterator<Item = Vec<u8>>,
|
||||
subst: Substitution,
|
||||
) -> Result<Vec<u8>> {
|
||||
dbg!(body.dbg(self.db));
|
||||
if let Some(x) = self.stack_depth_limit.checked_sub(1) {
|
||||
self.stack_depth_limit = x;
|
||||
} else {
|
||||
@ -1360,24 +1363,24 @@ fn exec_fn_trait(
|
||||
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")),
|
||||
},
|
||||
};
|
||||
let mut func_ty = self.operand_ty(func, locals)?;
|
||||
let mut func_data = self.eval_operand(func, locals)?;
|
||||
while let TyKind::Ref(_, _, z) = func_ty.kind(Interner) {
|
||||
func_ty = z.clone();
|
||||
if matches!(func_ty.kind(Interner), TyKind::Dyn(_)) {
|
||||
let id =
|
||||
from_bytes!(usize, &func_data.get(self)?[self.ptr_size()..self.ptr_size() * 2]);
|
||||
func_data = func_data.slice(0..self.ptr_size());
|
||||
func_ty = self.vtable_map.ty(id)?.clone();
|
||||
}
|
||||
let size = self.size_of_sized(&func_ty, locals, "self type of fn trait")?;
|
||||
func_data = Interval { addr: Address::from_bytes(func_data.get(self)?)?, size };
|
||||
}
|
||||
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:?}"),
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
||||
use hir_def::{
|
||||
adt::{VariantData, StructKind},
|
||||
adt::{StructKind, VariantData},
|
||||
body::Body,
|
||||
expr::{
|
||||
Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
|
||||
|
@ -145,10 +145,32 @@ pub(super) fn lower_expr_as_place_without_adjust(
|
||||
self.expr_ty(*expr).kind(Interner),
|
||||
TyKind::Ref(..) | TyKind::Raw(..)
|
||||
) {
|
||||
let Some(_) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||
let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
not_supported!("explicit overloaded deref");
|
||||
return self.lower_overloaded_deref(
|
||||
current,
|
||||
p,
|
||||
self.expr_ty_after_adjustments(*expr),
|
||||
self.expr_ty(expr_id),
|
||||
expr_id.into(),
|
||||
'b: {
|
||||
if let Some((f, _)) = self.infer.method_resolution(expr_id) {
|
||||
if let Some(deref_trait) =
|
||||
self.resolve_lang_item(LangItem::DerefMut)?.as_trait()
|
||||
{
|
||||
if let Some(deref_fn) = self
|
||||
.db
|
||||
.trait_data(deref_trait)
|
||||
.method_by_name(&name![deref_mut])
|
||||
{
|
||||
break 'b deref_fn == f;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
},
|
||||
);
|
||||
}
|
||||
let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||
return Ok(None);
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! A pretty-printer for MIR.
|
||||
|
||||
use std::fmt::{Display, Write, Debug};
|
||||
use std::fmt::{Debug, Display, Write};
|
||||
|
||||
use hir_def::{body::Body, expr::BindingId};
|
||||
use hir_expand::name::Name;
|
||||
@ -24,7 +24,7 @@ pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
|
||||
ctx.result
|
||||
}
|
||||
|
||||
// String with lines is rendered poorly in `dbg!` macros, which I use very much, so this
|
||||
// String with lines is rendered poorly in `dbg` macros, which I use very much, so this
|
||||
// function exists to solve that.
|
||||
pub fn dbg(&self, db: &dyn HirDatabase) -> impl Debug {
|
||||
struct StringDbg(String);
|
||||
|
@ -258,7 +258,6 @@ fn test() {
|
||||
|
||||
#[test]
|
||||
fn coerce_autoderef_block() {
|
||||
// FIXME: We should know mutability in overloaded deref
|
||||
check_no_mismatches(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
@ -268,7 +267,7 @@ fn takes_ref_str(x: &str) {}
|
||||
fn returns_string() -> String { loop {} }
|
||||
fn test() {
|
||||
takes_ref_str(&{ returns_string() });
|
||||
// ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(None))), Borrow(Ref(Not))
|
||||
// ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
@ -1255,7 +1255,6 @@ fn foo<T: Trait>(a: &T) {
|
||||
|
||||
#[test]
|
||||
fn autoderef_visibility_field() {
|
||||
// FIXME: We should know mutability in overloaded deref
|
||||
check(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
@ -1277,7 +1276,7 @@ fn deref(&self) -> &Foo {
|
||||
mod b {
|
||||
fn foo() {
|
||||
let x = super::a::Bar::new().0;
|
||||
// ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None)))
|
||||
// ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not))))
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
|
||||
}
|
||||
}
|
||||
|
@ -566,7 +566,6 @@ fn for_loop() {
|
||||
|
||||
#[test]
|
||||
fn overloaded_deref() {
|
||||
// FIXME: check for false negative
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: deref_mut
|
||||
@ -574,22 +573,36 @@ fn overloaded_deref() {
|
||||
|
||||
struct Foo;
|
||||
impl Deref for Foo {
|
||||
type Target = i32;
|
||||
fn deref(&self) -> &i32 {
|
||||
&5
|
||||
type Target = (i32, u8);
|
||||
fn deref(&self) -> &(i32, u8) {
|
||||
&(5, 2)
|
||||
}
|
||||
}
|
||||
impl DerefMut for Foo {
|
||||
fn deref_mut(&mut self) -> &mut i32 {
|
||||
&mut 5
|
||||
fn deref_mut(&mut self) -> &mut (i32, u8) {
|
||||
&mut (5, 2)
|
||||
}
|
||||
}
|
||||
fn f() {
|
||||
let x = Foo;
|
||||
let mut x = Foo;
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
let y = &*x;
|
||||
let x = Foo;
|
||||
let mut x = Foo;
|
||||
let y: &mut i32 = &mut x;
|
||||
let y = &mut *x;
|
||||
//^^ 💡 error: cannot mutate immutable variable `x`
|
||||
let x = Foo;
|
||||
let x = Foo;
|
||||
let y: &mut (i32, u8) = &mut x;
|
||||
//^^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
let ref mut y = *x;
|
||||
//^^ 💡 error: cannot mutate immutable variable `x`
|
||||
let (ref mut y, _) = *x;
|
||||
//^^ 💡 error: cannot mutate immutable variable `x`
|
||||
match *x {
|
||||
//^^ 💡 error: cannot mutate immutable variable `x`
|
||||
(ref y, _) => (),
|
||||
(_, ref mut y) => (),
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
@ -435,7 +435,7 @@ fn main() {
|
||||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 3415..3423,
|
||||
range: 5805..5813,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
@ -448,7 +448,7 @@ fn main() {
|
||||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 3447..3451,
|
||||
range: 5837..5841,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
@ -468,7 +468,7 @@ fn main() {
|
||||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 3415..3423,
|
||||
range: 5805..5813,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
@ -481,7 +481,7 @@ fn main() {
|
||||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 3447..3451,
|
||||
range: 5837..5841,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
@ -501,7 +501,7 @@ fn main() {
|
||||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 3415..3423,
|
||||
range: 5805..5813,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
@ -514,7 +514,7 @@ fn main() {
|
||||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 3447..3451,
|
||||
range: 5837..5841,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
@ -1126,5 +1126,5 @@ fn benchmark_syntax_highlighting_parser() {
|
||||
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
|
||||
.count()
|
||||
};
|
||||
assert_eq!(hash, 1608);
|
||||
assert_eq!(hash, 1170);
|
||||
}
|
||||
|
@ -613,7 +613,7 @@ Pat =
|
||||
| ConstBlockPat
|
||||
|
||||
LiteralPat =
|
||||
Literal
|
||||
'-'? Literal
|
||||
|
||||
IdentPat =
|
||||
Attr* 'ref'? 'mut'? Name ('@' Pat)?
|
||||
|
@ -1375,8 +1375,8 @@ pub struct LiteralPat {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl LiteralPat {
|
||||
pub fn literal(&self) -> Option<Literal> { support::child(&self.syntax) }
|
||||
pub fn minus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![-]) }
|
||||
pub fn literal(&self) -> Option<Literal> { support::child(&self.syntax) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -535,6 +535,7 @@ fn method_name(&self) -> proc_macro2::Ident {
|
||||
"!" => "excl",
|
||||
"*" => "star",
|
||||
"&" => "amp",
|
||||
"-" => "minus",
|
||||
"_" => "underscore",
|
||||
"." => "dot",
|
||||
".." => "dotdot",
|
||||
|
@ -375,6 +375,68 @@ pub trait FnOnce<Args: Tuple> {
|
||||
type Output;
|
||||
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
|
||||
}
|
||||
|
||||
mod impls {
|
||||
use crate::marker::Tuple;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
|
||||
impl<A: Tuple, F: ?Sized> const Fn<A> for &F
|
||||
where
|
||||
F: ~const Fn<A>,
|
||||
{
|
||||
extern "rust-call" fn call(&self, args: A) -> F::Output {
|
||||
(**self).call(args)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
|
||||
impl<A: Tuple, F: ?Sized> const FnMut<A> for &F
|
||||
where
|
||||
F: ~const Fn<A>,
|
||||
{
|
||||
extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output {
|
||||
(**self).call(args)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
|
||||
impl<A: Tuple, F: ?Sized> const FnOnce<A> for &F
|
||||
where
|
||||
F: ~const Fn<A>,
|
||||
{
|
||||
type Output = F::Output;
|
||||
|
||||
extern "rust-call" fn call_once(self, args: A) -> F::Output {
|
||||
(*self).call(args)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
|
||||
impl<A: Tuple, F: ?Sized> const FnMut<A> for &mut F
|
||||
where
|
||||
F: ~const FnMut<A>,
|
||||
{
|
||||
extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output {
|
||||
(*self).call_mut(args)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
|
||||
impl<A: Tuple, F: ?Sized> const FnOnce<A> for &mut F
|
||||
where
|
||||
F: ~const FnMut<A>,
|
||||
{
|
||||
type Output = F::Output;
|
||||
extern "rust-call" fn call_once(self, args: A) -> F::Output {
|
||||
(*self).call_mut(args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use self::function::{Fn, FnMut, FnOnce};
|
||||
// endregion:fn
|
||||
|
Loading…
Reference in New Issue
Block a user