Support overloaded deref MIR lowering

This commit is contained in:
hkalbasi 2023-03-17 14:02:55 +03:30
parent 9564773d5e
commit eb4939e217
18 changed files with 398 additions and 86 deletions

View File

@ -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

View File

@ -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

View File

@ -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())
}

View File

@ -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(
FnTrait::FnMut => {
if !matches!(
derefed_callee.kind(Interner),
TyKind::Ref(Mutability::Mut, _, _)
) {
adjustments.push(Adjustment::borrow(
Mutability::Mut,
derefed_callee.clone(),
)),
FnTrait::Fn => adjustments.push(Adjustment::borrow(
));
}
}
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 => {

View 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
}
}

View File

@ -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:?}"),

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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))
}
"#,
);

View File

@ -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
}
}

View File

@ -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) => (),
}
}
"#,
);

View File

@ -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: "",

View File

@ -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);
}

View File

@ -613,7 +613,7 @@ Pat =
| ConstBlockPat
LiteralPat =
Literal
'-'? Literal
IdentPat =
Attr* 'ref'? 'mut'? Name ('@' Pat)?

View File

@ -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)]

View File

@ -535,6 +535,7 @@ fn method_name(&self) -> proc_macro2::Ident {
"!" => "excl",
"*" => "star",
"&" => "amp",
"-" => "minus",
"_" => "underscore",
"." => "dot",
".." => "dotdot",

View File

@ -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