Support overloaded deref MIR lowering
This commit is contained in:
parent
9564773d5e
commit
eb4939e217
@ -609,21 +609,12 @@ impl ExprCollector<'_> {
|
|||||||
fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> 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: {
|
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(try_branch) = LangItem::TryTraitBranch.path(self.db, self.krate) {
|
||||||
if let Some(cf_continue) =
|
if let Some(cf_continue) = LangItem::ControlFlowContinue.path(self.db, self.krate) {
|
||||||
LangItem::ControlFlowContinue.path(self.db, self.krate)
|
if let Some(cf_break) = LangItem::ControlFlowBreak.path(self.db, self.krate) {
|
||||||
{
|
|
||||||
if let Some(cf_break) =
|
|
||||||
LangItem::ControlFlowBreak.path(self.db, self.krate)
|
|
||||||
{
|
|
||||||
if let Some(try_from_residual) =
|
if let Some(try_from_residual) =
|
||||||
LangItem::TryTraitFromResidual.path(self.db, self.krate)
|
LangItem::TryTraitFromResidual.path(self.db, self.krate)
|
||||||
{
|
{
|
||||||
break 'if_chain (
|
break 'if_chain (try_branch, cf_continue, cf_break, try_from_residual);
|
||||||
try_branch,
|
|
||||||
cf_continue,
|
|
||||||
cf_break,
|
|
||||||
try_from_residual,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -634,15 +625,10 @@ impl ExprCollector<'_> {
|
|||||||
let operand = self.collect_expr_opt(e.expr());
|
let operand = self.collect_expr_opt(e.expr());
|
||||||
let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr.clone());
|
let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr.clone());
|
||||||
let expr = self.alloc_expr(
|
let expr = self.alloc_expr(
|
||||||
Expr::Call {
|
Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false },
|
||||||
callee: try_branch,
|
|
||||||
args: Box::new([operand]),
|
|
||||||
is_assignee_expr: false,
|
|
||||||
},
|
|
||||||
syntax_ptr.clone(),
|
syntax_ptr.clone(),
|
||||||
);
|
);
|
||||||
let continue_binding =
|
let continue_binding = self.alloc_binding(name![v1], BindingAnnotation::Unannotated);
|
||||||
self.alloc_binding(name![v1], BindingAnnotation::Unannotated);
|
|
||||||
let continue_bpat =
|
let continue_bpat =
|
||||||
self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None });
|
self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None });
|
||||||
self.add_definition_to_binding(continue_binding, continue_bpat);
|
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()),
|
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_binding = self.alloc_binding(name![v1], BindingAnnotation::Unannotated);
|
||||||
let break_bpat =
|
let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
|
||||||
self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
|
|
||||||
self.add_definition_to_binding(break_binding, break_bpat);
|
self.add_definition_to_binding(break_binding, break_bpat);
|
||||||
let break_arm = MatchArm {
|
let break_arm = MatchArm {
|
||||||
pat: self.alloc_pat_desugared(Pat::TupleStruct {
|
pat: self.alloc_pat_desugared(Pat::TupleStruct {
|
||||||
@ -667,10 +652,8 @@ impl ExprCollector<'_> {
|
|||||||
}),
|
}),
|
||||||
guard: None,
|
guard: None,
|
||||||
expr: {
|
expr: {
|
||||||
let x =
|
let x = self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone());
|
||||||
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 callee =
|
|
||||||
self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone());
|
|
||||||
let result = self.alloc_expr(
|
let result = self.alloc_expr(
|
||||||
Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
|
Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
|
||||||
syntax_ptr.clone(),
|
syntax_ptr.clone(),
|
||||||
@ -1030,8 +1013,9 @@ impl ExprCollector<'_> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Pat::LiteralPat(lit) => 'b: {
|
// FIXME: rustfmt removes this label if it is a block and not a loop
|
||||||
if let Some(ast_lit) = lit.literal() {
|
ast::Pat::LiteralPat(lit) => 'b: loop {
|
||||||
|
break if let Some(ast_lit) = lit.literal() {
|
||||||
let mut hir_lit: Literal = ast_lit.kind().into();
|
let mut hir_lit: Literal = ast_lit.kind().into();
|
||||||
if lit.minus_token().is_some() {
|
if lit.minus_token().is_some() {
|
||||||
let Some(h) = hir_lit.negate() else {
|
let Some(h) = hir_lit.negate() else {
|
||||||
@ -1045,8 +1029,8 @@ impl ExprCollector<'_> {
|
|||||||
Pat::Lit(expr_id)
|
Pat::Lit(expr_id)
|
||||||
} else {
|
} else {
|
||||||
Pat::Missing
|
Pat::Missing
|
||||||
}
|
};
|
||||||
}
|
},
|
||||||
ast::Pat::RestPat(_) => {
|
ast::Pat::RestPat(_) => {
|
||||||
// `RestPat` requires special handling and should not be mapped
|
// `RestPat` requires special handling and should not be mapped
|
||||||
// to a Pat. Here we are using `Pat::Missing` as a fallback for
|
// to a Pat. Here we are using `Pat::Missing` as a fallback for
|
||||||
|
@ -166,8 +166,7 @@ fn reference_autoderef() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn overloaded_deref() {
|
fn overloaded_deref() {
|
||||||
// FIXME: We should support this.
|
check_number(
|
||||||
check_fail(
|
|
||||||
r#"
|
r#"
|
||||||
//- minicore: deref_mut
|
//- minicore: deref_mut
|
||||||
struct Foo;
|
struct Foo;
|
||||||
@ -185,9 +184,7 @@ fn overloaded_deref() {
|
|||||||
*y + *x
|
*y + *x
|
||||||
};
|
};
|
||||||
"#,
|
"#,
|
||||||
ConstEvalError::MirLowerError(MirLowerError::NotSupported(
|
10,
|
||||||
"explicit overloaded deref".into(),
|
|
||||||
)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -698,7 +695,7 @@ fn pattern_matching_literal() {
|
|||||||
}
|
}
|
||||||
const GOAL: i32 = f(-1) + f(1) + f(0) + f(-5);
|
const GOAL: i32 = f(-1) + f(1) + f(0) + f(-5);
|
||||||
"#,
|
"#,
|
||||||
211
|
211,
|
||||||
);
|
);
|
||||||
check_number(
|
check_number(
|
||||||
r#"
|
r#"
|
||||||
@ -711,7 +708,7 @@ fn pattern_matching_literal() {
|
|||||||
}
|
}
|
||||||
const GOAL: u8 = f("foo") + f("bar");
|
const GOAL: u8 = f("foo") + f("bar");
|
||||||
"#,
|
"#,
|
||||||
11
|
11,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1116,6 +1113,22 @@ fn function_traits() {
|
|||||||
"#,
|
"#,
|
||||||
15,
|
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(
|
check_number(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: fn
|
//- minicore: fn
|
||||||
|
@ -57,6 +57,7 @@ mod expr;
|
|||||||
mod pat;
|
mod pat;
|
||||||
mod coerce;
|
mod coerce;
|
||||||
mod closure;
|
mod closure;
|
||||||
|
mod mutability;
|
||||||
|
|
||||||
/// The entry point of type inference.
|
/// The entry point of type inference.
|
||||||
pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
|
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_body();
|
||||||
|
|
||||||
|
ctx.infer_mut_body();
|
||||||
|
|
||||||
Arc::new(ctx.resolve_all())
|
Arc::new(ctx.resolve_all())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,14 +390,28 @@ impl<'a> InferenceContext<'a> {
|
|||||||
if let Some(fn_x) = func {
|
if let Some(fn_x) = func {
|
||||||
match fn_x {
|
match fn_x {
|
||||||
FnTrait::FnOnce => (),
|
FnTrait::FnOnce => (),
|
||||||
FnTrait::FnMut => adjustments.push(Adjustment::borrow(
|
FnTrait::FnMut => {
|
||||||
Mutability::Mut,
|
if !matches!(
|
||||||
derefed_callee.clone(),
|
derefed_callee.kind(Interner),
|
||||||
)),
|
TyKind::Ref(Mutability::Mut, _, _)
|
||||||
FnTrait::Fn => adjustments.push(Adjustment::borrow(
|
) {
|
||||||
Mutability::Not,
|
adjustments.push(Adjustment::borrow(
|
||||||
derefed_callee.clone(),
|
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
|
let trait_ = fn_x
|
||||||
.get_id(self.db, self.trait_env.krate)
|
.get_id(self.db, self.trait_env.krate)
|
||||||
@ -673,6 +687,23 @@ impl<'a> InferenceContext<'a> {
|
|||||||
// FIXME: Note down method resolution her
|
// FIXME: Note down method resolution her
|
||||||
match op {
|
match op {
|
||||||
UnaryOp::Deref => {
|
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())
|
autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty())
|
||||||
}
|
}
|
||||||
UnaryOp::Neg => {
|
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.
|
//! 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 base_db::CrateId;
|
||||||
use chalk_ir::{
|
use chalk_ir::{
|
||||||
@ -109,6 +109,10 @@ impl Interval {
|
|||||||
fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
|
fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
|
||||||
memory.read_memory(self.addr, self.size)
|
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 {
|
enum IntervalOrOwned {
|
||||||
@ -423,7 +427,6 @@ impl Evaluator<'_> {
|
|||||||
args: impl Iterator<Item = Vec<u8>>,
|
args: impl Iterator<Item = Vec<u8>>,
|
||||||
subst: Substitution,
|
subst: Substitution,
|
||||||
) -> Result<Vec<u8>> {
|
) -> Result<Vec<u8>> {
|
||||||
dbg!(body.dbg(self.db));
|
|
||||||
if let Some(x) = self.stack_depth_limit.checked_sub(1) {
|
if let Some(x) = self.stack_depth_limit.checked_sub(1) {
|
||||||
self.stack_depth_limit = x;
|
self.stack_depth_limit = x;
|
||||||
} else {
|
} else {
|
||||||
@ -1360,24 +1363,24 @@ impl Evaluator<'_> {
|
|||||||
locals: &Locals<'_>,
|
locals: &Locals<'_>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?;
|
let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?;
|
||||||
let ref_func_ty = self.operand_ty(func, locals)?;
|
let mut func_ty = self.operand_ty(func, locals)?;
|
||||||
let func_ty = match ft {
|
let mut func_data = self.eval_operand(func, locals)?;
|
||||||
FnTrait::FnOnce => ref_func_ty,
|
while let TyKind::Ref(_, _, z) = func_ty.kind(Interner) {
|
||||||
FnTrait::FnMut | FnTrait::Fn => match ref_func_ty.as_reference() {
|
func_ty = z.clone();
|
||||||
Some(x) => x.0.clone(),
|
if matches!(func_ty.kind(Interner), TyKind::Dyn(_)) {
|
||||||
None => return Err(MirEvalError::TypeError("fn trait with non-reference arg")),
|
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 {
|
match &func_ty.data(Interner).kind {
|
||||||
TyKind::FnDef(def, subst) => {
|
TyKind::FnDef(def, subst) => {
|
||||||
self.exec_fn_def(*def, subst, destination, &args[1..], locals)?;
|
self.exec_fn_def(*def, subst, destination, &args[1..], locals)?;
|
||||||
}
|
}
|
||||||
TyKind::Function(_) => {
|
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)?;
|
self.exec_fn_pointer(func_data, destination, &args[1..], locals)?;
|
||||||
}
|
}
|
||||||
x => not_supported!("Call {ft:?} trait methods with type {x:?}"),
|
x => not_supported!("Call {ft:?} trait methods with type {x:?}"),
|
||||||
|
@ -4,7 +4,7 @@ use std::{iter, mem, sync::Arc};
|
|||||||
|
|
||||||
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
adt::{VariantData, StructKind},
|
adt::{StructKind, VariantData},
|
||||||
body::Body,
|
body::Body,
|
||||||
expr::{
|
expr::{
|
||||||
Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
|
Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
|
||||||
|
@ -145,10 +145,32 @@ impl MirLowerCtx<'_> {
|
|||||||
self.expr_ty(*expr).kind(Interner),
|
self.expr_ty(*expr).kind(Interner),
|
||||||
TyKind::Ref(..) | TyKind::Raw(..)
|
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);
|
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 {
|
let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! A pretty-printer for MIR.
|
//! 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_def::{body::Body, expr::BindingId};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
@ -24,7 +24,7 @@ impl MirBody {
|
|||||||
ctx.result
|
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.
|
// function exists to solve that.
|
||||||
pub fn dbg(&self, db: &dyn HirDatabase) -> impl Debug {
|
pub fn dbg(&self, db: &dyn HirDatabase) -> impl Debug {
|
||||||
struct StringDbg(String);
|
struct StringDbg(String);
|
||||||
|
@ -258,7 +258,6 @@ fn test() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn coerce_autoderef_block() {
|
fn coerce_autoderef_block() {
|
||||||
// FIXME: We should know mutability in overloaded deref
|
|
||||||
check_no_mismatches(
|
check_no_mismatches(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: deref
|
//- minicore: deref
|
||||||
@ -268,7 +267,7 @@ fn takes_ref_str(x: &str) {}
|
|||||||
fn returns_string() -> String { loop {} }
|
fn returns_string() -> String { loop {} }
|
||||||
fn test() {
|
fn test() {
|
||||||
takes_ref_str(&{ returns_string() });
|
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]
|
#[test]
|
||||||
fn autoderef_visibility_field() {
|
fn autoderef_visibility_field() {
|
||||||
// FIXME: We should know mutability in overloaded deref
|
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: deref
|
//- minicore: deref
|
||||||
@ -1277,7 +1276,7 @@ mod a {
|
|||||||
mod b {
|
mod b {
|
||||||
fn foo() {
|
fn foo() {
|
||||||
let x = super::a::Bar::new().0;
|
let x = super::a::Bar::new().0;
|
||||||
// ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None)))
|
// ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not))))
|
||||||
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
|
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -566,7 +566,6 @@ fn f(x: [(i32, u8); 10]) {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn overloaded_deref() {
|
fn overloaded_deref() {
|
||||||
// FIXME: check for false negative
|
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: deref_mut
|
//- minicore: deref_mut
|
||||||
@ -574,22 +573,36 @@ use core::ops::{Deref, DerefMut};
|
|||||||
|
|
||||||
struct Foo;
|
struct Foo;
|
||||||
impl Deref for Foo {
|
impl Deref for Foo {
|
||||||
type Target = i32;
|
type Target = (i32, u8);
|
||||||
fn deref(&self) -> &i32 {
|
fn deref(&self) -> &(i32, u8) {
|
||||||
&5
|
&(5, 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl DerefMut for Foo {
|
impl DerefMut for Foo {
|
||||||
fn deref_mut(&mut self) -> &mut i32 {
|
fn deref_mut(&mut self) -> &mut (i32, u8) {
|
||||||
&mut 5
|
&mut (5, 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn f() {
|
fn f() {
|
||||||
let x = Foo;
|
let mut x = Foo;
|
||||||
|
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||||
let y = &*x;
|
let y = &*x;
|
||||||
let x = Foo;
|
let x = Foo;
|
||||||
let mut x = Foo;
|
let y = &mut *x;
|
||||||
let y: &mut i32 = &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(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 3415..3423,
|
range: 5805..5813,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -448,7 +448,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 3447..3451,
|
range: 5837..5841,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -468,7 +468,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 3415..3423,
|
range: 5805..5813,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -481,7 +481,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 3447..3451,
|
range: 5837..5841,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -501,7 +501,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 3415..3423,
|
range: 5805..5813,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -514,7 +514,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 3447..3451,
|
range: 5837..5841,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
|
@ -1126,5 +1126,5 @@ fn benchmark_syntax_highlighting_parser() {
|
|||||||
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
|
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
|
||||||
.count()
|
.count()
|
||||||
};
|
};
|
||||||
assert_eq!(hash, 1608);
|
assert_eq!(hash, 1170);
|
||||||
}
|
}
|
||||||
|
@ -613,7 +613,7 @@ Pat =
|
|||||||
| ConstBlockPat
|
| ConstBlockPat
|
||||||
|
|
||||||
LiteralPat =
|
LiteralPat =
|
||||||
Literal
|
'-'? Literal
|
||||||
|
|
||||||
IdentPat =
|
IdentPat =
|
||||||
Attr* 'ref'? 'mut'? Name ('@' Pat)?
|
Attr* 'ref'? 'mut'? Name ('@' Pat)?
|
||||||
|
@ -1375,8 +1375,8 @@ pub struct LiteralPat {
|
|||||||
pub(crate) syntax: SyntaxNode,
|
pub(crate) syntax: SyntaxNode,
|
||||||
}
|
}
|
||||||
impl LiteralPat {
|
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 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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -535,6 +535,7 @@ impl Field {
|
|||||||
"!" => "excl",
|
"!" => "excl",
|
||||||
"*" => "star",
|
"*" => "star",
|
||||||
"&" => "amp",
|
"&" => "amp",
|
||||||
|
"-" => "minus",
|
||||||
"_" => "underscore",
|
"_" => "underscore",
|
||||||
"." => "dot",
|
"." => "dot",
|
||||||
".." => "dotdot",
|
".." => "dotdot",
|
||||||
|
@ -375,6 +375,68 @@ pub mod ops {
|
|||||||
type Output;
|
type Output;
|
||||||
extern "rust-call" fn call_once(self, args: Args) -> Self::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};
|
pub use self::function::{Fn, FnMut, FnOnce};
|
||||||
// endregion:fn
|
// endregion:fn
|
||||||
|
Loading…
x
Reference in New Issue
Block a user