Unnamed repository; edit this file 'description' to name the repository.
Support overloaded deref MIR lowering
hkalbasi 2023-03-17
parent 9564773 · commit eb4939e
-rw-r--r--crates/hir-def/src/body/lower.rs42
-rw-r--r--crates/hir-ty/src/consteval/tests.rs27
-rw-r--r--crates/hir-ty/src/infer.rs3
-rw-r--r--crates/hir-ty/src/infer/expr.rs47
-rw-r--r--crates/hir-ty/src/infer/mutability.rs182
-rw-r--r--crates/hir-ty/src/mir/eval.rs33
-rw-r--r--crates/hir-ty/src/mir/lower.rs2
-rw-r--r--crates/hir-ty/src/mir/lower/as_place.rs26
-rw-r--r--crates/hir-ty/src/mir/pretty.rs4
-rw-r--r--crates/hir-ty/src/tests/coercion.rs3
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs3
-rw-r--r--crates/ide-diagnostics/src/handlers/mutability_errors.rs31
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs12
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs2
-rw-r--r--crates/syntax/rust.ungram2
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs2
-rw-r--r--crates/syntax/src/tests/sourcegen_ast.rs1
-rw-r--r--crates/test-utils/src/minicore.rs62
18 files changed, 398 insertions, 86 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 7cce23b531..448821d384 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -609,21 +609,12 @@ impl ExprCollector<'_> {
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 @@ impl ExprCollector<'_> {
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 @@ impl ExprCollector<'_> {
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 @@ impl ExprCollector<'_> {
}),
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 @@ impl ExprCollector<'_> {
.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 @@ impl ExprCollector<'_> {
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
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index ecc163a415..a658bfa0c9 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -166,8 +166,7 @@ fn reference_autoderef() {
#[test]
fn overloaded_deref() {
- // FIXME: We should support this.
- check_fail(
+ check_number(
r#"
//- minicore: deref_mut
struct Foo;
@@ -185,9 +184,7 @@ fn overloaded_deref() {
*y + *x
};
"#,
- ConstEvalError::MirLowerError(MirLowerError::NotSupported(
- "explicit overloaded deref".into(),
- )),
+ 10,
);
}
@@ -698,7 +695,7 @@ fn pattern_matching_literal() {
}
const GOAL: i32 = f(-1) + f(1) + f(0) + f(-5);
"#,
- 211
+ 211,
);
check_number(
r#"
@@ -711,7 +708,7 @@ fn pattern_matching_literal() {
}
const GOAL: u8 = f("foo") + f("bar");
"#,
- 11
+ 11,
);
}
@@ -1118,6 +1115,22 @@ fn function_traits() {
);
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
fn add2(x: u8) -> u8 {
x + 2
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 06d74215ff..d1b9aff36d 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -57,6 +57,7 @@ mod expr;
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())
}
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index ca285f2fec..82119c97ec 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -390,14 +390,28 @@ impl<'a> InferenceContext<'a> {
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 @@ impl<'a> InferenceContext<'a> {
// 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 => {
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
new file mode 100644
index 0000000000..8e3d71788f
--- /dev/null
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -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
+ }
+}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 450cd5404e..f8545e88ad 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -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 @@ impl Interval {
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 @@ impl Evaluator<'_> {
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 @@ impl Evaluator<'_> {
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:?}"),
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 4da0f87609..04175fb4d9 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -4,7 +4,7 @@ use std::{iter, mem, sync::Arc};
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,
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index b683dd7f90..c6f4f66ada 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -145,10 +145,32 @@ impl MirLowerCtx<'_> {
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);
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index ab3bb643e7..9ec2913dce 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -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 @@ impl MirBody {
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);
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index 696bdef03f..9f624cc32c 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -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))
}
"#,
);
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index 378d478336..f3ca93672d 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -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 @@ mod a {
mod b {
fn foo() {
let x = super::a::Bar::new().0;
- // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None)))
+ // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not))))
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
}
}
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 03951ea2bf..83c61f73db 100644
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -566,7 +566,6 @@ fn f(x: [(i32, u8); 10]) {
#[test]
fn overloaded_deref() {
- // FIXME: check for false negative
check_diagnostics(
r#"
//- minicore: deref_mut
@@ -574,22 +573,36 @@ use core::ops::{Deref, DerefMut};
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) => (),
+ }
}
"#,
);
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index 1e1771259b..11e6dc05fa 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -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: "",
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index ac9bd8e39d..5cc3bad04b 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -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);
}
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 548b5ba8b8..1c15a606f9 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -613,7 +613,7 @@ Pat =
| ConstBlockPat
LiteralPat =
- Literal
+ '-'? Literal
IdentPat =
Attr* 'ref'? 'mut'? Name ('@' Pat)?
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index a2124f3ac1..0e84aca5c7 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -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)]
diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs
index e954b58251..77a8363a18 100644
--- a/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/crates/syntax/src/tests/sourcegen_ast.rs
@@ -535,6 +535,7 @@ impl Field {
"!" => "excl",
"*" => "star",
"&" => "amp",
+ "-" => "minus",
"_" => "underscore",
"." => "dot",
".." => "dotdot",
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index bf592c1f7b..118b9ad631 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -375,6 +375,68 @@ pub mod ops {
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