mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 01:17:27 +00:00
Auto merge of #14368 - HKalbasi:mir, r=HKalbasi
MIR episode 3 This PR adds lowering for try operator and overloaded dereference, and adds evaluating support for function pointers and trait objects. It also adds a flag to `analysis-stats` to show percentage of functions that it fails to emit mir for them, which is currently `20%` (which is somehow lying, as most of the supported `80%` are tests). The most offenders are closure (1975 items) and overloaded index (415 items). I will try to add overloaded index before monday to have it in this PR, and tackle the closure in the next episode.
This commit is contained in:
commit
3321799e8f
52 changed files with 3100 additions and 937 deletions
|
@ -391,7 +391,7 @@ impl Body {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let expander = Expander::new(db, file_id, module);
|
let expander = Expander::new(db, file_id, module);
|
||||||
let (mut body, source_map) = Body::new(db, expander, params, body);
|
let (mut body, source_map) = Body::new(db, expander, params, body, module.krate);
|
||||||
body.shrink_to_fit();
|
body.shrink_to_fit();
|
||||||
|
|
||||||
(Arc::new(body), Arc::new(source_map))
|
(Arc::new(body), Arc::new(source_map))
|
||||||
|
@ -420,8 +420,9 @@ impl Body {
|
||||||
expander: Expander,
|
expander: Expander,
|
||||||
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
||||||
body: Option<ast::Expr>,
|
body: Option<ast::Expr>,
|
||||||
|
krate: CrateId,
|
||||||
) -> (Body, BodySourceMap) {
|
) -> (Body, BodySourceMap) {
|
||||||
lower::lower(db, expander, params, body)
|
lower::lower(db, expander, params, body, krate)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shrink_to_fit(&mut self) {
|
fn shrink_to_fit(&mut self) {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
use std::{mem, sync::Arc};
|
use std::{mem, sync::Arc};
|
||||||
|
|
||||||
|
use base_db::CrateId;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
ast_id_map::AstIdMap,
|
ast_id_map::AstIdMap,
|
||||||
|
@ -18,7 +19,7 @@ use rustc_hash::FxHashMap;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind,
|
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName, LiteralKind,
|
||||||
SlicePatComponents,
|
SlicePatComponents,
|
||||||
},
|
},
|
||||||
AstNode, AstPtr, SyntaxNodePtr,
|
AstNode, AstPtr, SyntaxNodePtr,
|
||||||
|
@ -36,6 +37,7 @@ use crate::{
|
||||||
RecordFieldPat, RecordLitField, Statement,
|
RecordFieldPat, RecordLitField, Statement,
|
||||||
},
|
},
|
||||||
item_scope::BuiltinShadowMode,
|
item_scope::BuiltinShadowMode,
|
||||||
|
lang_item::LangItem,
|
||||||
path::{GenericArgs, Path},
|
path::{GenericArgs, Path},
|
||||||
type_ref::{Mutability, Rawness, TypeRef},
|
type_ref::{Mutability, Rawness, TypeRef},
|
||||||
AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
|
AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
|
||||||
|
@ -80,9 +82,11 @@ pub(super) fn lower(
|
||||||
expander: Expander,
|
expander: Expander,
|
||||||
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
||||||
body: Option<ast::Expr>,
|
body: Option<ast::Expr>,
|
||||||
|
krate: CrateId,
|
||||||
) -> (Body, BodySourceMap) {
|
) -> (Body, BodySourceMap) {
|
||||||
ExprCollector {
|
ExprCollector {
|
||||||
db,
|
db,
|
||||||
|
krate,
|
||||||
source_map: BodySourceMap::default(),
|
source_map: BodySourceMap::default(),
|
||||||
ast_id_map: db.ast_id_map(expander.current_file_id),
|
ast_id_map: db.ast_id_map(expander.current_file_id),
|
||||||
body: Body {
|
body: Body {
|
||||||
|
@ -96,6 +100,7 @@ pub(super) fn lower(
|
||||||
_c: Count::new(),
|
_c: Count::new(),
|
||||||
},
|
},
|
||||||
expander,
|
expander,
|
||||||
|
current_try_block: None,
|
||||||
is_lowering_assignee_expr: false,
|
is_lowering_assignee_expr: false,
|
||||||
is_lowering_generator: false,
|
is_lowering_generator: false,
|
||||||
}
|
}
|
||||||
|
@ -107,7 +112,9 @@ struct ExprCollector<'a> {
|
||||||
expander: Expander,
|
expander: Expander,
|
||||||
ast_id_map: Arc<AstIdMap>,
|
ast_id_map: Arc<AstIdMap>,
|
||||||
body: Body,
|
body: Body,
|
||||||
|
krate: CrateId,
|
||||||
source_map: BodySourceMap,
|
source_map: BodySourceMap,
|
||||||
|
current_try_block: Option<LabelId>,
|
||||||
is_lowering_assignee_expr: bool,
|
is_lowering_assignee_expr: bool,
|
||||||
is_lowering_generator: bool,
|
is_lowering_generator: bool,
|
||||||
}
|
}
|
||||||
|
@ -176,8 +183,7 @@ impl ExprCollector<'_> {
|
||||||
self.source_map.expr_map.insert(src, id);
|
self.source_map.expr_map.insert(src, id);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
// desugared exprs don't have ptr, that's wrong and should be fixed
|
// FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
|
||||||
// somehow.
|
|
||||||
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
|
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
|
||||||
self.body.exprs.alloc(expr)
|
self.body.exprs.alloc(expr)
|
||||||
}
|
}
|
||||||
|
@ -199,6 +205,10 @@ impl ExprCollector<'_> {
|
||||||
self.source_map.pat_map.insert(src, id);
|
self.source_map.pat_map.insert(src, id);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
// FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
|
||||||
|
fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
|
||||||
|
self.body.pats.alloc(pat)
|
||||||
|
}
|
||||||
fn missing_pat(&mut self) -> PatId {
|
fn missing_pat(&mut self) -> PatId {
|
||||||
self.body.pats.alloc(Pat::Missing)
|
self.body.pats.alloc(Pat::Missing)
|
||||||
}
|
}
|
||||||
|
@ -214,6 +224,10 @@ impl ExprCollector<'_> {
|
||||||
self.source_map.label_map.insert(src, id);
|
self.source_map.label_map.insert(src, id);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
// FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
|
||||||
|
fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
|
||||||
|
self.body.labels.alloc(label)
|
||||||
|
}
|
||||||
fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId {
|
fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId {
|
||||||
let id = self.body.labels.alloc(label);
|
let id = self.body.labels.alloc(label);
|
||||||
self.source_map.label_map_back.insert(id, src);
|
self.source_map.label_map_back.insert(id, src);
|
||||||
|
@ -251,13 +265,7 @@ impl ExprCollector<'_> {
|
||||||
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
|
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
|
||||||
}
|
}
|
||||||
ast::Expr::BlockExpr(e) => match e.modifier() {
|
ast::Expr::BlockExpr(e) => match e.modifier() {
|
||||||
Some(ast::BlockModifier::Try(_)) => {
|
Some(ast::BlockModifier::Try(_)) => self.collect_try_block(e),
|
||||||
self.collect_block_(e, |id, statements, tail| Expr::TryBlock {
|
|
||||||
id,
|
|
||||||
statements,
|
|
||||||
tail,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Some(ast::BlockModifier::Unsafe(_)) => {
|
Some(ast::BlockModifier::Unsafe(_)) => {
|
||||||
self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
|
self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
|
||||||
id,
|
id,
|
||||||
|
@ -437,10 +445,7 @@ impl ExprCollector<'_> {
|
||||||
let expr = self.collect_expr_opt(e.expr());
|
let expr = self.collect_expr_opt(e.expr());
|
||||||
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
|
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
|
||||||
}
|
}
|
||||||
ast::Expr::TryExpr(e) => {
|
ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e),
|
||||||
let expr = self.collect_expr_opt(e.expr());
|
|
||||||
self.alloc_expr(Expr::Try { expr }, syntax_ptr)
|
|
||||||
}
|
|
||||||
ast::Expr::CastExpr(e) => {
|
ast::Expr::CastExpr(e) => {
|
||||||
let expr = self.collect_expr_opt(e.expr());
|
let expr = self.collect_expr_opt(e.expr());
|
||||||
let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
|
let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
|
||||||
|
@ -601,6 +606,126 @@ impl ExprCollector<'_> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
|
||||||
|
/// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
|
||||||
|
/// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
|
||||||
|
fn collect_try_block(&mut self, e: BlockExpr) -> ExprId {
|
||||||
|
let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else {
|
||||||
|
return self.alloc_expr_desugared(Expr::Missing);
|
||||||
|
};
|
||||||
|
let prev_try_block = self.current_try_block.take();
|
||||||
|
self.current_try_block =
|
||||||
|
Some(self.alloc_label_desugared(Label { name: Name::generate_new_name() }));
|
||||||
|
let expr_id = self.collect_block(e);
|
||||||
|
let callee = self.alloc_expr_desugared(Expr::Path(try_from_output));
|
||||||
|
let Expr::Block { label, tail, .. } = &mut self.body.exprs[expr_id] else {
|
||||||
|
unreachable!("It is the output of collect block");
|
||||||
|
};
|
||||||
|
*label = self.current_try_block;
|
||||||
|
let next_tail = match *tail {
|
||||||
|
Some(tail) => self.alloc_expr_desugared(Expr::Call {
|
||||||
|
callee,
|
||||||
|
args: Box::new([tail]),
|
||||||
|
is_assignee_expr: false,
|
||||||
|
}),
|
||||||
|
None => {
|
||||||
|
let unit = self.alloc_expr_desugared(Expr::Tuple {
|
||||||
|
exprs: Box::new([]),
|
||||||
|
is_assignee_expr: false,
|
||||||
|
});
|
||||||
|
self.alloc_expr_desugared(Expr::Call {
|
||||||
|
callee,
|
||||||
|
args: Box::new([unit]),
|
||||||
|
is_assignee_expr: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
|
||||||
|
unreachable!("It is the output of collect block");
|
||||||
|
};
|
||||||
|
*tail = Some(next_tail);
|
||||||
|
self.current_try_block = prev_try_block;
|
||||||
|
expr_id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Desugar `ast::TryExpr` from: `<expr>?` into:
|
||||||
|
/// ```ignore (pseudo-rust)
|
||||||
|
/// match Try::branch(<expr>) {
|
||||||
|
/// ControlFlow::Continue(val) => val,
|
||||||
|
/// ControlFlow::Break(residual) =>
|
||||||
|
/// // If there is an enclosing `try {...}`:
|
||||||
|
/// break 'catch_target Try::from_residual(residual),
|
||||||
|
/// // Otherwise:
|
||||||
|
/// return Try::from_residual(residual),
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
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(try_from_residual) =
|
||||||
|
LangItem::TryTraitFromResidual.path(self.db, self.krate)
|
||||||
|
{
|
||||||
|
break 'if_chain (try_branch, cf_continue, cf_break, try_from_residual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Some of the needed lang items are missing, so we can't desugar
|
||||||
|
return self.alloc_expr(Expr::Missing, syntax_ptr);
|
||||||
|
};
|
||||||
|
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 },
|
||||||
|
syntax_ptr.clone(),
|
||||||
|
);
|
||||||
|
let continue_name = Name::generate_new_name();
|
||||||
|
let continue_binding =
|
||||||
|
self.alloc_binding(continue_name.clone(), 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);
|
||||||
|
let continue_arm = MatchArm {
|
||||||
|
pat: self.alloc_pat_desugared(Pat::TupleStruct {
|
||||||
|
path: Some(Box::new(cf_continue)),
|
||||||
|
args: Box::new([continue_bpat]),
|
||||||
|
ellipsis: None,
|
||||||
|
}),
|
||||||
|
guard: None,
|
||||||
|
expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr.clone()),
|
||||||
|
};
|
||||||
|
let break_name = Name::generate_new_name();
|
||||||
|
let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated);
|
||||||
|
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 {
|
||||||
|
path: Some(Box::new(cf_break)),
|
||||||
|
args: Box::new([break_bpat]),
|
||||||
|
ellipsis: None,
|
||||||
|
}),
|
||||||
|
guard: None,
|
||||||
|
expr: {
|
||||||
|
let x = self.alloc_expr(Expr::Path(Path::from(break_name)), 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(),
|
||||||
|
);
|
||||||
|
if let Some(label) = self.current_try_block {
|
||||||
|
let label = Some(self.body.labels[label].name.clone());
|
||||||
|
self.alloc_expr(Expr::Break { expr: Some(result), label }, syntax_ptr.clone())
|
||||||
|
} else {
|
||||||
|
self.alloc_expr(Expr::Return { expr: Some(result) }, syntax_ptr.clone())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let arms = Box::new([continue_arm, break_arm]);
|
||||||
|
self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
|
||||||
|
}
|
||||||
|
|
||||||
fn collect_macro_call<F, T, U>(
|
fn collect_macro_call<F, T, U>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mcall: ast::MacroCall,
|
mcall: ast::MacroCall,
|
||||||
|
@ -949,16 +1074,24 @@ impl ExprCollector<'_> {
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Pat::LiteralPat(lit) => {
|
// 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 {
|
||||||
let expr = Expr::Literal(ast_lit.kind().into());
|
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 {
|
||||||
|
break 'b Pat::Missing;
|
||||||
|
};
|
||||||
|
hir_lit = h;
|
||||||
|
}
|
||||||
|
let expr = Expr::Literal(hir_lit);
|
||||||
let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
|
let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
|
||||||
let expr_id = self.alloc_expr(expr, expr_ptr);
|
let expr_id = self.alloc_expr(expr, expr_ptr);
|
||||||
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
|
||||||
|
@ -1063,11 +1196,11 @@ impl From<ast::LiteralKind> for Literal {
|
||||||
FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
|
FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
|
||||||
builtin,
|
builtin,
|
||||||
)
|
)
|
||||||
} else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinInt::from_suffix) {
|
} else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
|
||||||
Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
|
|
||||||
} else {
|
|
||||||
let builtin = lit.suffix().and_then(BuiltinUint::from_suffix);
|
|
||||||
Literal::Uint(lit.value().unwrap_or(0), builtin)
|
Literal::Uint(lit.value().unwrap_or(0), builtin)
|
||||||
|
} else {
|
||||||
|
let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
|
||||||
|
Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LiteralKind::FloatNumber(lit) => {
|
LiteralKind::FloatNumber(lit) => {
|
||||||
|
|
|
@ -288,10 +288,6 @@ impl<'a> Printer<'a> {
|
||||||
self.print_expr(*expr);
|
self.print_expr(*expr);
|
||||||
w!(self, ".await");
|
w!(self, ".await");
|
||||||
}
|
}
|
||||||
Expr::Try { expr } => {
|
|
||||||
self.print_expr(*expr);
|
|
||||||
w!(self, "?");
|
|
||||||
}
|
|
||||||
Expr::Cast { expr, type_ref } => {
|
Expr::Cast { expr, type_ref } => {
|
||||||
self.print_expr(*expr);
|
self.print_expr(*expr);
|
||||||
w!(self, " as ");
|
w!(self, " as ");
|
||||||
|
@ -424,9 +420,6 @@ impl<'a> Printer<'a> {
|
||||||
Expr::Unsafe { id: _, statements, tail } => {
|
Expr::Unsafe { id: _, statements, tail } => {
|
||||||
self.print_block(Some("unsafe "), statements, tail);
|
self.print_block(Some("unsafe "), statements, tail);
|
||||||
}
|
}
|
||||||
Expr::TryBlock { id: _, statements, tail } => {
|
|
||||||
self.print_block(Some("try "), statements, tail);
|
|
||||||
}
|
|
||||||
Expr::Async { id: _, statements, tail } => {
|
Expr::Async { id: _, statements, tail } => {
|
||||||
self.print_block(Some("async "), statements, tail);
|
self.print_block(Some("async "), statements, tail);
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,8 +202,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
|
||||||
}
|
}
|
||||||
Expr::Unsafe { id, statements, tail }
|
Expr::Unsafe { id, statements, tail }
|
||||||
| Expr::Async { id, statements, tail }
|
| Expr::Async { id, statements, tail }
|
||||||
| Expr::Const { id, statements, tail }
|
| Expr::Const { id, statements, tail } => {
|
||||||
| Expr::TryBlock { id, statements, tail } => {
|
|
||||||
let mut scope = scopes.new_block_scope(*scope, *id, None);
|
let mut scope = scopes.new_block_scope(*scope, *id, None);
|
||||||
// Overwrite the old scope for the block expr, so that every block scope can be found
|
// Overwrite the old scope for the block expr, so that every block scope can be found
|
||||||
// via the block itself (important for blocks that only contain items, no expressions).
|
// via the block itself (important for blocks that only contain items, no expressions).
|
||||||
|
|
|
@ -92,6 +92,16 @@ pub enum Literal {
|
||||||
Float(FloatTypeWrapper, Option<BuiltinFloat>),
|
Float(FloatTypeWrapper, Option<BuiltinFloat>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Literal {
|
||||||
|
pub fn negate(self) -> Option<Self> {
|
||||||
|
if let Literal::Int(i, k) = self {
|
||||||
|
Some(Literal::Int(-i, k))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
/// This is produced if the syntax tree does not have a required expression piece.
|
/// This is produced if the syntax tree does not have a required expression piece.
|
||||||
|
@ -112,11 +122,6 @@ pub enum Expr {
|
||||||
tail: Option<ExprId>,
|
tail: Option<ExprId>,
|
||||||
label: Option<LabelId>,
|
label: Option<LabelId>,
|
||||||
},
|
},
|
||||||
TryBlock {
|
|
||||||
id: BlockId,
|
|
||||||
statements: Box<[Statement]>,
|
|
||||||
tail: Option<ExprId>,
|
|
||||||
},
|
|
||||||
Async {
|
Async {
|
||||||
id: BlockId,
|
id: BlockId,
|
||||||
statements: Box<[Statement]>,
|
statements: Box<[Statement]>,
|
||||||
|
@ -192,9 +197,6 @@ pub enum Expr {
|
||||||
Await {
|
Await {
|
||||||
expr: ExprId,
|
expr: ExprId,
|
||||||
},
|
},
|
||||||
Try {
|
|
||||||
expr: ExprId,
|
|
||||||
},
|
|
||||||
Cast {
|
Cast {
|
||||||
expr: ExprId,
|
expr: ExprId,
|
||||||
type_ref: Interned<TypeRef>,
|
type_ref: Interned<TypeRef>,
|
||||||
|
@ -303,7 +305,6 @@ impl Expr {
|
||||||
f(*expr);
|
f(*expr);
|
||||||
}
|
}
|
||||||
Expr::Block { statements, tail, .. }
|
Expr::Block { statements, tail, .. }
|
||||||
| Expr::TryBlock { statements, tail, .. }
|
|
||||||
| Expr::Unsafe { statements, tail, .. }
|
| Expr::Unsafe { statements, tail, .. }
|
||||||
| Expr::Async { statements, tail, .. }
|
| Expr::Async { statements, tail, .. }
|
||||||
| Expr::Const { statements, tail, .. } => {
|
| Expr::Const { statements, tail, .. } => {
|
||||||
|
@ -383,7 +384,6 @@ impl Expr {
|
||||||
}
|
}
|
||||||
Expr::Field { expr, .. }
|
Expr::Field { expr, .. }
|
||||||
| Expr::Await { expr }
|
| Expr::Await { expr }
|
||||||
| Expr::Try { expr }
|
|
||||||
| Expr::Cast { expr, .. }
|
| Expr::Cast { expr, .. }
|
||||||
| Expr::Ref { expr, .. }
|
| Expr::Ref { expr, .. }
|
||||||
| Expr::UnaryOp { expr, .. }
|
| Expr::UnaryOp { expr, .. }
|
||||||
|
|
|
@ -8,8 +8,8 @@ use rustc_hash::FxHashMap;
|
||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId,
|
db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
|
||||||
ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
|
FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -220,11 +220,6 @@ macro_rules! language_item_table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opposite of [`LangItem::name`]
|
|
||||||
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
|
|
||||||
Self::from_str(name.as_str()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Opposite of [`LangItem::name`]
|
/// Opposite of [`LangItem::name`]
|
||||||
pub fn from_str(name: &str) -> Option<Self> {
|
pub fn from_str(name: &str) -> Option<Self> {
|
||||||
match name {
|
match name {
|
||||||
|
@ -236,6 +231,18 @@ macro_rules! language_item_table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LangItem {
|
||||||
|
/// Opposite of [`LangItem::name`]
|
||||||
|
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
|
||||||
|
Self::from_str(name.as_str()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
|
||||||
|
let t = db.lang_item(start_crate, *self)?;
|
||||||
|
Some(Path::LangItem(t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
language_item_table! {
|
language_item_table! {
|
||||||
// Variant name, Name, Getter method name, Target Generic requirements;
|
// Variant name, Name, Getter method name, Target Generic requirements;
|
||||||
Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
|
Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||||
|
|
|
@ -8,6 +8,7 @@ use std::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::LowerCtx,
|
body::LowerCtx,
|
||||||
|
lang_item::LangItemTarget,
|
||||||
type_ref::{ConstRefOrPath, LifetimeRef},
|
type_ref::{ConstRefOrPath, LifetimeRef},
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
|
@ -36,13 +37,19 @@ impl Display for ImportAlias {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Path {
|
pub enum Path {
|
||||||
/// Type based path like `<T>::foo`.
|
/// A normal path
|
||||||
/// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
|
Normal {
|
||||||
type_anchor: Option<Interned<TypeRef>>,
|
/// Type based path like `<T>::foo`.
|
||||||
mod_path: Interned<ModPath>,
|
/// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
|
||||||
/// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
|
type_anchor: Option<Interned<TypeRef>>,
|
||||||
generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
|
mod_path: Interned<ModPath>,
|
||||||
|
/// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
|
||||||
|
generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
|
||||||
|
},
|
||||||
|
/// A link to a lang item. It is used in desugaring of things like `x?`. We can show these
|
||||||
|
/// links via a normal path since they might be private and not accessible in the usage place.
|
||||||
|
LangItem(LangItemTarget),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
|
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
|
||||||
|
@ -102,51 +109,77 @@ impl Path {
|
||||||
) -> Path {
|
) -> Path {
|
||||||
let generic_args = generic_args.into();
|
let generic_args = generic_args.into();
|
||||||
assert_eq!(path.len(), generic_args.len());
|
assert_eq!(path.len(), generic_args.len());
|
||||||
Path { type_anchor: None, mod_path: Interned::new(path), generic_args: Some(generic_args) }
|
Path::Normal {
|
||||||
|
type_anchor: None,
|
||||||
|
mod_path: Interned::new(path),
|
||||||
|
generic_args: Some(generic_args),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a known mod path to `Path`.
|
||||||
|
pub fn from_known_path_with_no_generic(path: ModPath) -> Path {
|
||||||
|
Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> &PathKind {
|
pub fn kind(&self) -> &PathKind {
|
||||||
&self.mod_path.kind
|
match self {
|
||||||
|
Path::Normal { mod_path, .. } => &mod_path.kind,
|
||||||
|
Path::LangItem(_) => &PathKind::Abs,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_anchor(&self) -> Option<&TypeRef> {
|
pub fn type_anchor(&self) -> Option<&TypeRef> {
|
||||||
self.type_anchor.as_deref()
|
match self {
|
||||||
|
Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
|
||||||
|
Path::LangItem(_) => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn segments(&self) -> PathSegments<'_> {
|
pub fn segments(&self) -> PathSegments<'_> {
|
||||||
let s = PathSegments {
|
let Path::Normal { mod_path, generic_args, .. } = self else {
|
||||||
segments: self.mod_path.segments(),
|
return PathSegments {
|
||||||
generic_args: self.generic_args.as_deref(),
|
segments: &[],
|
||||||
|
generic_args: None,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
let s =
|
||||||
|
PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
|
||||||
if let Some(generic_args) = s.generic_args {
|
if let Some(generic_args) = s.generic_args {
|
||||||
assert_eq!(s.segments.len(), generic_args.len());
|
assert_eq!(s.segments.len(), generic_args.len());
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mod_path(&self) -> &ModPath {
|
pub fn mod_path(&self) -> Option<&ModPath> {
|
||||||
&self.mod_path
|
match self {
|
||||||
|
Path::Normal { mod_path, .. } => Some(&mod_path),
|
||||||
|
Path::LangItem(_) => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn qualifier(&self) -> Option<Path> {
|
pub fn qualifier(&self) -> Option<Path> {
|
||||||
if self.mod_path.is_ident() {
|
let Path::Normal { mod_path, generic_args, type_anchor } = self else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
if mod_path.is_ident() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let res = Path {
|
let res = Path::Normal {
|
||||||
type_anchor: self.type_anchor.clone(),
|
type_anchor: type_anchor.clone(),
|
||||||
mod_path: Interned::new(ModPath::from_segments(
|
mod_path: Interned::new(ModPath::from_segments(
|
||||||
self.mod_path.kind,
|
mod_path.kind,
|
||||||
self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(),
|
mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
|
||||||
)),
|
)),
|
||||||
generic_args: self.generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
|
generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
|
||||||
};
|
};
|
||||||
Some(res)
|
Some(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_self_type(&self) -> bool {
|
pub fn is_self_type(&self) -> bool {
|
||||||
self.type_anchor.is_none()
|
let Path::Normal { mod_path, generic_args, type_anchor } = self else {
|
||||||
&& self.generic_args.as_deref().is_none()
|
return false;
|
||||||
&& self.mod_path.is_Self()
|
};
|
||||||
|
type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +255,7 @@ impl GenericArgs {
|
||||||
|
|
||||||
impl From<Name> for Path {
|
impl From<Name> for Path {
|
||||||
fn from(name: Name) -> Path {
|
fn from(name: Name) -> Path {
|
||||||
Path {
|
Path::Normal {
|
||||||
type_anchor: None,
|
type_anchor: None,
|
||||||
mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
|
mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
|
||||||
generic_args: None,
|
generic_args: None,
|
||||||
|
|
|
@ -75,8 +75,11 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
|
||||||
}
|
}
|
||||||
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
||||||
Some(trait_ref) => {
|
Some(trait_ref) => {
|
||||||
let Path { mod_path, generic_args: path_generic_args, .. } =
|
let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
|
||||||
Path::from_src(trait_ref.path()?, ctx)?;
|
Path::from_src(trait_ref.path()?, ctx)? else
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
};
|
||||||
let num_segments = mod_path.segments().len();
|
let num_segments = mod_path.segments().len();
|
||||||
kind = mod_path.kind;
|
kind = mod_path.kind;
|
||||||
|
|
||||||
|
@ -157,7 +160,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
|
||||||
}
|
}
|
||||||
|
|
||||||
let mod_path = Interned::new(ModPath::from_segments(kind, segments));
|
let mod_path = Interned::new(ModPath::from_segments(kind, segments));
|
||||||
return Some(Path {
|
return Some(Path::Normal {
|
||||||
type_anchor,
|
type_anchor,
|
||||||
mod_path,
|
mod_path,
|
||||||
generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },
|
generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },
|
||||||
|
|
|
@ -15,8 +15,9 @@ use crate::{
|
||||||
expr::{BindingId, ExprId, LabelId},
|
expr::{BindingId, ExprId, LabelId},
|
||||||
generics::{GenericParams, TypeOrConstParamData},
|
generics::{GenericParams, TypeOrConstParamData},
|
||||||
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
|
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
|
||||||
|
lang_item::LangItemTarget,
|
||||||
nameres::DefMap,
|
nameres::DefMap,
|
||||||
path::{ModPath, PathKind},
|
path::{ModPath, Path, PathKind},
|
||||||
per_ns::PerNs,
|
per_ns::PerNs,
|
||||||
visibility::{RawVisibility, Visibility},
|
visibility::{RawVisibility, Visibility},
|
||||||
AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
|
AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
|
||||||
|
@ -176,8 +177,27 @@ impl Resolver {
|
||||||
pub fn resolve_path_in_type_ns(
|
pub fn resolve_path_in_type_ns(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
path: &ModPath,
|
path: &Path,
|
||||||
) -> Option<(TypeNs, Option<usize>)> {
|
) -> Option<(TypeNs, Option<usize>)> {
|
||||||
|
let path = match path {
|
||||||
|
Path::Normal { mod_path, .. } => mod_path,
|
||||||
|
Path::LangItem(l) => {
|
||||||
|
return Some((
|
||||||
|
match *l {
|
||||||
|
LangItemTarget::Union(x) => TypeNs::AdtId(x.into()),
|
||||||
|
LangItemTarget::TypeAlias(x) => TypeNs::TypeAliasId(x),
|
||||||
|
LangItemTarget::Struct(x) => TypeNs::AdtId(x.into()),
|
||||||
|
LangItemTarget::EnumVariant(x) => TypeNs::EnumVariantId(x),
|
||||||
|
LangItemTarget::EnumId(x) => TypeNs::AdtId(x.into()),
|
||||||
|
LangItemTarget::Trait(x) => TypeNs::TraitId(x),
|
||||||
|
LangItemTarget::Function(_)
|
||||||
|
| LangItemTarget::ImplDef(_)
|
||||||
|
| LangItemTarget::Static(_) => return None,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
let first_name = path.segments().first()?;
|
let first_name = path.segments().first()?;
|
||||||
let skip_to_mod = path.kind != PathKind::Plain;
|
let skip_to_mod = path.kind != PathKind::Plain;
|
||||||
if skip_to_mod {
|
if skip_to_mod {
|
||||||
|
@ -217,7 +237,7 @@ impl Resolver {
|
||||||
pub fn resolve_path_in_type_ns_fully(
|
pub fn resolve_path_in_type_ns_fully(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
path: &ModPath,
|
path: &Path,
|
||||||
) -> Option<TypeNs> {
|
) -> Option<TypeNs> {
|
||||||
let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
|
let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
|
||||||
if unresolved.is_some() {
|
if unresolved.is_some() {
|
||||||
|
@ -245,8 +265,24 @@ impl Resolver {
|
||||||
pub fn resolve_path_in_value_ns(
|
pub fn resolve_path_in_value_ns(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
path: &ModPath,
|
path: &Path,
|
||||||
) -> Option<ResolveValueResult> {
|
) -> Option<ResolveValueResult> {
|
||||||
|
let path = match path {
|
||||||
|
Path::Normal { mod_path, .. } => mod_path,
|
||||||
|
Path::LangItem(l) => {
|
||||||
|
return Some(ResolveValueResult::ValueNs(match *l {
|
||||||
|
LangItemTarget::Function(x) => ValueNs::FunctionId(x),
|
||||||
|
LangItemTarget::Static(x) => ValueNs::StaticId(x),
|
||||||
|
LangItemTarget::Struct(x) => ValueNs::StructId(x),
|
||||||
|
LangItemTarget::EnumVariant(x) => ValueNs::EnumVariantId(x),
|
||||||
|
LangItemTarget::Union(_)
|
||||||
|
| LangItemTarget::ImplDef(_)
|
||||||
|
| LangItemTarget::TypeAlias(_)
|
||||||
|
| LangItemTarget::Trait(_)
|
||||||
|
| LangItemTarget::EnumId(_) => return None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
};
|
||||||
let n_segments = path.segments().len();
|
let n_segments = path.segments().len();
|
||||||
let tmp = name![self];
|
let tmp = name![self];
|
||||||
let first_name = if path.is_self() { &tmp } else { path.segments().first()? };
|
let first_name = if path.is_self() { &tmp } else { path.segments().first()? };
|
||||||
|
@ -340,7 +376,7 @@ impl Resolver {
|
||||||
pub fn resolve_path_in_value_ns_fully(
|
pub fn resolve_path_in_value_ns_fully(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
path: &ModPath,
|
path: &Path,
|
||||||
) -> Option<ValueNs> {
|
) -> Option<ValueNs> {
|
||||||
match self.resolve_path_in_value_ns(db, path)? {
|
match self.resolve_path_in_value_ns(db, path)? {
|
||||||
ResolveValueResult::ValueNs(it) => Some(it),
|
ResolveValueResult::ValueNs(it) => Some(it),
|
||||||
|
@ -441,7 +477,7 @@ impl Resolver {
|
||||||
&Scope::ImplDefScope(impl_) => {
|
&Scope::ImplDefScope(impl_) => {
|
||||||
if let Some(target_trait) = &db.impl_data(impl_).target_trait {
|
if let Some(target_trait) = &db.impl_data(impl_).target_trait {
|
||||||
if let Some(TypeNs::TraitId(trait_)) =
|
if let Some(TypeNs::TraitId(trait_)) =
|
||||||
self.resolve_path_in_type_ns_fully(db, target_trait.path.mod_path())
|
self.resolve_path_in_type_ns_fully(db, &target_trait.path)
|
||||||
{
|
{
|
||||||
traits.insert(trait_);
|
traits.insert(trait_);
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ impl Name {
|
||||||
Self::new_text(lt.text().into())
|
Self::new_text(lt.text().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shortcut to create inline plain text name
|
/// Shortcut to create inline plain text name. Panics if `text.len() > 22`
|
||||||
const fn new_inline(text: &str) -> Name {
|
const fn new_inline(text: &str) -> Name {
|
||||||
Name::new_text(SmolStr::new_inline(text))
|
Name::new_text(SmolStr::new_inline(text))
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,18 @@ impl Name {
|
||||||
Name::new_inline("[missing name]")
|
Name::new_inline("[missing name]")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a new name which is only equal to itself, by incrementing a counter. Due
|
||||||
|
/// its implementation, it should not be used in things that salsa considers, like
|
||||||
|
/// type names or field names, and it should be only used in names of local variables
|
||||||
|
/// and labels and similar things.
|
||||||
|
pub fn generate_new_name() -> Name {
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
static CNT: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
let c = CNT.fetch_add(1, Ordering::Relaxed);
|
||||||
|
// FIXME: Currently a `__RA_generated_name` in user code will break our analysis
|
||||||
|
Name::new_text(format!("__RA_geneated_name_{c}").into())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the tuple index this name represents if it is a tuple field.
|
/// Returns the tuple index this name represents if it is a tuple field.
|
||||||
pub fn as_tuple_index(&self) -> Option<usize> {
|
pub fn as_tuple_index(&self) -> Option<usize> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
|
@ -343,6 +355,8 @@ pub mod known {
|
||||||
feature,
|
feature,
|
||||||
// known methods of lang items
|
// known methods of lang items
|
||||||
call_once,
|
call_once,
|
||||||
|
call_mut,
|
||||||
|
call,
|
||||||
eq,
|
eq,
|
||||||
ne,
|
ne,
|
||||||
ge,
|
ge,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use base_db::CrateId;
|
||||||
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
|
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
expr::Expr,
|
expr::Expr,
|
||||||
path::ModPath,
|
path::Path,
|
||||||
resolver::{Resolver, ValueNs},
|
resolver::{Resolver, ValueNs},
|
||||||
type_ref::ConstRef,
|
type_ref::ConstRef,
|
||||||
ConstId, EnumVariantId,
|
ConstId, EnumVariantId,
|
||||||
|
@ -15,7 +15,7 @@ use stdx::never;
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
|
db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
|
||||||
to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg,
|
to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg,
|
||||||
Interner, MemoryMap, Ty, TyBuilder,
|
Interner, MemoryMap, Substitution, Ty, TyBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
|
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
|
||||||
|
@ -72,7 +72,7 @@ impl From<MirEvalError> for ConstEvalError {
|
||||||
pub(crate) fn path_to_const(
|
pub(crate) fn path_to_const(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
path: &ModPath,
|
path: &Path,
|
||||||
mode: ParamLoweringMode,
|
mode: ParamLoweringMode,
|
||||||
args_lazy: impl FnOnce() -> Generics,
|
args_lazy: impl FnOnce() -> Generics,
|
||||||
debruijn: DebruijnIndex,
|
debruijn: DebruijnIndex,
|
||||||
|
@ -89,7 +89,7 @@ pub(crate) fn path_to_const(
|
||||||
Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)),
|
Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)),
|
||||||
None => {
|
None => {
|
||||||
never!(
|
never!(
|
||||||
"Generic list doesn't contain this param: {:?}, {}, {:?}",
|
"Generic list doesn't contain this param: {:?}, {:?}, {:?}",
|
||||||
args,
|
args,
|
||||||
path,
|
path,
|
||||||
p
|
p
|
||||||
|
@ -169,6 +169,7 @@ pub(crate) fn const_eval_recover(
|
||||||
_: &dyn HirDatabase,
|
_: &dyn HirDatabase,
|
||||||
_: &[String],
|
_: &[String],
|
||||||
_: &ConstId,
|
_: &ConstId,
|
||||||
|
_: &Substitution,
|
||||||
) -> Result<Const, ConstEvalError> {
|
) -> Result<Const, ConstEvalError> {
|
||||||
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
|
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
|
||||||
}
|
}
|
||||||
|
@ -184,10 +185,11 @@ pub(crate) fn const_eval_discriminant_recover(
|
||||||
pub(crate) fn const_eval_query(
|
pub(crate) fn const_eval_query(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
const_id: ConstId,
|
const_id: ConstId,
|
||||||
|
subst: Substitution,
|
||||||
) -> Result<Const, ConstEvalError> {
|
) -> Result<Const, ConstEvalError> {
|
||||||
let def = const_id.into();
|
let def = const_id.into();
|
||||||
let body = db.mir_body(def)?;
|
let body = db.mir_body(def)?;
|
||||||
let c = interpret_mir(db, &body, false)?;
|
let c = interpret_mir(db, &body, subst, false)?;
|
||||||
Ok(c)
|
Ok(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +212,7 @@ pub(crate) fn const_eval_discriminant_variant(
|
||||||
return Ok(value);
|
return Ok(value);
|
||||||
}
|
}
|
||||||
let mir_body = db.mir_body(def)?;
|
let mir_body = db.mir_body(def)?;
|
||||||
let c = interpret_mir(db, &mir_body, false)?;
|
let c = interpret_mir(db, &mir_body, Substitution::empty(Interner), false)?;
|
||||||
let c = try_const_usize(&c).unwrap() as i128;
|
let c = try_const_usize(&c).unwrap() as i128;
|
||||||
Ok(c)
|
Ok(c)
|
||||||
}
|
}
|
||||||
|
@ -228,13 +230,13 @@ pub(crate) fn eval_to_const(
|
||||||
let db = ctx.db;
|
let db = ctx.db;
|
||||||
if let Expr::Path(p) = &ctx.body.exprs[expr] {
|
if let Expr::Path(p) = &ctx.body.exprs[expr] {
|
||||||
let resolver = &ctx.resolver;
|
let resolver = &ctx.resolver;
|
||||||
if let Some(c) = path_to_const(db, resolver, p.mod_path(), mode, args, debruijn) {
|
if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn) {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let infer = ctx.clone().resolve_all();
|
let infer = ctx.clone().resolve_all();
|
||||||
if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
|
if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
|
||||||
if let Ok(result) = interpret_mir(db, &mir_body, true) {
|
if let Ok(result) = interpret_mir(db, &mir_body, Substitution::empty(Interner), true) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use base_db::fixture::WithFixture;
|
use base_db::fixture::WithFixture;
|
||||||
|
use chalk_ir::Substitution;
|
||||||
use hir_def::db::DefDatabase;
|
use hir_def::db::DefDatabase;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::try_const_usize, db::HirDatabase, test_db::TestDB, Const, ConstScalar, Interner,
|
consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar,
|
||||||
|
Interner,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -10,6 +12,8 @@ use super::{
|
||||||
ConstEvalError,
|
ConstEvalError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod intrinsics;
|
||||||
|
|
||||||
fn simplify(e: ConstEvalError) -> ConstEvalError {
|
fn simplify(e: ConstEvalError) -> ConstEvalError {
|
||||||
match e {
|
match e {
|
||||||
ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => {
|
ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => {
|
||||||
|
@ -30,7 +34,12 @@ fn check_number(ra_fixture: &str, answer: i128) {
|
||||||
match &r.data(Interner).value {
|
match &r.data(Interner).value {
|
||||||
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
|
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
|
||||||
ConstScalar::Bytes(b, _) => {
|
ConstScalar::Bytes(b, _) => {
|
||||||
assert_eq!(b, &answer.to_le_bytes()[0..b.len()]);
|
assert_eq!(
|
||||||
|
b,
|
||||||
|
&answer.to_le_bytes()[0..b.len()],
|
||||||
|
"Bytes differ. In decimal form: actual = {}, expected = {answer}",
|
||||||
|
i128::from_le_bytes(pad16(b, true))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
x => panic!("Expected number but found {:?}", x),
|
x => panic!("Expected number but found {:?}", x),
|
||||||
},
|
},
|
||||||
|
@ -56,7 +65,7 @@ fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.const_eval(const_id)
|
db.const_eval(const_id, Substitution::empty(Interner))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -76,6 +85,49 @@ fn bit_op() {
|
||||||
check_number(r#"const GOAL: i8 = 1 << 8"#, 0);
|
check_number(r#"const GOAL: i8 = 1 << 8"#, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn casts() {
|
||||||
|
check_number(r#"const GOAL: usize = 12 as *const i32 as usize"#, 12);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, index, slice
|
||||||
|
const GOAL: i32 = {
|
||||||
|
let a = [10, 20, 3, 15];
|
||||||
|
let x: &[i32] = &a;
|
||||||
|
let y: *const [i32] = x;
|
||||||
|
let z = y as *const i32;
|
||||||
|
unsafe { *z }
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, index, slice
|
||||||
|
const GOAL: i16 = {
|
||||||
|
let a = &mut 5;
|
||||||
|
let z = a as *mut _;
|
||||||
|
unsafe { *z }
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, index, slice
|
||||||
|
const GOAL: usize = {
|
||||||
|
let a = [10, 20, 3, 15];
|
||||||
|
let x: &[i32] = &a;
|
||||||
|
let y: *const [i32] = x;
|
||||||
|
let z = y as *const [u8]; // slice fat pointer cast don't touch metadata
|
||||||
|
let w = unsafe { &*z };
|
||||||
|
w.len()
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
4,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn locals() {
|
fn locals() {
|
||||||
check_number(
|
check_number(
|
||||||
|
@ -166,8 +218,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 +236,7 @@ fn overloaded_deref() {
|
||||||
*y + *x
|
*y + *x
|
||||||
};
|
};
|
||||||
"#,
|
"#,
|
||||||
ConstEvalError::MirLowerError(MirLowerError::NotSupported(
|
10,
|
||||||
"explicit overloaded deref".into(),
|
|
||||||
)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,6 +267,42 @@ fn overloaded_deref_autoref() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn overloaded_index() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: index
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
impl core::ops::Index<usize> for Foo {
|
||||||
|
type Output = i32;
|
||||||
|
fn index(&self, index: usize) -> &i32 {
|
||||||
|
if index == 7 {
|
||||||
|
&700
|
||||||
|
} else {
|
||||||
|
&1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::IndexMut<usize> for Foo {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut i32 {
|
||||||
|
if index == 7 {
|
||||||
|
&mut 7
|
||||||
|
} else {
|
||||||
|
&mut 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: i32 = {
|
||||||
|
(Foo[2]) + (Foo[7]) + (*&Foo[2]) + (*&Foo[7]) + (*&mut Foo[2]) + (*&mut Foo[7])
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
3417,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_call() {
|
fn function_call() {
|
||||||
check_number(
|
check_number(
|
||||||
|
@ -240,20 +325,6 @@ fn function_call() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn intrinsics() {
|
|
||||||
check_number(
|
|
||||||
r#"
|
|
||||||
extern "rust-intrinsic" {
|
|
||||||
pub fn size_of<T>() -> usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
const GOAL: usize = size_of::<i32>();
|
|
||||||
"#,
|
|
||||||
4,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trait_basic() {
|
fn trait_basic() {
|
||||||
check_number(
|
check_number(
|
||||||
|
@ -483,6 +554,42 @@ fn loops() {
|
||||||
"#,
|
"#,
|
||||||
4,
|
4,
|
||||||
);
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let mut x = 0;
|
||||||
|
loop {
|
||||||
|
x = x + 1;
|
||||||
|
if x == 5 {
|
||||||
|
break x + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
7,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
const GOAL: u8 = {
|
||||||
|
'a: loop {
|
||||||
|
let x = 'b: loop {
|
||||||
|
let x = 'c: loop {
|
||||||
|
let x = 'd: loop {
|
||||||
|
let x = 'e: loop {
|
||||||
|
break 'd 1;
|
||||||
|
};
|
||||||
|
break 2 + x;
|
||||||
|
};
|
||||||
|
break 3 + x;
|
||||||
|
};
|
||||||
|
break 'a 4 + x;
|
||||||
|
};
|
||||||
|
break 5 + x;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
8,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -522,6 +629,18 @@ fn for_loops() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ranges() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: range
|
||||||
|
const GOAL: i32 = (1..2).start + (20..10).end + (100..=200).start + (2000..=1000).end
|
||||||
|
+ (10000..).start + (..100000).end + (..=1000000).end;
|
||||||
|
"#,
|
||||||
|
1111111,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn recursion() {
|
fn recursion() {
|
||||||
check_number(
|
check_number(
|
||||||
|
@ -555,6 +674,38 @@ fn structs() {
|
||||||
"#,
|
"#,
|
||||||
17,
|
17,
|
||||||
);
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
struct Point {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: i32 = {
|
||||||
|
let p = Point { x: 5, y: 2 };
|
||||||
|
let p2 = Point { x: 3, ..p };
|
||||||
|
p.x * 1000 + p.y * 100 + p2.x * 10 + p2.y
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
5232,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
struct Point {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: i32 = {
|
||||||
|
let p = Point { x: 5, y: 2 };
|
||||||
|
let Point { x, y } = p;
|
||||||
|
let Point { x: x2, .. } = p;
|
||||||
|
let Point { y: y2, .. } = p;
|
||||||
|
x * 1000 + y * 100 + x2 * 10 + y2
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
5252,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -599,13 +750,14 @@ fn tuples() {
|
||||||
);
|
);
|
||||||
check_number(
|
check_number(
|
||||||
r#"
|
r#"
|
||||||
struct TupleLike(i32, u8, i64, u16);
|
struct TupleLike(i32, i64, u8, u16);
|
||||||
const GOAL: u8 = {
|
const GOAL: i64 = {
|
||||||
let a = TupleLike(10, 20, 3, 15);
|
let a = TupleLike(10, 20, 3, 15);
|
||||||
a.1
|
let TupleLike(b, .., c) = a;
|
||||||
|
a.1 * 100 + b as i64 + c as i64
|
||||||
};
|
};
|
||||||
"#,
|
"#,
|
||||||
20,
|
2025,
|
||||||
);
|
);
|
||||||
check_number(
|
check_number(
|
||||||
r#"
|
r#"
|
||||||
|
@ -652,6 +804,36 @@ fn path_pattern_matching() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pattern_matching_literal() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
const fn f(x: i32) -> i32 {
|
||||||
|
match x {
|
||||||
|
-1 => 1,
|
||||||
|
1 => 10,
|
||||||
|
_ => 100,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const GOAL: i32 = f(-1) + f(1) + f(0) + f(-5);
|
||||||
|
"#,
|
||||||
|
211,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
const fn f(x: &str) -> u8 {
|
||||||
|
match x {
|
||||||
|
"foo" => 1,
|
||||||
|
"bar" => 10,
|
||||||
|
_ => 100,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const GOAL: u8 = f("foo") + f("bar");
|
||||||
|
"#,
|
||||||
|
11,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pattern_matching_ergonomics() {
|
fn pattern_matching_ergonomics() {
|
||||||
check_number(
|
check_number(
|
||||||
|
@ -665,6 +847,16 @@ fn pattern_matching_ergonomics() {
|
||||||
"#,
|
"#,
|
||||||
5,
|
5,
|
||||||
);
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let a = &(2, 3);
|
||||||
|
let &(x, y) = a;
|
||||||
|
x + y
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -748,6 +940,33 @@ fn function_param_patterns() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn match_guards() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: option, eq
|
||||||
|
impl<T: PartialEq> PartialEq for Option<T> {
|
||||||
|
fn eq(&self, other: &Rhs) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Some(x), Some(y)) => x == y,
|
||||||
|
(None, None) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn f(x: Option<i32>) -> i32 {
|
||||||
|
match x {
|
||||||
|
y if y == Some(42) => 42000,
|
||||||
|
Some(y) => y,
|
||||||
|
None => 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const GOAL: i32 = f(Some(42)) + f(Some(2)) + f(None);
|
||||||
|
"#,
|
||||||
|
42012,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn options() {
|
fn options() {
|
||||||
check_number(
|
check_number(
|
||||||
|
@ -801,6 +1020,91 @@ fn options() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_trait() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: from
|
||||||
|
struct E1(i32);
|
||||||
|
struct E2(i32);
|
||||||
|
|
||||||
|
impl From<E1> for E2 {
|
||||||
|
fn from(E1(x): E1) -> Self {
|
||||||
|
E2(1000 * x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const GOAL: i32 = {
|
||||||
|
let x: E2 = E1(2).into();
|
||||||
|
x.0
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
2000,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn try_operator() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: option, try
|
||||||
|
const fn f(x: Option<i32>, y: Option<i32>) -> Option<i32> {
|
||||||
|
Some(x? * y?)
|
||||||
|
}
|
||||||
|
const fn g(x: Option<i32>, y: Option<i32>) -> i32 {
|
||||||
|
match f(x, y) {
|
||||||
|
Some(k) => k,
|
||||||
|
None => 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None);
|
||||||
|
"#,
|
||||||
|
215,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: result, try, from
|
||||||
|
struct E1(i32);
|
||||||
|
struct E2(i32);
|
||||||
|
|
||||||
|
impl From<E1> for E2 {
|
||||||
|
fn from(E1(x): E1) -> Self {
|
||||||
|
E2(1000 * x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn f(x: Result<i32, E1>) -> Result<i32, E2> {
|
||||||
|
Ok(x? * 10)
|
||||||
|
}
|
||||||
|
const fn g(x: Result<i32, E1>) -> i32 {
|
||||||
|
match f(x) {
|
||||||
|
Ok(k) => 7 * k,
|
||||||
|
Err(E2(k)) => 5 * k,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const GOAL: i32 = g(Ok(2)) + g(Err(E1(3)));
|
||||||
|
"#,
|
||||||
|
15140,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn try_block() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: option, try
|
||||||
|
const fn g(x: Option<i32>, y: Option<i32>) -> i32 {
|
||||||
|
let r = try { x? * y? };
|
||||||
|
match r {
|
||||||
|
Some(k) => k,
|
||||||
|
None => 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None);
|
||||||
|
"#,
|
||||||
|
215,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn or_pattern() {
|
fn or_pattern() {
|
||||||
check_number(
|
check_number(
|
||||||
|
@ -839,6 +1143,220 @@ fn or_pattern() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_pointer() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
fn add2(x: u8) -> u8 {
|
||||||
|
x + 2
|
||||||
|
}
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let plus2 = add2;
|
||||||
|
plus2(3)
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
fn add2(x: u8) -> u8 {
|
||||||
|
x + 2
|
||||||
|
}
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let plus2: fn(u8) -> u8 = add2;
|
||||||
|
plus2(3)
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, index, slice
|
||||||
|
fn add2(x: u8) -> u8 {
|
||||||
|
x + 2
|
||||||
|
}
|
||||||
|
fn mult3(x: u8) -> u8 {
|
||||||
|
x * 3
|
||||||
|
}
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let x = [add2, mult3];
|
||||||
|
x[0](1) + x[1](5)
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
18,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enum_variant_as_function() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: option
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let f = Some;
|
||||||
|
f(3).unwrap_or(2)
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: option
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let f: fn(u8) -> Option<u8> = Some;
|
||||||
|
f(3).unwrap_or(2)
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, index, slice
|
||||||
|
enum Foo {
|
||||||
|
Add2(u8),
|
||||||
|
Mult3(u8),
|
||||||
|
}
|
||||||
|
use Foo::*;
|
||||||
|
const fn f(x: Foo) -> u8 {
|
||||||
|
match x {
|
||||||
|
Add2(x) => x + 2,
|
||||||
|
Mult3(x) => x * 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let x = [Add2, Mult3];
|
||||||
|
f(x[0](1)) + f(x[1](5))
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
18,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_traits() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: fn
|
||||||
|
fn add2(x: u8) -> u8 {
|
||||||
|
x + 2
|
||||||
|
}
|
||||||
|
fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
const GOAL: u8 = call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3);
|
||||||
|
"#,
|
||||||
|
15,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: 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
|
||||||
|
}
|
||||||
|
fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let add2: fn(u8) -> u8 = add2;
|
||||||
|
call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3)
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
15,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: fn
|
||||||
|
fn add2(x: u8) -> u8 {
|
||||||
|
x + 2
|
||||||
|
}
|
||||||
|
fn call(f: &&&&&impl Fn(u8) -> u8, x: u8) -> u8 {
|
||||||
|
f(x)
|
||||||
|
}
|
||||||
|
const GOAL: u8 = call(&&&&&add2, 3);
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dyn_trait() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, index, slice
|
||||||
|
trait Foo {
|
||||||
|
fn foo(&self) -> u8 { 10 }
|
||||||
|
}
|
||||||
|
struct S1;
|
||||||
|
struct S2;
|
||||||
|
struct S3;
|
||||||
|
impl Foo for S1 {
|
||||||
|
fn foo(&self) -> u8 { 1 }
|
||||||
|
}
|
||||||
|
impl Foo for S2 {
|
||||||
|
fn foo(&self) -> u8 { 2 }
|
||||||
|
}
|
||||||
|
impl Foo for S3 {}
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let x: &[&dyn Foo] = &[&S1, &S2, &S3];
|
||||||
|
x[0].foo() + x[1].foo() + x[2].foo()
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
13,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, index, slice
|
||||||
|
trait Foo {
|
||||||
|
fn foo(&self) -> i32 { 10 }
|
||||||
|
}
|
||||||
|
trait Bar {
|
||||||
|
fn bar(&self) -> i32 { 20 }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
impl Foo for S {
|
||||||
|
fn foo(&self) -> i32 { 200 }
|
||||||
|
}
|
||||||
|
impl Bar for dyn Foo {
|
||||||
|
fn bar(&self) -> i32 { 700 }
|
||||||
|
}
|
||||||
|
const GOAL: i32 = {
|
||||||
|
let x: &dyn Foo = &S;
|
||||||
|
x.bar() + x.foo()
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
900,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn array_and_index() {
|
fn array_and_index() {
|
||||||
check_number(
|
check_number(
|
||||||
|
@ -867,6 +1385,17 @@ fn array_and_index() {
|
||||||
check_number(
|
check_number(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: coerce_unsized, index, slice
|
//- minicore: coerce_unsized, index, slice
|
||||||
|
const GOAL: usize = {
|
||||||
|
let a = [1, 2, 3];
|
||||||
|
let x: &[i32] = &a;
|
||||||
|
let y = &*x;
|
||||||
|
y.len()
|
||||||
|
};"#,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, index, slice
|
||||||
const GOAL: usize = [1, 2, 3, 4, 5].len();"#,
|
const GOAL: usize = [1, 2, 3, 4, 5].len();"#,
|
||||||
5,
|
5,
|
||||||
);
|
);
|
||||||
|
@ -991,8 +1520,7 @@ fn const_generic_subst_fn() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn const_generic_subst_assoc_const_impl() {
|
fn const_generic_subst_assoc_const_impl() {
|
||||||
// FIXME: this should evaluate to 5
|
check_number(
|
||||||
check_fail(
|
|
||||||
r#"
|
r#"
|
||||||
struct Adder<const N: usize, const M: usize>;
|
struct Adder<const N: usize, const M: usize>;
|
||||||
impl<const N: usize, const M: usize> Adder<N, M> {
|
impl<const N: usize, const M: usize> Adder<N, M> {
|
||||||
|
@ -1000,7 +1528,7 @@ fn const_generic_subst_assoc_const_impl() {
|
||||||
}
|
}
|
||||||
const GOAL: usize = Adder::<2, 3>::VAL;
|
const GOAL: usize = Adder::<2, 3>::VAL;
|
||||||
"#,
|
"#,
|
||||||
ConstEvalError::MirEvalError(MirEvalError::TypeError("missing generic arg")),
|
5,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
162
crates/hir-ty/src/consteval/tests/intrinsics.rs
Normal file
162
crates/hir-ty/src/consteval/tests/intrinsics.rs
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn size_of() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn size_of<T>() -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: usize = size_of::<i32>();
|
||||||
|
"#,
|
||||||
|
4,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transmute() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn transmute<T, U>(e: T) -> U;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: i32 = transmute((1i16, 1i16));
|
||||||
|
"#,
|
||||||
|
0x00010001,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_eval_select() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET
|
||||||
|
where
|
||||||
|
G: FnOnce<ARG, Output = RET>,
|
||||||
|
F: FnOnce<ARG, Output = RET>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn in_const(x: i32, y: i32) -> i32 {
|
||||||
|
x + y
|
||||||
|
}
|
||||||
|
|
||||||
|
fn in_rt(x: i32, y: i32) -> i32 {
|
||||||
|
x + y
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: i32 = const_eval_select((2, 3), in_const, in_rt);
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wrapping_add() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn wrapping_add<T>(a: T, b: T) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: u8 = wrapping_add(10, 250);
|
||||||
|
"#,
|
||||||
|
4,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn offset() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, index, slice
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: u8 = unsafe {
|
||||||
|
let ar: &[(u8, u8, u8)] = &[
|
||||||
|
(10, 11, 12),
|
||||||
|
(20, 21, 22),
|
||||||
|
(30, 31, 32),
|
||||||
|
(40, 41, 42),
|
||||||
|
(50, 51, 52),
|
||||||
|
];
|
||||||
|
let ar: *const [(u8, u8, u8)] = ar;
|
||||||
|
let ar = ar as *const (u8, u8, u8);
|
||||||
|
let element = offset(ar, 2);
|
||||||
|
element.1
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
31,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn arith_offset() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, index, slice
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: u8 = unsafe {
|
||||||
|
let ar: &[(u8, u8, u8)] = &[
|
||||||
|
(10, 11, 12),
|
||||||
|
(20, 21, 22),
|
||||||
|
(30, 31, 32),
|
||||||
|
(40, 41, 42),
|
||||||
|
(50, 51, 52),
|
||||||
|
];
|
||||||
|
let ar: *const [(u8, u8, u8)] = ar;
|
||||||
|
let ar = ar as *const (u8, u8, u8);
|
||||||
|
let element = arith_offset(arith_offset(ar, 102), -100);
|
||||||
|
element.1
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
31,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn copy_nonoverlapping() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: u8 = unsafe {
|
||||||
|
let mut x = 2;
|
||||||
|
let y = 5;
|
||||||
|
copy_nonoverlapping(&y, &mut x, 1);
|
||||||
|
x
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn copy() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, index, slice
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn copy<T>(src: *const T, dst: *mut T, count: usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: i32 = unsafe {
|
||||||
|
let mut x = [1i32, 2, 3, 4, 5];
|
||||||
|
let y = (&mut x as *mut _) as *mut i32;
|
||||||
|
let z = (y as usize + 4) as *const i32;
|
||||||
|
copy(z, y, 4);
|
||||||
|
x[0] + x[1] + x[2] + x[3] + x[4]
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
19,
|
||||||
|
);
|
||||||
|
}
|
|
@ -57,7 +57,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||||
|
|
||||||
#[salsa::invoke(crate::consteval::const_eval_query)]
|
#[salsa::invoke(crate::consteval::const_eval_query)]
|
||||||
#[salsa::cycle(crate::consteval::const_eval_recover)]
|
#[salsa::cycle(crate::consteval::const_eval_recover)]
|
||||||
fn const_eval(&self, def: ConstId) -> Result<Const, ConstEvalError>;
|
fn const_eval(&self, def: ConstId, subst: Substitution) -> Result<Const, ConstEvalError>;
|
||||||
|
|
||||||
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
|
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
|
||||||
#[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
|
#[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
|
||||||
|
@ -97,6 +97,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||||
#[salsa::invoke(crate::lower::generic_predicates_query)]
|
#[salsa::invoke(crate::lower::generic_predicates_query)]
|
||||||
fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders<QuantifiedWhereClause>]>;
|
fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders<QuantifiedWhereClause>]>;
|
||||||
|
|
||||||
|
#[salsa::invoke(crate::lower::trait_environment_for_body_query)]
|
||||||
|
#[salsa::transparent]
|
||||||
|
fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<crate::TraitEnvironment>;
|
||||||
|
|
||||||
#[salsa::invoke(crate::lower::trait_environment_query)]
|
#[salsa::invoke(crate::lower::trait_environment_query)]
|
||||||
fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>;
|
fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>;
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ fn walk_unsafe(
|
||||||
}
|
}
|
||||||
Expr::Path(path) => {
|
Expr::Path(path) => {
|
||||||
let resolver = resolver_for_expr(db.upcast(), def, current);
|
let resolver = resolver_for_expr(db.upcast(), def, current);
|
||||||
let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path.mod_path());
|
let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
|
||||||
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial {
|
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial {
|
||||||
if db.static_data(id).mutable {
|
if db.static_data(id).mutable {
|
||||||
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
|
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
|
||||||
|
|
|
@ -25,22 +25,22 @@ use hir_def::{
|
||||||
expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
|
expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
|
||||||
lang_item::{LangItem, LangItemTarget},
|
lang_item::{LangItem, LangItemTarget},
|
||||||
layout::Integer,
|
layout::Integer,
|
||||||
path::Path,
|
path::{ModPath, Path},
|
||||||
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
|
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
|
||||||
type_ref::TypeRef,
|
type_ref::TypeRef,
|
||||||
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
|
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, ItemContainerId, Lookup,
|
||||||
ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
|
TraitId, TypeAliasId, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::{name, Name};
|
use hir_expand::name::{name, Name};
|
||||||
use la_arena::ArenaMap;
|
use la_arena::ArenaMap;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use stdx::always;
|
use stdx::{always, never};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
|
db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
|
||||||
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
|
lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, AliasEq, AliasTy, Const,
|
||||||
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
|
DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId,
|
||||||
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This lint has a false positive here. See the link below for details.
|
// This lint has a false positive here. See the link below for details.
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,10 +113,7 @@ pub(crate) fn normalize(db: &dyn HirDatabase, owner: DefWithBodyId, ty: Ty) -> T
|
||||||
if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) {
|
if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) {
|
||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
let krate = owner.module(db.upcast()).krate();
|
let trait_env = db.trait_environment_for_body(owner);
|
||||||
let trait_env = owner
|
|
||||||
.as_generic_def_id()
|
|
||||||
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
|
|
||||||
let mut table = unify::InferenceTable::new(db, trait_env);
|
let mut table = unify::InferenceTable::new(db, trait_env);
|
||||||
|
|
||||||
let ty_with_vars = table.normalize_associated_types_in(ty);
|
let ty_with_vars = table.normalize_associated_types_in(ty);
|
||||||
|
@ -276,6 +276,13 @@ pub struct Adjustment {
|
||||||
pub target: Ty,
|
pub target: Ty,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Adjustment {
|
||||||
|
pub fn borrow(m: Mutability, ty: Ty) -> Self {
|
||||||
|
let ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
|
||||||
|
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Adjust {
|
pub enum Adjust {
|
||||||
/// Go from ! to any type.
|
/// Go from ! to any type.
|
||||||
|
@ -506,10 +513,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
body: &'a Body,
|
body: &'a Body,
|
||||||
resolver: Resolver,
|
resolver: Resolver,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let krate = owner.module(db.upcast()).krate();
|
let trait_env = db.trait_environment_for_body(owner);
|
||||||
let trait_env = owner
|
|
||||||
.as_generic_def_id()
|
|
||||||
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
|
|
||||||
InferenceContext {
|
InferenceContext {
|
||||||
result: InferenceResult::default(),
|
result: InferenceResult::default(),
|
||||||
table: unify::InferenceTable::new(db, trait_env.clone()),
|
table: unify::InferenceTable::new(db, trait_env.clone()),
|
||||||
|
@ -851,7 +855,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
// FIXME: this should resolve assoc items as well, see this example:
|
// FIXME: this should resolve assoc items as well, see this example:
|
||||||
// https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
|
// https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
|
||||||
let (resolution, unresolved) = if value_ns {
|
let (resolution, unresolved) = if value_ns {
|
||||||
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) {
|
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
|
||||||
Some(ResolveValueResult::ValueNs(value)) => match value {
|
Some(ResolveValueResult::ValueNs(value)) => match value {
|
||||||
ValueNs::EnumVariantId(var) => {
|
ValueNs::EnumVariantId(var) => {
|
||||||
let substs = ctx.substs_from_path(path, var.into(), true);
|
let substs = ctx.substs_from_path(path, var.into(), true);
|
||||||
|
@ -872,11 +876,15 @@ impl<'a> InferenceContext<'a> {
|
||||||
None => return (self.err_ty(), None),
|
None => return (self.err_ty(), None),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
|
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return (self.err_ty(), None),
|
None => return (self.err_ty(), None),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let Some(mod_path) = path.mod_path() else {
|
||||||
|
never!("resolver should always resolve lang item paths");
|
||||||
|
return (self.err_ty(), None);
|
||||||
|
};
|
||||||
return match resolution {
|
return match resolution {
|
||||||
TypeNs::AdtId(AdtId::StructId(strukt)) => {
|
TypeNs::AdtId(AdtId::StructId(strukt)) => {
|
||||||
let substs = ctx.substs_from_path(path, strukt.into(), true);
|
let substs = ctx.substs_from_path(path, strukt.into(), true);
|
||||||
|
@ -900,7 +908,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
|
let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
|
||||||
let substs = generics.placeholder_subst(self.db);
|
let substs = generics.placeholder_subst(self.db);
|
||||||
let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
|
let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
|
||||||
self.resolve_variant_on_alias(ty, unresolved, path)
|
self.resolve_variant_on_alias(ty, unresolved, mod_path)
|
||||||
}
|
}
|
||||||
TypeNs::TypeAliasId(it) => {
|
TypeNs::TypeAliasId(it) => {
|
||||||
let container = it.lookup(self.db.upcast()).container;
|
let container = it.lookup(self.db.upcast()).container;
|
||||||
|
@ -917,7 +925,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst)
|
let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst)
|
||||||
.fill_with_inference_vars(&mut self.table)
|
.fill_with_inference_vars(&mut self.table)
|
||||||
.build();
|
.build();
|
||||||
self.resolve_variant_on_alias(ty, unresolved, path)
|
self.resolve_variant_on_alias(ty, unresolved, mod_path)
|
||||||
}
|
}
|
||||||
TypeNs::AdtSelfType(_) => {
|
TypeNs::AdtSelfType(_) => {
|
||||||
// FIXME this could happen in array size expressions, once we're checking them
|
// FIXME this could happen in array size expressions, once we're checking them
|
||||||
|
@ -953,9 +961,9 @@ impl<'a> InferenceContext<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
ty: Ty,
|
ty: Ty,
|
||||||
unresolved: Option<usize>,
|
unresolved: Option<usize>,
|
||||||
path: &Path,
|
path: &ModPath,
|
||||||
) -> (Ty, Option<VariantId>) {
|
) -> (Ty, Option<VariantId>) {
|
||||||
let remaining = unresolved.map(|x| path.segments().skip(x).len()).filter(|x| x > &0);
|
let remaining = unresolved.map(|x| path.segments()[x..].len()).filter(|x| x > &0);
|
||||||
match remaining {
|
match remaining {
|
||||||
None => {
|
None => {
|
||||||
let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
|
let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
|
||||||
|
@ -969,7 +977,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
(ty, variant)
|
(ty, variant)
|
||||||
}
|
}
|
||||||
Some(1) => {
|
Some(1) => {
|
||||||
let segment = path.mod_path().segments().last().unwrap();
|
let segment = path.segments().last().unwrap();
|
||||||
// this could be an enum variant or associated type
|
// this could be an enum variant or associated type
|
||||||
if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
|
if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
|
||||||
let enum_data = self.db.enum_data(enum_id);
|
let enum_data = self.db.enum_data(enum_id);
|
||||||
|
@ -1017,10 +1025,6 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.resolve_lang_item(lang)?.as_trait()
|
self.resolve_lang_item(lang)?.as_trait()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_ops_try_output(&self) -> Option<TypeAliasId> {
|
|
||||||
self.resolve_output_on(self.resolve_lang_trait(LangItem::Try)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
|
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
|
||||||
self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?)
|
self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,11 +51,12 @@ fn success(
|
||||||
pub(super) struct CoerceMany {
|
pub(super) struct CoerceMany {
|
||||||
expected_ty: Ty,
|
expected_ty: Ty,
|
||||||
final_ty: Option<Ty>,
|
final_ty: Option<Ty>,
|
||||||
|
expressions: Vec<ExprId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoerceMany {
|
impl CoerceMany {
|
||||||
pub(super) fn new(expected: Ty) -> Self {
|
pub(super) fn new(expected: Ty) -> Self {
|
||||||
CoerceMany { expected_ty: expected, final_ty: None }
|
CoerceMany { expected_ty: expected, final_ty: None, expressions: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the "expected type" with which this coercion was
|
/// Returns the "expected type" with which this coercion was
|
||||||
|
@ -125,8 +126,15 @@ impl CoerceMany {
|
||||||
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
|
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
|
||||||
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
|
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
|
||||||
if let (Ok(result1), Ok(result2)) = (result1, result2) {
|
if let (Ok(result1), Ok(result2)) = (result1, result2) {
|
||||||
ctx.table.register_infer_ok(result1);
|
ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
|
||||||
ctx.table.register_infer_ok(result2);
|
for &e in &self.expressions {
|
||||||
|
ctx.write_expr_adj(e, result1.value.0.clone());
|
||||||
|
}
|
||||||
|
ctx.table.register_infer_ok(InferOk { value: (), goals: result2.goals });
|
||||||
|
if let Some(expr) = expr {
|
||||||
|
ctx.write_expr_adj(expr, result2.value.0);
|
||||||
|
self.expressions.push(expr);
|
||||||
|
}
|
||||||
return self.final_ty = Some(target_ty);
|
return self.final_ty = Some(target_ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,6 +156,9 @@ impl CoerceMany {
|
||||||
}
|
}
|
||||||
cov_mark::hit!(coerce_merge_fail_fallback);
|
cov_mark::hit!(coerce_merge_fail_fallback);
|
||||||
}
|
}
|
||||||
|
if let Some(expr) = expr {
|
||||||
|
self.expressions.push(expr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ use crate::{
|
||||||
method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
|
method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
|
||||||
primitive::{self, UintTy},
|
primitive::{self, UintTy},
|
||||||
static_lifetime, to_chalk_trait_id,
|
static_lifetime, to_chalk_trait_id,
|
||||||
|
traits::FnTrait,
|
||||||
utils::{generics, Generics},
|
utils::{generics, Generics},
|
||||||
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
|
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
|
||||||
Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt,
|
Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt,
|
||||||
|
@ -158,26 +159,6 @@ impl<'a> InferenceContext<'a> {
|
||||||
})
|
})
|
||||||
.1
|
.1
|
||||||
}
|
}
|
||||||
Expr::TryBlock { id: _, statements, tail } => {
|
|
||||||
// The type that is returned from the try block
|
|
||||||
let try_ty = self.table.new_type_var();
|
|
||||||
if let Some(ty) = expected.only_has_type(&mut self.table) {
|
|
||||||
self.unify(&try_ty, &ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The ok-ish type that is expected from the last expression
|
|
||||||
let ok_ty =
|
|
||||||
self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_output());
|
|
||||||
|
|
||||||
self.infer_block(
|
|
||||||
tgt_expr,
|
|
||||||
statements,
|
|
||||||
*tail,
|
|
||||||
None,
|
|
||||||
&Expectation::has_type(ok_ty.clone()),
|
|
||||||
);
|
|
||||||
try_ty
|
|
||||||
}
|
|
||||||
Expr::Async { id: _, statements, tail } => {
|
Expr::Async { id: _, statements, tail } => {
|
||||||
let ret_ty = self.table.new_type_var();
|
let ret_ty = self.table.new_type_var();
|
||||||
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||||
|
@ -385,16 +366,46 @@ impl<'a> InferenceContext<'a> {
|
||||||
|| res.is_none();
|
|| res.is_none();
|
||||||
let (param_tys, ret_ty) = match res {
|
let (param_tys, ret_ty) = match res {
|
||||||
Some((func, params, ret_ty)) => {
|
Some((func, params, ret_ty)) => {
|
||||||
let adjustments = auto_deref_adjust_steps(&derefs);
|
let mut adjustments = auto_deref_adjust_steps(&derefs);
|
||||||
// FIXME: Handle call adjustments for Fn/FnMut
|
if let Some(fn_x) = func {
|
||||||
self.write_expr_adj(*callee, adjustments);
|
match fn_x {
|
||||||
if let Some((trait_, func)) = func {
|
FnTrait::FnOnce => (),
|
||||||
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
|
FnTrait::FnMut => {
|
||||||
.push(callee_ty.clone())
|
if !matches!(
|
||||||
.push(TyBuilder::tuple_with(params.iter().cloned()))
|
derefed_callee.kind(Interner),
|
||||||
.build();
|
TyKind::Ref(Mutability::Mut, _, _)
|
||||||
self.write_method_resolution(tgt_expr, func, subst.clone());
|
) {
|
||||||
|
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)
|
||||||
|
.expect("We just used it");
|
||||||
|
let trait_data = self.db.trait_data(trait_);
|
||||||
|
if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
|
||||||
|
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
|
||||||
|
.push(callee_ty.clone())
|
||||||
|
.push(TyBuilder::tuple_with(params.iter().cloned()))
|
||||||
|
.build();
|
||||||
|
self.write_method_resolution(tgt_expr, func, subst.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
self.write_expr_adj(*callee, adjustments);
|
||||||
(params, ret_ty)
|
(params, ret_ty)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -601,26 +612,18 @@ impl<'a> InferenceContext<'a> {
|
||||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||||
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
|
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
|
||||||
}
|
}
|
||||||
Expr::Try { expr } => {
|
|
||||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
|
||||||
if let Some(trait_) = self.resolve_lang_trait(LangItem::Try) {
|
|
||||||
if let Some(func) = self.db.trait_data(trait_).method_by_name(&name!(branch)) {
|
|
||||||
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
|
|
||||||
.push(inner_ty.clone())
|
|
||||||
.build();
|
|
||||||
self.write_method_resolution(tgt_expr, func, subst.clone());
|
|
||||||
}
|
|
||||||
let try_output = self.resolve_output_on(trait_);
|
|
||||||
self.resolve_associated_type(inner_ty, try_output)
|
|
||||||
} else {
|
|
||||||
self.err_ty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::Cast { expr, type_ref } => {
|
Expr::Cast { expr, type_ref } => {
|
||||||
let cast_ty = self.make_ty(type_ref);
|
let cast_ty = self.make_ty(type_ref);
|
||||||
// FIXME: propagate the "castable to" expectation
|
// FIXME: propagate the "castable to" expectation
|
||||||
let _inner_ty = self.infer_expr_no_expect(*expr);
|
let inner_ty = self.infer_expr_no_expect(*expr);
|
||||||
// FIXME check the cast...
|
match (inner_ty.kind(Interner), cast_ty.kind(Interner)) {
|
||||||
|
(TyKind::Ref(_, _, inner), TyKind::Raw(_, cast)) => {
|
||||||
|
// FIXME: record invalid cast diagnostic in case of mismatch
|
||||||
|
self.unify(inner, cast);
|
||||||
|
}
|
||||||
|
// FIXME check the other kinds of cast...
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
cast_ty
|
cast_ty
|
||||||
}
|
}
|
||||||
Expr::Ref { expr, rawness, mutability } => {
|
Expr::Ref { expr, rawness, mutability } => {
|
||||||
|
@ -656,6 +659,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 => {
|
||||||
|
|
196
crates/hir-ty/src/infer/mutability.rs
Normal file
196
crates/hir-ty/src/infer/mutability.rs
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
//! 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::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 } => {
|
||||||
|
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
|
||||||
|
if mutability == Mutability::Mut {
|
||||||
|
if let Some(index_trait) = self
|
||||||
|
.db
|
||||||
|
.lang_item(self.table.trait_env.krate, LangItem::IndexMut)
|
||||||
|
.and_then(|l| l.as_trait())
|
||||||
|
{
|
||||||
|
if let Some(index_fn) =
|
||||||
|
self.db.trait_data(index_trait).method_by_name(&name![index_mut])
|
||||||
|
{
|
||||||
|
*f = index_fn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,7 +39,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
} else {
|
} else {
|
||||||
// FIXME: report error, unresolved first path segment
|
// FIXME: report error, unresolved first path segment
|
||||||
let value_or_partial =
|
let value_or_partial =
|
||||||
self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
|
self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
|
||||||
|
|
||||||
match value_or_partial {
|
match value_or_partial {
|
||||||
ResolveValueResult::ValueNs(it) => (it, None),
|
ResolveValueResult::ValueNs(it) => (it, None),
|
||||||
|
|
|
@ -8,16 +8,15 @@ use chalk_ir::{
|
||||||
};
|
};
|
||||||
use chalk_solve::infer::ParameterEnaVariableExt;
|
use chalk_solve::infer::ParameterEnaVariableExt;
|
||||||
use ena::unify::UnifyKey;
|
use ena::unify::UnifyKey;
|
||||||
use hir_def::{FunctionId, TraitId};
|
|
||||||
use hir_expand::name;
|
use hir_expand::name;
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
|
||||||
use super::{InferOk, InferResult, InferenceContext, TypeError};
|
use super::{InferOk, InferResult, InferenceContext, TypeError};
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar,
|
db::HirDatabase, fold_tys, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq,
|
||||||
Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment,
|
AliasTy, BoundVar, Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance,
|
||||||
InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution,
|
InEnvironment, InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt,
|
||||||
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
|
Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'a> InferenceContext<'a> {
|
impl<'a> InferenceContext<'a> {
|
||||||
|
@ -631,10 +630,13 @@ impl<'a> InferenceTable<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
ty: &Ty,
|
ty: &Ty,
|
||||||
num_args: usize,
|
num_args: usize,
|
||||||
) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
|
) -> Option<(Option<FnTrait>, Vec<Ty>, Ty)> {
|
||||||
match ty.callable_sig(self.db) {
|
match ty.callable_sig(self.db) {
|
||||||
Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())),
|
Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())),
|
||||||
None => self.callable_sig_from_fn_trait(ty, num_args),
|
None => {
|
||||||
|
let (f, args_ty, return_ty) = self.callable_sig_from_fn_trait(ty, num_args)?;
|
||||||
|
Some((Some(f), args_ty, return_ty))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,7 +644,7 @@ impl<'a> InferenceTable<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
ty: &Ty,
|
ty: &Ty,
|
||||||
num_args: usize,
|
num_args: usize,
|
||||||
) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
|
) -> Option<(FnTrait, Vec<Ty>, Ty)> {
|
||||||
let krate = self.trait_env.krate;
|
let krate = self.trait_env.krate;
|
||||||
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
|
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
|
||||||
let trait_data = self.db.trait_data(fn_once_trait);
|
let trait_data = self.db.trait_data(fn_once_trait);
|
||||||
|
@ -676,19 +678,28 @@ impl<'a> InferenceTable<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let trait_env = self.trait_env.env.clone();
|
let trait_env = self.trait_env.env.clone();
|
||||||
|
let mut trait_ref = projection.trait_ref(self.db);
|
||||||
let obligation = InEnvironment {
|
let obligation = InEnvironment {
|
||||||
goal: projection.trait_ref(self.db).cast(Interner),
|
goal: trait_ref.clone().cast(Interner),
|
||||||
environment: trait_env,
|
environment: trait_env.clone(),
|
||||||
};
|
};
|
||||||
let canonical = self.canonicalize(obligation.clone());
|
let canonical = self.canonicalize(obligation.clone());
|
||||||
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
|
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
|
||||||
self.register_obligation(obligation.goal);
|
self.register_obligation(obligation.goal);
|
||||||
let return_ty = self.normalize_projection_ty(projection);
|
let return_ty = self.normalize_projection_ty(projection);
|
||||||
Some((
|
for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
|
||||||
Some(fn_once_trait).zip(trait_data.method_by_name(&name!(call_once))),
|
let fn_x_trait = fn_x.get_id(self.db, krate)?;
|
||||||
arg_tys,
|
trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
|
||||||
return_ty,
|
let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> = InEnvironment {
|
||||||
))
|
goal: trait_ref.clone().cast(Interner),
|
||||||
|
environment: trait_env.clone(),
|
||||||
|
};
|
||||||
|
let canonical = self.canonicalize(obligation.clone());
|
||||||
|
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
|
||||||
|
return Some((fn_x, arg_tys, return_ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable!("It should at least implement FnOnce at this point");
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -576,10 +576,14 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn callable_sig_from_fnonce(
|
pub fn callable_sig_from_fnonce(
|
||||||
self_ty: &Ty,
|
mut self_ty: &Ty,
|
||||||
env: Arc<TraitEnvironment>,
|
env: Arc<TraitEnvironment>,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
) -> Option<CallableSig> {
|
) -> Option<CallableSig> {
|
||||||
|
if let Some((ty, _, _)) = self_ty.as_reference() {
|
||||||
|
// This will happen when it implements fn or fn mut, since we add a autoborrow adjustment
|
||||||
|
self_ty = ty;
|
||||||
|
}
|
||||||
let krate = env.krate;
|
let krate = env.krate;
|
||||||
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
|
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
|
||||||
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
|
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
|
||||||
|
|
|
@ -25,12 +25,12 @@ use hir_def::{
|
||||||
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
|
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
|
||||||
},
|
},
|
||||||
lang_item::{lang_attr, LangItem},
|
lang_item::{lang_attr, LangItem},
|
||||||
path::{GenericArg, ModPath, Path, PathKind, PathSegment, PathSegments},
|
path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
|
||||||
resolver::{HasResolver, Resolver, TypeNs},
|
resolver::{HasResolver, Resolver, TypeNs},
|
||||||
type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
|
type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
|
||||||
AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId,
|
AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
|
||||||
HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, StructId,
|
GenericDefId, HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId,
|
||||||
TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
|
StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::{name::Name, ExpandResult};
|
use hir_expand::{name::Name, ExpandResult};
|
||||||
use intern::Interned;
|
use intern::Interned;
|
||||||
|
@ -425,11 +425,10 @@ impl<'a> TyLoweringContext<'a> {
|
||||||
if path.segments().len() > 1 {
|
if path.segments().len() > 1 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let resolution =
|
let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
|
||||||
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
|
Some((it, None)) => it,
|
||||||
Some((it, None)) => it,
|
_ => return None,
|
||||||
_ => return None,
|
};
|
||||||
};
|
|
||||||
match resolution {
|
match resolution {
|
||||||
TypeNs::GenericParam(param_id) => Some(param_id.into()),
|
TypeNs::GenericParam(param_id) => Some(param_id.into()),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -608,7 +607,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (resolution, remaining_index) =
|
let (resolution, remaining_index) =
|
||||||
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
|
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return (TyKind::Error.intern(Interner), None),
|
None => return (TyKind::Error.intern(Interner), None),
|
||||||
};
|
};
|
||||||
|
@ -716,7 +715,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||||
resolved: ValueTyDefId,
|
resolved: ValueTyDefId,
|
||||||
infer_args: bool,
|
infer_args: bool,
|
||||||
) -> Substitution {
|
) -> Substitution {
|
||||||
let last = path.segments().last().expect("path should have at least one segment");
|
let last = path.segments().last();
|
||||||
let (segment, generic_def) = match resolved {
|
let (segment, generic_def) = match resolved {
|
||||||
ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
|
ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
|
||||||
ValueTyDefId::StructId(it) => (last, Some(it.into())),
|
ValueTyDefId::StructId(it) => (last, Some(it.into())),
|
||||||
|
@ -732,13 +731,20 @@ impl<'a> TyLoweringContext<'a> {
|
||||||
let len = path.segments().len();
|
let len = path.segments().len();
|
||||||
let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx));
|
let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx));
|
||||||
let segment = match penultimate {
|
let segment = match penultimate {
|
||||||
Some(segment) if segment.args_and_bindings.is_some() => segment,
|
Some(segment) if segment.args_and_bindings.is_some() => Some(segment),
|
||||||
_ => last,
|
_ => last,
|
||||||
};
|
};
|
||||||
(segment, Some(var.parent.into()))
|
(segment, Some(var.parent.into()))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.substs_from_path_segment(segment, generic_def, infer_args, None)
|
if let Some(segment) = segment {
|
||||||
|
self.substs_from_path_segment(segment, generic_def, infer_args, None)
|
||||||
|
} else if let Some(generic_def) = generic_def {
|
||||||
|
// lang item
|
||||||
|
self.substs_from_args_and_bindings(None, Some(generic_def), infer_args, None)
|
||||||
|
} else {
|
||||||
|
Substitution::empty(Interner)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn substs_from_path_segment(
|
fn substs_from_path_segment(
|
||||||
|
@ -747,6 +753,21 @@ impl<'a> TyLoweringContext<'a> {
|
||||||
def: Option<GenericDefId>,
|
def: Option<GenericDefId>,
|
||||||
infer_args: bool,
|
infer_args: bool,
|
||||||
explicit_self_ty: Option<Ty>,
|
explicit_self_ty: Option<Ty>,
|
||||||
|
) -> Substitution {
|
||||||
|
self.substs_from_args_and_bindings(
|
||||||
|
segment.args_and_bindings,
|
||||||
|
def,
|
||||||
|
infer_args,
|
||||||
|
explicit_self_ty,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn substs_from_args_and_bindings(
|
||||||
|
&self,
|
||||||
|
args_and_bindings: Option<&GenericArgs>,
|
||||||
|
def: Option<GenericDefId>,
|
||||||
|
infer_args: bool,
|
||||||
|
explicit_self_ty: Option<Ty>,
|
||||||
) -> Substitution {
|
) -> Substitution {
|
||||||
// Remember that the item's own generic args come before its parent's.
|
// Remember that the item's own generic args come before its parent's.
|
||||||
let mut substs = Vec::new();
|
let mut substs = Vec::new();
|
||||||
|
@ -780,7 +801,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||||
};
|
};
|
||||||
let mut had_explicit_args = false;
|
let mut had_explicit_args = false;
|
||||||
|
|
||||||
if let Some(generic_args) = &segment.args_and_bindings {
|
if let Some(generic_args) = &args_and_bindings {
|
||||||
if !generic_args.has_self_type {
|
if !generic_args.has_self_type {
|
||||||
fill_self_params();
|
fill_self_params();
|
||||||
}
|
}
|
||||||
|
@ -879,12 +900,11 @@ impl<'a> TyLoweringContext<'a> {
|
||||||
path: &Path,
|
path: &Path,
|
||||||
explicit_self_ty: Option<Ty>,
|
explicit_self_ty: Option<Ty>,
|
||||||
) -> Option<TraitRef> {
|
) -> Option<TraitRef> {
|
||||||
let resolved =
|
let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? {
|
||||||
match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path())? {
|
// FIXME(trait_alias): We need to handle trait alias here.
|
||||||
// FIXME(trait_alias): We need to handle trait alias here.
|
TypeNs::TraitId(tr) => tr,
|
||||||
TypeNs::TraitId(tr) => tr,
|
_ => return None,
|
||||||
_ => return None,
|
};
|
||||||
};
|
|
||||||
let segment = path.segments().last().expect("path should have at least one segment");
|
let segment = path.segments().last().expect("path should have at least one segment");
|
||||||
Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty))
|
Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty))
|
||||||
}
|
}
|
||||||
|
@ -1381,9 +1401,7 @@ pub(crate) fn generic_predicates_for_param_query(
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return true,
|
None => return true,
|
||||||
};
|
};
|
||||||
let tr = match resolver
|
let tr = match resolver.resolve_path_in_type_ns_fully(db.upcast(), path) {
|
||||||
.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path())
|
|
||||||
{
|
|
||||||
Some(TypeNs::TraitId(tr)) => tr,
|
Some(TypeNs::TraitId(tr)) => tr,
|
||||||
_ => return false,
|
_ => return false,
|
||||||
};
|
};
|
||||||
|
@ -1423,6 +1441,17 @@ pub(crate) fn generic_predicates_for_param_recover(
|
||||||
Arc::new([])
|
Arc::new([])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn trait_environment_for_body_query(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
def: DefWithBodyId,
|
||||||
|
) -> Arc<TraitEnvironment> {
|
||||||
|
let Some(def) = def.as_generic_def_id() else {
|
||||||
|
let krate = def.module(db.upcast()).krate();
|
||||||
|
return Arc::new(TraitEnvironment::empty(krate));
|
||||||
|
};
|
||||||
|
db.trait_environment(def)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn trait_environment_query(
|
pub(crate) fn trait_environment_query(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
def: GenericDefId,
|
def: GenericDefId,
|
||||||
|
@ -1948,7 +1977,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
|
||||||
// as types. Maybe here is not the best place to do it, but
|
// as types. Maybe here is not the best place to do it, but
|
||||||
// it works.
|
// it works.
|
||||||
if let TypeRef::Path(p) = t {
|
if let TypeRef::Path(p) = t {
|
||||||
let p = p.mod_path();
|
let p = p.mod_path()?;
|
||||||
if p.kind == PathKind::Plain {
|
if p.kind == PathKind::Plain {
|
||||||
if let [n] = p.segments() {
|
if let [n] = p.segments() {
|
||||||
let c = ConstRefOrPath::Path(n.clone());
|
let c = ConstRefOrPath::Path(n.clone());
|
||||||
|
@ -1977,8 +2006,15 @@ pub(crate) fn const_or_path_to_chalk(
|
||||||
ConstRefOrPath::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()),
|
ConstRefOrPath::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()),
|
||||||
ConstRefOrPath::Path(n) => {
|
ConstRefOrPath::Path(n) => {
|
||||||
let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
|
let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
|
||||||
path_to_const(db, resolver, &path, mode, args, debruijn)
|
path_to_const(
|
||||||
.unwrap_or_else(|| unknown_const(expected_ty))
|
db,
|
||||||
|
resolver,
|
||||||
|
&Path::from_known_path_with_no_generic(path),
|
||||||
|
mode,
|
||||||
|
args,
|
||||||
|
debruijn,
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|| unknown_const(expected_ty))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
use std::{ops::ControlFlow, sync::Arc};
|
use std::{ops::ControlFlow, sync::Arc};
|
||||||
|
|
||||||
use base_db::{CrateId, Edition};
|
use base_db::{CrateId, Edition};
|
||||||
use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex};
|
use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
data::ImplData, item_scope::ItemScope, lang_item::LangItem, nameres::DefMap, AssocItemId,
|
data::ImplData, item_scope::ItemScope, lang_item::LangItem, nameres::DefMap, AssocItemId,
|
||||||
BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId,
|
BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId,
|
||||||
|
@ -20,7 +20,7 @@ use crate::{
|
||||||
autoderef::{self, AutoderefKind},
|
autoderef::{self, AutoderefKind},
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
from_chalk_trait_id, from_foreign_def_id,
|
from_chalk_trait_id, from_foreign_def_id,
|
||||||
infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
|
infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast},
|
||||||
primitive::{FloatTy, IntTy, UintTy},
|
primitive::{FloatTy, IntTy, UintTy},
|
||||||
static_lifetime, to_chalk_trait_id,
|
static_lifetime, to_chalk_trait_id,
|
||||||
utils::all_super_traits,
|
utils::all_super_traits,
|
||||||
|
@ -600,9 +600,9 @@ impl ReceiverAdjustments {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(m) = self.autoref {
|
if let Some(m) = self.autoref {
|
||||||
ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
|
let a = Adjustment::borrow(m, ty);
|
||||||
adjust
|
ty = a.target.clone();
|
||||||
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() });
|
adjust.push(a);
|
||||||
}
|
}
|
||||||
if self.unsize_array {
|
if self.unsize_array {
|
||||||
ty = 'x: {
|
ty = 'x: {
|
||||||
|
@ -692,6 +692,39 @@ pub fn lookup_impl_const(
|
||||||
.unwrap_or((const_id, subs))
|
.unwrap_or((const_id, subs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the self parameter of `Trait` method is the `dyn Trait` and we should
|
||||||
|
/// call the method using the vtable.
|
||||||
|
pub fn is_dyn_method(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
_env: Arc<TraitEnvironment>,
|
||||||
|
func: FunctionId,
|
||||||
|
fn_subst: Substitution,
|
||||||
|
) -> Option<usize> {
|
||||||
|
let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
|
||||||
|
let fn_params = fn_subst.len(Interner) - trait_params;
|
||||||
|
let trait_ref = TraitRef {
|
||||||
|
trait_id: to_chalk_trait_id(trait_id),
|
||||||
|
substitution: Substitution::from_iter(Interner, fn_subst.iter(Interner).skip(fn_params)),
|
||||||
|
};
|
||||||
|
let self_ty = trait_ref.self_type_parameter(Interner);
|
||||||
|
if let TyKind::Dyn(d) = self_ty.kind(Interner) {
|
||||||
|
let is_my_trait_in_bounds =
|
||||||
|
d.bounds.skip_binders().as_slice(Interner).iter().any(|x| match x.skip_binders() {
|
||||||
|
// rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter
|
||||||
|
// what the generics are, we are sure that the method is come from the vtable.
|
||||||
|
WhereClause::Implemented(tr) => tr.trait_id == trait_ref.trait_id,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
if is_my_trait_in_bounds {
|
||||||
|
return Some(fn_params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Looks up the impl method that actually runs for the trait method `func`.
|
/// Looks up the impl method that actually runs for the trait method `func`.
|
||||||
///
|
///
|
||||||
/// Returns `func` if it's not a method defined in a trait or the lookup failed.
|
/// Returns `func` if it's not a method defined in a trait or the lookup failed.
|
||||||
|
@ -701,9 +734,8 @@ pub fn lookup_impl_method(
|
||||||
func: FunctionId,
|
func: FunctionId,
|
||||||
fn_subst: Substitution,
|
fn_subst: Substitution,
|
||||||
) -> (FunctionId, Substitution) {
|
) -> (FunctionId, Substitution) {
|
||||||
let trait_id = match func.lookup(db.upcast()).container {
|
let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
|
||||||
ItemContainerId::TraitId(id) => id,
|
return (func, fn_subst)
|
||||||
_ => return (func, fn_subst),
|
|
||||||
};
|
};
|
||||||
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
|
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
|
||||||
let fn_params = fn_subst.len(Interner) - trait_params;
|
let fn_params = fn_subst.len(Interner) - trait_params;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,19 +4,21 @@ 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::{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,
|
||||||
RecordLitField,
|
RecordFieldPat, RecordLitField,
|
||||||
},
|
},
|
||||||
lang_item::{LangItem, LangItemTarget},
|
lang_item::{LangItem, LangItemTarget},
|
||||||
layout::LayoutError,
|
layout::LayoutError,
|
||||||
path::Path,
|
path::Path,
|
||||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||||
DefWithBodyId, EnumVariantId, HasModule,
|
AdtId, DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, LocalFieldId, TraitId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use la_arena::ArenaMap;
|
use la_arena::ArenaMap;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
|
consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
|
||||||
|
@ -27,18 +29,25 @@ use crate::{
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
mod as_place;
|
mod as_place;
|
||||||
|
mod pattern_matching;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
use pattern_matching::AdtPatternShape;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
struct LoopBlocks {
|
struct LoopBlocks {
|
||||||
begin: BasicBlockId,
|
begin: BasicBlockId,
|
||||||
/// `None` for loops that are not terminating
|
/// `None` for loops that are not terminating
|
||||||
end: Option<BasicBlockId>,
|
end: Option<BasicBlockId>,
|
||||||
|
place: Place,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MirLowerCtx<'a> {
|
struct MirLowerCtx<'a> {
|
||||||
result: MirBody,
|
result: MirBody,
|
||||||
owner: DefWithBodyId,
|
owner: DefWithBodyId,
|
||||||
current_loop_blocks: Option<LoopBlocks>,
|
current_loop_blocks: Option<LoopBlocks>,
|
||||||
|
// FIXME: we should resolve labels in HIR lowering and always work with label id here, not
|
||||||
|
// with raw names.
|
||||||
|
labeled_loop_blocks: FxHashMap<Name, LoopBlocks>,
|
||||||
discr_temp: Option<Place>,
|
discr_temp: Option<Place>,
|
||||||
db: &'a dyn HirDatabase,
|
db: &'a dyn HirDatabase,
|
||||||
body: &'a Body,
|
body: &'a Body,
|
||||||
|
@ -50,6 +59,8 @@ pub enum MirLowerError {
|
||||||
ConstEvalError(Box<ConstEvalError>),
|
ConstEvalError(Box<ConstEvalError>),
|
||||||
LayoutError(LayoutError),
|
LayoutError(LayoutError),
|
||||||
IncompleteExpr,
|
IncompleteExpr,
|
||||||
|
/// Trying to lower a trait function, instead of an implementation
|
||||||
|
TraitFunctionDefinition(TraitId, Name),
|
||||||
UnresolvedName(String),
|
UnresolvedName(String),
|
||||||
RecordLiteralWithoutPath,
|
RecordLiteralWithoutPath,
|
||||||
UnresolvedMethod,
|
UnresolvedMethod,
|
||||||
|
@ -66,6 +77,7 @@ pub enum MirLowerError {
|
||||||
ImplementationError(&'static str),
|
ImplementationError(&'static str),
|
||||||
LangItemNotFound(LangItem),
|
LangItemNotFound(LangItem),
|
||||||
MutatingRvalue,
|
MutatingRvalue,
|
||||||
|
UnresolvedLabel,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! not_supported {
|
macro_rules! not_supported {
|
||||||
|
@ -200,26 +212,42 @@ impl MirLowerCtx<'_> {
|
||||||
mut current: BasicBlockId,
|
mut current: BasicBlockId,
|
||||||
) -> Result<Option<BasicBlockId>> {
|
) -> Result<Option<BasicBlockId>> {
|
||||||
match &self.body.exprs[expr_id] {
|
match &self.body.exprs[expr_id] {
|
||||||
Expr::Missing => Err(MirLowerError::IncompleteExpr),
|
Expr::Missing => {
|
||||||
|
if let DefWithBodyId::FunctionId(f) = self.owner {
|
||||||
|
let assoc = self.db.lookup_intern_function(f);
|
||||||
|
if let ItemContainerId::TraitId(t) = assoc.container {
|
||||||
|
let name = &self.db.function_data(f).name;
|
||||||
|
return Err(MirLowerError::TraitFunctionDefinition(t, name.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(MirLowerError::IncompleteExpr)
|
||||||
|
},
|
||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
|
let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
|
||||||
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
||||||
let pr = resolver
|
let pr = resolver
|
||||||
.resolve_path_in_value_ns(self.db.upcast(), p.mod_path())
|
.resolve_path_in_value_ns(self.db.upcast(), p)
|
||||||
.ok_or_else(unresolved_name)?;
|
.ok_or_else(unresolved_name)?;
|
||||||
let pr = match pr {
|
let pr = match pr {
|
||||||
ResolveValueResult::ValueNs(v) => v,
|
ResolveValueResult::ValueNs(v) => v,
|
||||||
ResolveValueResult::Partial(..) => {
|
ResolveValueResult::Partial(..) => {
|
||||||
if let Some(assoc) = self
|
if let Some((assoc, subst)) = self
|
||||||
.infer
|
.infer
|
||||||
.assoc_resolutions_for_expr(expr_id)
|
.assoc_resolutions_for_expr(expr_id)
|
||||||
{
|
{
|
||||||
match assoc.0 {
|
match assoc {
|
||||||
hir_def::AssocItemId::ConstId(c) => {
|
hir_def::AssocItemId::ConstId(c) => {
|
||||||
self.lower_const(c, current, place, expr_id.into())?;
|
self.lower_const(c, current, place, subst, expr_id.into())?;
|
||||||
return Ok(Some(current))
|
return Ok(Some(current))
|
||||||
},
|
},
|
||||||
_ => not_supported!("associated functions and types"),
|
hir_def::AssocItemId::FunctionId(_) => {
|
||||||
|
// FnDefs are zero sized, no action is needed.
|
||||||
|
return Ok(Some(current))
|
||||||
|
}
|
||||||
|
hir_def::AssocItemId::TypeAliasId(_) => {
|
||||||
|
// FIXME: If it is unreachable, use proper error instead of `not_supported`.
|
||||||
|
not_supported!("associated functions and types")
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} else if let Some(variant) = self
|
} else if let Some(variant) = self
|
||||||
.infer
|
.infer
|
||||||
|
@ -246,19 +274,23 @@ impl MirLowerCtx<'_> {
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
ValueNs::ConstId(const_id) => {
|
ValueNs::ConstId(const_id) => {
|
||||||
self.lower_const(const_id, current, place, expr_id.into())?;
|
self.lower_const(const_id, current, place, Substitution::empty(Interner), expr_id.into())?;
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
ValueNs::EnumVariantId(variant_id) => {
|
ValueNs::EnumVariantId(variant_id) => {
|
||||||
let ty = self.infer.type_of_expr[expr_id].clone();
|
let variant_data = &self.db.enum_data(variant_id.parent).variants[variant_id.local_id];
|
||||||
let current = self.lower_enum_variant(
|
if variant_data.variant_data.kind() == StructKind::Unit {
|
||||||
variant_id,
|
let ty = self.infer.type_of_expr[expr_id].clone();
|
||||||
current,
|
current = self.lower_enum_variant(
|
||||||
place,
|
variant_id,
|
||||||
ty,
|
current,
|
||||||
vec![],
|
place,
|
||||||
expr_id.into(),
|
ty,
|
||||||
)?;
|
vec![],
|
||||||
|
expr_id.into(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
// Otherwise its a tuple like enum, treated like a zero sized function, so no action is needed
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
ValueNs::GenericParam(p) => {
|
ValueNs::GenericParam(p) => {
|
||||||
|
@ -287,7 +319,7 @@ impl MirLowerCtx<'_> {
|
||||||
);
|
);
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
ValueNs::StructId(_) => {
|
ValueNs::FunctionId(_) | ValueNs::StructId(_) => {
|
||||||
// It's probably a unit struct or a zero sized function, so no action is needed.
|
// It's probably a unit struct or a zero sized function, so no action is needed.
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
|
@ -349,19 +381,29 @@ impl MirLowerCtx<'_> {
|
||||||
Ok(self.merge_blocks(Some(then_target), else_target))
|
Ok(self.merge_blocks(Some(then_target), else_target))
|
||||||
}
|
}
|
||||||
Expr::Unsafe { id: _, statements, tail } => {
|
Expr::Unsafe { id: _, statements, tail } => {
|
||||||
self.lower_block_to_place(None, statements, current, *tail, place)
|
self.lower_block_to_place(statements, current, *tail, place)
|
||||||
}
|
}
|
||||||
Expr::Block { id: _, statements, tail, label } => {
|
Expr::Block { id: _, statements, tail, label } => {
|
||||||
self.lower_block_to_place(*label, statements, current, *tail, place)
|
if let Some(label) = label {
|
||||||
|
self.lower_loop(current, place.clone(), Some(*label), |this, begin| {
|
||||||
|
if let Some(block) = this.lower_block_to_place(statements, begin, *tail, place)? {
|
||||||
|
let end = this.current_loop_end()?;
|
||||||
|
this.set_goto(block, end);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
self.lower_block_to_place(statements, current, *tail, place)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| {
|
Expr::Loop { body, label } => self.lower_loop(current, place, *label, |this, begin| {
|
||||||
if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
|
if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
|
||||||
this.set_goto(block, begin);
|
this.set_goto(block, begin);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
Expr::While { condition, body, label } => {
|
Expr::While { condition, body, label } => {
|
||||||
self.lower_loop(current, *label, |this, begin| {
|
self.lower_loop(current, place, *label, |this, begin| {
|
||||||
let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else {
|
let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
@ -412,7 +454,7 @@ impl MirLowerCtx<'_> {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into());
|
self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into());
|
||||||
self.lower_loop(current, label, |this, begin| {
|
self.lower_loop(current, place, label, |this, begin| {
|
||||||
let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)?
|
let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)?
|
||||||
else {
|
else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -426,7 +468,8 @@ impl MirLowerCtx<'_> {
|
||||||
current,
|
current,
|
||||||
pat.into(),
|
pat.into(),
|
||||||
Some(end),
|
Some(end),
|
||||||
&[pat], &None)?;
|
AdtPatternShape::Tuple { args: &[pat], ellipsis: None },
|
||||||
|
)?;
|
||||||
if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
|
if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
|
||||||
this.set_goto(block, begin);
|
this.set_goto(block, begin);
|
||||||
}
|
}
|
||||||
|
@ -434,36 +477,36 @@ impl MirLowerCtx<'_> {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Expr::Call { callee, args, .. } => {
|
Expr::Call { callee, args, .. } => {
|
||||||
|
if let Some((func_id, generic_args)) =
|
||||||
|
self.infer.method_resolution(expr_id) {
|
||||||
|
let ty = chalk_ir::TyKind::FnDef(
|
||||||
|
CallableDefId::FunctionId(func_id).to_chalk(self.db),
|
||||||
|
generic_args,
|
||||||
|
)
|
||||||
|
.intern(Interner);
|
||||||
|
let func = Operand::from_bytes(vec![], ty);
|
||||||
|
return self.lower_call_and_args(
|
||||||
|
func,
|
||||||
|
iter::once(*callee).chain(args.iter().copied()),
|
||||||
|
place,
|
||||||
|
current,
|
||||||
|
self.is_uninhabited(expr_id),
|
||||||
|
);
|
||||||
|
}
|
||||||
let callee_ty = self.expr_ty_after_adjustments(*callee);
|
let callee_ty = self.expr_ty_after_adjustments(*callee);
|
||||||
match &callee_ty.data(Interner).kind {
|
match &callee_ty.data(Interner).kind {
|
||||||
chalk_ir::TyKind::FnDef(..) => {
|
chalk_ir::TyKind::FnDef(..) => {
|
||||||
let func = Operand::from_bytes(vec![], callee_ty.clone());
|
let func = Operand::from_bytes(vec![], callee_ty.clone());
|
||||||
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
|
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
|
||||||
}
|
}
|
||||||
TyKind::Scalar(_)
|
chalk_ir::TyKind::Function(_) => {
|
||||||
| TyKind::Tuple(_, _)
|
let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
|
||||||
| TyKind::Array(_, _)
|
return Ok(None);
|
||||||
| TyKind::Adt(_, _)
|
};
|
||||||
| TyKind::Str
|
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
|
||||||
| TyKind::Foreign(_)
|
|
||||||
| TyKind::Slice(_) => {
|
|
||||||
return Err(MirLowerError::TypeError("function call on data type"))
|
|
||||||
}
|
}
|
||||||
TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
|
TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
|
||||||
TyKind::AssociatedType(_, _)
|
_ => return Err(MirLowerError::TypeError("function call on bad type")),
|
||||||
| TyKind::Raw(_, _)
|
|
||||||
| TyKind::Ref(_, _, _)
|
|
||||||
| TyKind::OpaqueType(_, _)
|
|
||||||
| TyKind::Never
|
|
||||||
| TyKind::Closure(_, _)
|
|
||||||
| TyKind::Generator(_, _)
|
|
||||||
| TyKind::GeneratorWitness(_, _)
|
|
||||||
| TyKind::Placeholder(_)
|
|
||||||
| TyKind::Dyn(_)
|
|
||||||
| TyKind::Alias(_)
|
|
||||||
| TyKind::Function(_)
|
|
||||||
| TyKind::BoundVar(_)
|
|
||||||
| TyKind::InferenceVar(_, _) => not_supported!("dynamic function call"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::MethodCall { receiver, args, .. } => {
|
Expr::MethodCall { receiver, args, .. } => {
|
||||||
|
@ -491,10 +534,7 @@ impl MirLowerCtx<'_> {
|
||||||
let cond_ty = self.expr_ty_after_adjustments(*expr);
|
let cond_ty = self.expr_ty_after_adjustments(*expr);
|
||||||
let mut end = None;
|
let mut end = None;
|
||||||
for MatchArm { pat, guard, expr } in arms.iter() {
|
for MatchArm { pat, guard, expr } in arms.iter() {
|
||||||
if guard.is_some() {
|
let (then, mut otherwise) = self.pattern_match(
|
||||||
not_supported!("pattern matching with guard");
|
|
||||||
}
|
|
||||||
let (then, otherwise) = self.pattern_match(
|
|
||||||
current,
|
current,
|
||||||
None,
|
None,
|
||||||
cond_place.clone(),
|
cond_place.clone(),
|
||||||
|
@ -502,6 +542,16 @@ impl MirLowerCtx<'_> {
|
||||||
*pat,
|
*pat,
|
||||||
BindingAnnotation::Unannotated,
|
BindingAnnotation::Unannotated,
|
||||||
)?;
|
)?;
|
||||||
|
let then = if let &Some(guard) = guard {
|
||||||
|
let next = self.new_basic_block();
|
||||||
|
let o = otherwise.get_or_insert_with(|| self.new_basic_block());
|
||||||
|
if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? {
|
||||||
|
self.set_terminator(c, Terminator::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) });
|
||||||
|
}
|
||||||
|
next
|
||||||
|
} else {
|
||||||
|
then
|
||||||
|
};
|
||||||
if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? {
|
if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? {
|
||||||
let r = end.get_or_insert_with(|| self.new_basic_block());
|
let r = end.get_or_insert_with(|| self.new_basic_block());
|
||||||
self.set_goto(block, *r);
|
self.set_goto(block, *r);
|
||||||
|
@ -524,24 +574,28 @@ impl MirLowerCtx<'_> {
|
||||||
Some(_) => not_supported!("continue with label"),
|
Some(_) => not_supported!("continue with label"),
|
||||||
None => {
|
None => {
|
||||||
let loop_data =
|
let loop_data =
|
||||||
self.current_loop_blocks.ok_or(MirLowerError::ContinueWithoutLoop)?;
|
self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?;
|
||||||
self.set_goto(current, loop_data.begin);
|
self.set_goto(current, loop_data.begin);
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Expr::Break { expr, label } => {
|
Expr::Break { expr, label } => {
|
||||||
if expr.is_some() {
|
if let Some(expr) = expr {
|
||||||
not_supported!("break with value");
|
let loop_data = match label {
|
||||||
}
|
Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
|
||||||
match label {
|
None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?,
|
||||||
Some(_) => not_supported!("break with label"),
|
};
|
||||||
None => {
|
let Some(c) = self.lower_expr_to_place(*expr, loop_data.place.clone(), current)? else {
|
||||||
let end =
|
return Ok(None);
|
||||||
self.current_loop_end()?;
|
};
|
||||||
self.set_goto(current, end);
|
current = c;
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let end = match label {
|
||||||
|
Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?.end.expect("We always generate end for labeled loops"),
|
||||||
|
None => self.current_loop_end()?,
|
||||||
|
};
|
||||||
|
self.set_goto(current, end);
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
Expr::Return { expr } => {
|
Expr::Return { expr } => {
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
|
@ -555,7 +609,17 @@ impl MirLowerCtx<'_> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
Expr::Yield { .. } => not_supported!("yield"),
|
Expr::Yield { .. } => not_supported!("yield"),
|
||||||
Expr::RecordLit { fields, path, .. } => {
|
Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => {
|
||||||
|
let spread_place = match spread {
|
||||||
|
&Some(x) => {
|
||||||
|
let Some((p, c)) = self.lower_expr_as_place(current, x, true)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
current = c;
|
||||||
|
Some(p)
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
let variant_id = self
|
let variant_id = self
|
||||||
.infer
|
.infer
|
||||||
.variant_resolution_for_expr(expr_id)
|
.variant_resolution_for_expr(expr_id)
|
||||||
|
@ -585,9 +649,24 @@ impl MirLowerCtx<'_> {
|
||||||
place,
|
place,
|
||||||
Rvalue::Aggregate(
|
Rvalue::Aggregate(
|
||||||
AggregateKind::Adt(variant_id, subst),
|
AggregateKind::Adt(variant_id, subst),
|
||||||
operands.into_iter().map(|x| x).collect::<Option<_>>().ok_or(
|
match spread_place {
|
||||||
MirLowerError::TypeError("missing field in record literal"),
|
Some(sp) => operands.into_iter().enumerate().map(|(i, x)| {
|
||||||
)?,
|
match x {
|
||||||
|
Some(x) => x,
|
||||||
|
None => {
|
||||||
|
let mut p = sp.clone();
|
||||||
|
p.projection.push(ProjectionElem::Field(FieldId {
|
||||||
|
parent: variant_id,
|
||||||
|
local_id: LocalFieldId::from_raw(RawIdx::from(i as u32)),
|
||||||
|
}));
|
||||||
|
Operand::Copy(p)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}).collect(),
|
||||||
|
None => operands.into_iter().collect::<Option<_>>().ok_or(
|
||||||
|
MirLowerError::TypeError("missing field in record literal"),
|
||||||
|
)?,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
expr_id.into(),
|
expr_id.into(),
|
||||||
);
|
);
|
||||||
|
@ -608,9 +687,7 @@ impl MirLowerCtx<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Await { .. } => not_supported!("await"),
|
Expr::Await { .. } => not_supported!("await"),
|
||||||
Expr::Try { .. } => not_supported!("? operator"),
|
|
||||||
Expr::Yeet { .. } => not_supported!("yeet"),
|
Expr::Yeet { .. } => not_supported!("yeet"),
|
||||||
Expr::TryBlock { .. } => not_supported!("try block"),
|
|
||||||
Expr::Async { .. } => not_supported!("async block"),
|
Expr::Async { .. } => not_supported!("async block"),
|
||||||
Expr::Const { .. } => not_supported!("anonymous const block"),
|
Expr::Const { .. } => not_supported!("anonymous const block"),
|
||||||
Expr::Cast { expr, type_ref: _ } => {
|
Expr::Cast { expr, type_ref: _ } => {
|
||||||
|
@ -703,7 +780,49 @@ impl MirLowerCtx<'_> {
|
||||||
);
|
);
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
Expr::Range { .. } => not_supported!("range"),
|
&Expr::Range { lhs, rhs, range_type: _ } => {
|
||||||
|
let ty = self.expr_ty(expr_id);
|
||||||
|
let Some((adt, subst)) = ty.as_adt() else {
|
||||||
|
return Err(MirLowerError::TypeError("Range type is not adt"));
|
||||||
|
};
|
||||||
|
let AdtId::StructId(st) = adt else {
|
||||||
|
return Err(MirLowerError::TypeError("Range type is not struct"));
|
||||||
|
};
|
||||||
|
let mut lp = None;
|
||||||
|
let mut rp = None;
|
||||||
|
if let Some(x) = lhs {
|
||||||
|
let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
lp = Some(o);
|
||||||
|
current = c;
|
||||||
|
}
|
||||||
|
if let Some(x) = rhs {
|
||||||
|
let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
rp = Some(o);
|
||||||
|
current = c;
|
||||||
|
}
|
||||||
|
self.push_assignment(
|
||||||
|
current,
|
||||||
|
place,
|
||||||
|
Rvalue::Aggregate(
|
||||||
|
AggregateKind::Adt(st.into(), subst.clone()),
|
||||||
|
self.db.struct_data(st).variant_data.fields().iter().map(|x| {
|
||||||
|
let o = match x.1.name.as_str() {
|
||||||
|
Some("start") => lp.take(),
|
||||||
|
Some("end") => rp.take(),
|
||||||
|
Some("exhausted") => Some(Operand::from_bytes(vec![0], TyBuilder::bool())),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
o.ok_or(MirLowerError::UnresolvedField)
|
||||||
|
}).collect::<Result<_>>()?,
|
||||||
|
),
|
||||||
|
expr_id.into(),
|
||||||
|
);
|
||||||
|
Ok(Some(current))
|
||||||
|
},
|
||||||
Expr::Closure { .. } => not_supported!("closure"),
|
Expr::Closure { .. } => not_supported!("closure"),
|
||||||
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||||
let Some(values) = exprs
|
let Some(values) = exprs
|
||||||
|
@ -832,9 +951,10 @@ impl MirLowerCtx<'_> {
|
||||||
const_id: hir_def::ConstId,
|
const_id: hir_def::ConstId,
|
||||||
prev_block: BasicBlockId,
|
prev_block: BasicBlockId,
|
||||||
place: Place,
|
place: Place,
|
||||||
|
subst: Substitution,
|
||||||
span: MirSpan,
|
span: MirSpan,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let c = self.db.const_eval(const_id)?;
|
let c = self.db.const_eval(const_id, subst)?;
|
||||||
self.write_const_to_place(c, prev_block, place, span)
|
self.write_const_to_place(c, prev_block, place, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -872,7 +992,7 @@ impl MirLowerCtx<'_> {
|
||||||
) -> Result<BasicBlockId> {
|
) -> Result<BasicBlockId> {
|
||||||
let subst = match ty.kind(Interner) {
|
let subst = match ty.kind(Interner) {
|
||||||
TyKind::Adt(_, subst) => subst.clone(),
|
TyKind::Adt(_, subst) => subst.clone(),
|
||||||
_ => not_supported!("Non ADT enum"),
|
_ => implementation_error!("Non ADT enum"),
|
||||||
};
|
};
|
||||||
self.push_assignment(
|
self.push_assignment(
|
||||||
prev_block,
|
prev_block,
|
||||||
|
@ -970,287 +1090,6 @@ impl MirLowerCtx<'_> {
|
||||||
self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span));
|
self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if
|
|
||||||
/// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which
|
|
||||||
/// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the
|
|
||||||
/// mismatched path block is `None`.
|
|
||||||
///
|
|
||||||
/// By default, it will create a new block for mismatched path. If you already have one, you can provide it with
|
|
||||||
/// `current_else` argument to save an unneccessary jump. If `current_else` isn't `None`, the result mismatched path
|
|
||||||
/// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block,
|
|
||||||
/// so it should be an empty block.
|
|
||||||
fn pattern_match(
|
|
||||||
&mut self,
|
|
||||||
mut current: BasicBlockId,
|
|
||||||
mut current_else: Option<BasicBlockId>,
|
|
||||||
mut cond_place: Place,
|
|
||||||
mut cond_ty: Ty,
|
|
||||||
pattern: PatId,
|
|
||||||
mut binding_mode: BindingAnnotation,
|
|
||||||
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
|
||||||
Ok(match &self.body.pats[pattern] {
|
|
||||||
Pat::Missing => return Err(MirLowerError::IncompleteExpr),
|
|
||||||
Pat::Wild => (current, current_else),
|
|
||||||
Pat::Tuple { args, ellipsis } => {
|
|
||||||
pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
|
|
||||||
let subst = match cond_ty.kind(Interner) {
|
|
||||||
TyKind::Tuple(_, s) => s,
|
|
||||||
_ => {
|
|
||||||
return Err(MirLowerError::TypeError(
|
|
||||||
"non tuple type matched with tuple pattern",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.pattern_match_tuple_like(
|
|
||||||
current,
|
|
||||||
current_else,
|
|
||||||
args.iter().enumerate().map(|(i, x)| {
|
|
||||||
(
|
|
||||||
PlaceElem::TupleField(i),
|
|
||||||
*x,
|
|
||||||
subst.at(Interner, i).assert_ty_ref(Interner).clone(),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
*ellipsis,
|
|
||||||
&cond_place,
|
|
||||||
binding_mode,
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
Pat::Or(pats) => {
|
|
||||||
let then_target = self.new_basic_block();
|
|
||||||
let mut finished = false;
|
|
||||||
for pat in &**pats {
|
|
||||||
let (next, next_else) = self.pattern_match(
|
|
||||||
current,
|
|
||||||
None,
|
|
||||||
cond_place.clone(),
|
|
||||||
cond_ty.clone(),
|
|
||||||
*pat,
|
|
||||||
binding_mode,
|
|
||||||
)?;
|
|
||||||
self.set_goto(next, then_target);
|
|
||||||
match next_else {
|
|
||||||
Some(t) => {
|
|
||||||
current = t;
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
finished = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !finished {
|
|
||||||
let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
|
|
||||||
self.set_goto(current, ce);
|
|
||||||
}
|
|
||||||
(then_target, current_else)
|
|
||||||
}
|
|
||||||
Pat::Record { .. } => not_supported!("record pattern"),
|
|
||||||
Pat::Range { .. } => not_supported!("range pattern"),
|
|
||||||
Pat::Slice { .. } => not_supported!("slice pattern"),
|
|
||||||
Pat::Path(_) => {
|
|
||||||
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
|
|
||||||
not_supported!("unresolved variant");
|
|
||||||
};
|
|
||||||
self.pattern_matching_variant(
|
|
||||||
cond_ty,
|
|
||||||
binding_mode,
|
|
||||||
cond_place,
|
|
||||||
variant,
|
|
||||||
current,
|
|
||||||
pattern.into(),
|
|
||||||
current_else,
|
|
||||||
&[],
|
|
||||||
&None,
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
Pat::Lit(l) => {
|
|
||||||
let then_target = self.new_basic_block();
|
|
||||||
let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
|
|
||||||
match &self.body.exprs[*l] {
|
|
||||||
Expr::Literal(l) => match l {
|
|
||||||
hir_def::expr::Literal::Int(x, _) => {
|
|
||||||
self.set_terminator(
|
|
||||||
current,
|
|
||||||
Terminator::SwitchInt {
|
|
||||||
discr: Operand::Copy(cond_place),
|
|
||||||
targets: SwitchTargets::static_if(
|
|
||||||
*x as u128,
|
|
||||||
then_target,
|
|
||||||
else_target,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
hir_def::expr::Literal::Uint(x, _) => {
|
|
||||||
self.set_terminator(
|
|
||||||
current,
|
|
||||||
Terminator::SwitchInt {
|
|
||||||
discr: Operand::Copy(cond_place),
|
|
||||||
targets: SwitchTargets::static_if(*x, then_target, else_target),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => not_supported!("non int path literal"),
|
|
||||||
},
|
|
||||||
_ => not_supported!("expression path literal"),
|
|
||||||
}
|
|
||||||
(then_target, Some(else_target))
|
|
||||||
}
|
|
||||||
Pat::Bind { id, subpat } => {
|
|
||||||
let target_place = self.result.binding_locals[*id];
|
|
||||||
let mode = self.body.bindings[*id].mode;
|
|
||||||
if let Some(subpat) = subpat {
|
|
||||||
(current, current_else) = self.pattern_match(
|
|
||||||
current,
|
|
||||||
current_else,
|
|
||||||
cond_place.clone(),
|
|
||||||
cond_ty,
|
|
||||||
*subpat,
|
|
||||||
binding_mode,
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
|
|
||||||
binding_mode = mode;
|
|
||||||
}
|
|
||||||
self.push_storage_live(*id, current);
|
|
||||||
self.push_assignment(
|
|
||||||
current,
|
|
||||||
target_place.into(),
|
|
||||||
match binding_mode {
|
|
||||||
BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
|
|
||||||
Operand::Copy(cond_place).into()
|
|
||||||
}
|
|
||||||
BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place),
|
|
||||||
BindingAnnotation::RefMut => Rvalue::Ref(
|
|
||||||
BorrowKind::Mut { allow_two_phase_borrow: false },
|
|
||||||
cond_place,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
pattern.into(),
|
|
||||||
);
|
|
||||||
(current, current_else)
|
|
||||||
}
|
|
||||||
Pat::TupleStruct { path: _, args, ellipsis } => {
|
|
||||||
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
|
|
||||||
not_supported!("unresolved variant");
|
|
||||||
};
|
|
||||||
self.pattern_matching_variant(
|
|
||||||
cond_ty,
|
|
||||||
binding_mode,
|
|
||||||
cond_place,
|
|
||||||
variant,
|
|
||||||
current,
|
|
||||||
pattern.into(),
|
|
||||||
current_else,
|
|
||||||
args,
|
|
||||||
ellipsis,
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
Pat::Ref { .. } => not_supported!("& pattern"),
|
|
||||||
Pat::Box { .. } => not_supported!("box pattern"),
|
|
||||||
Pat::ConstBlock(_) => not_supported!("const block pattern"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pattern_matching_variant(
|
|
||||||
&mut self,
|
|
||||||
mut cond_ty: Ty,
|
|
||||||
mut binding_mode: BindingAnnotation,
|
|
||||||
mut cond_place: Place,
|
|
||||||
variant: VariantId,
|
|
||||||
current: BasicBlockId,
|
|
||||||
span: MirSpan,
|
|
||||||
current_else: Option<BasicBlockId>,
|
|
||||||
args: &[PatId],
|
|
||||||
ellipsis: &Option<usize>,
|
|
||||||
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
|
||||||
pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
|
|
||||||
let subst = match cond_ty.kind(Interner) {
|
|
||||||
TyKind::Adt(_, s) => s,
|
|
||||||
_ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")),
|
|
||||||
};
|
|
||||||
let fields_type = self.db.field_types(variant);
|
|
||||||
Ok(match variant {
|
|
||||||
VariantId::EnumVariantId(v) => {
|
|
||||||
let e = self.db.const_eval_discriminant(v)? as u128;
|
|
||||||
let next = self.new_basic_block();
|
|
||||||
let tmp = self.discr_temp_place();
|
|
||||||
self.push_assignment(
|
|
||||||
current,
|
|
||||||
tmp.clone(),
|
|
||||||
Rvalue::Discriminant(cond_place.clone()),
|
|
||||||
span,
|
|
||||||
);
|
|
||||||
let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
|
|
||||||
self.set_terminator(
|
|
||||||
current,
|
|
||||||
Terminator::SwitchInt {
|
|
||||||
discr: Operand::Copy(tmp),
|
|
||||||
targets: SwitchTargets::static_if(e, next, else_target),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let enum_data = self.db.enum_data(v.parent);
|
|
||||||
let fields =
|
|
||||||
enum_data.variants[v.local_id].variant_data.fields().iter().map(|(x, _)| {
|
|
||||||
(
|
|
||||||
PlaceElem::Field(FieldId { parent: v.into(), local_id: x }),
|
|
||||||
fields_type[x].clone().substitute(Interner, subst),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
self.pattern_match_tuple_like(
|
|
||||||
next,
|
|
||||||
Some(else_target),
|
|
||||||
args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
|
|
||||||
*ellipsis,
|
|
||||||
&cond_place,
|
|
||||||
binding_mode,
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
VariantId::StructId(s) => {
|
|
||||||
let struct_data = self.db.struct_data(s);
|
|
||||||
let fields = struct_data.variant_data.fields().iter().map(|(x, _)| {
|
|
||||||
(
|
|
||||||
PlaceElem::Field(FieldId { parent: s.into(), local_id: x }),
|
|
||||||
fields_type[x].clone().substitute(Interner, subst),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
self.pattern_match_tuple_like(
|
|
||||||
current,
|
|
||||||
current_else,
|
|
||||||
args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
|
|
||||||
*ellipsis,
|
|
||||||
&cond_place,
|
|
||||||
binding_mode,
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
VariantId::UnionId(_) => {
|
|
||||||
return Err(MirLowerError::TypeError("pattern matching on union"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pattern_match_tuple_like(
|
|
||||||
&mut self,
|
|
||||||
mut current: BasicBlockId,
|
|
||||||
mut current_else: Option<BasicBlockId>,
|
|
||||||
args: impl Iterator<Item = (PlaceElem, PatId, Ty)>,
|
|
||||||
ellipsis: Option<usize>,
|
|
||||||
cond_place: &Place,
|
|
||||||
binding_mode: BindingAnnotation,
|
|
||||||
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
|
||||||
if ellipsis.is_some() {
|
|
||||||
not_supported!("tuple like pattern with ellipsis");
|
|
||||||
}
|
|
||||||
for (proj, arg, ty) in args {
|
|
||||||
let mut cond_place = cond_place.clone();
|
|
||||||
cond_place.projection.push(proj);
|
|
||||||
(current, current_else) =
|
|
||||||
self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?;
|
|
||||||
}
|
|
||||||
Ok((current, current_else))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn discr_temp_place(&mut self) -> Place {
|
fn discr_temp_place(&mut self) -> Place {
|
||||||
match &self.discr_temp {
|
match &self.discr_temp {
|
||||||
Some(x) => x.clone(),
|
Some(x) => x.clone(),
|
||||||
|
@ -1266,19 +1105,34 @@ impl MirLowerCtx<'_> {
|
||||||
fn lower_loop(
|
fn lower_loop(
|
||||||
&mut self,
|
&mut self,
|
||||||
prev_block: BasicBlockId,
|
prev_block: BasicBlockId,
|
||||||
|
place: Place,
|
||||||
label: Option<LabelId>,
|
label: Option<LabelId>,
|
||||||
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
|
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
|
||||||
) -> Result<Option<BasicBlockId>> {
|
) -> Result<Option<BasicBlockId>> {
|
||||||
if label.is_some() {
|
|
||||||
not_supported!("loop with label");
|
|
||||||
}
|
|
||||||
let begin = self.new_basic_block();
|
let begin = self.new_basic_block();
|
||||||
let prev =
|
let prev = mem::replace(
|
||||||
mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end: None }));
|
&mut self.current_loop_blocks,
|
||||||
|
Some(LoopBlocks { begin, end: None, place }),
|
||||||
|
);
|
||||||
|
let prev_label = if let Some(label) = label {
|
||||||
|
// We should generate the end now, to make sure that it wouldn't change later. It is
|
||||||
|
// bad as we may emit end (unneccessary unreachable block) for unterminating loop, but
|
||||||
|
// it should not affect correctness.
|
||||||
|
self.current_loop_end()?;
|
||||||
|
self.labeled_loop_blocks.insert(
|
||||||
|
self.body.labels[label].name.clone(),
|
||||||
|
self.current_loop_blocks.as_ref().unwrap().clone(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
self.set_goto(prev_block, begin);
|
self.set_goto(prev_block, begin);
|
||||||
f(self, begin)?;
|
f(self, begin)?;
|
||||||
let my = mem::replace(&mut self.current_loop_blocks, prev)
|
let my = mem::replace(&mut self.current_loop_blocks, prev)
|
||||||
.ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?;
|
.ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?;
|
||||||
|
if let Some(prev) = prev_label {
|
||||||
|
self.labeled_loop_blocks.insert(self.body.labels[label.unwrap()].name.clone(), prev);
|
||||||
|
}
|
||||||
Ok(my.end)
|
Ok(my.end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1366,15 +1220,11 @@ impl MirLowerCtx<'_> {
|
||||||
|
|
||||||
fn lower_block_to_place(
|
fn lower_block_to_place(
|
||||||
&mut self,
|
&mut self,
|
||||||
label: Option<LabelId>,
|
|
||||||
statements: &[hir_def::expr::Statement],
|
statements: &[hir_def::expr::Statement],
|
||||||
mut current: BasicBlockId,
|
mut current: BasicBlockId,
|
||||||
tail: Option<ExprId>,
|
tail: Option<ExprId>,
|
||||||
place: Place,
|
place: Place,
|
||||||
) -> Result<Option<Idx<BasicBlock>>> {
|
) -> Result<Option<Idx<BasicBlock>>> {
|
||||||
if label.is_some() {
|
|
||||||
not_supported!("block with label");
|
|
||||||
}
|
|
||||||
for statement in statements.iter() {
|
for statement in statements.iter() {
|
||||||
match statement {
|
match statement {
|
||||||
hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
|
hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
|
||||||
|
@ -1428,22 +1278,6 @@ impl MirLowerCtx<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pattern_matching_dereference(
|
|
||||||
cond_ty: &mut Ty,
|
|
||||||
binding_mode: &mut BindingAnnotation,
|
|
||||||
cond_place: &mut Place,
|
|
||||||
) {
|
|
||||||
while let Some((ty, _, mu)) = cond_ty.as_reference() {
|
|
||||||
if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
|
|
||||||
*binding_mode = BindingAnnotation::RefMut;
|
|
||||||
} else {
|
|
||||||
*binding_mode = BindingAnnotation::Ref;
|
|
||||||
}
|
|
||||||
*cond_ty = ty.clone();
|
|
||||||
cond_place.projection.push(ProjectionElem::Deref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
|
fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
|
||||||
Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) {
|
Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) {
|
||||||
(TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) {
|
(TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) {
|
||||||
|
@ -1452,6 +1286,11 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
|
||||||
(_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat,
|
(_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat,
|
||||||
(_, _) => CastKind::IntToInt,
|
(_, _) => CastKind::IntToInt,
|
||||||
},
|
},
|
||||||
|
(TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress,
|
||||||
|
(TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress,
|
||||||
|
(TyKind::Raw(..) | TyKind::Ref(..), TyKind::Raw(..) | TyKind::Ref(..)) => {
|
||||||
|
CastKind::PtrToPtr
|
||||||
|
}
|
||||||
// Enum to int casts
|
// Enum to int casts
|
||||||
(TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
|
(TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
|
||||||
CastKind::IntToInt
|
CastKind::IntToInt
|
||||||
|
@ -1552,6 +1391,7 @@ pub fn lower_to_mir(
|
||||||
body,
|
body,
|
||||||
owner,
|
owner,
|
||||||
current_loop_blocks: None,
|
current_loop_blocks: None,
|
||||||
|
labeled_loop_blocks: Default::default(),
|
||||||
discr_temp: None,
|
discr_temp: None,
|
||||||
};
|
};
|
||||||
let mut current = start_block;
|
let mut current = start_block;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//! MIR lowering for places
|
//! MIR lowering for places
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use hir_def::FunctionId;
|
||||||
use hir_expand::name;
|
use hir_expand::name;
|
||||||
|
|
||||||
macro_rules! not_supported {
|
macro_rules! not_supported {
|
||||||
|
@ -125,7 +126,7 @@ impl MirLowerCtx<'_> {
|
||||||
match &self.body.exprs[expr_id] {
|
match &self.body.exprs[expr_id] {
|
||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
||||||
let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) else {
|
let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) else {
|
||||||
return Err(MirLowerError::unresolved_path(self.db, p));
|
return Err(MirLowerError::unresolved_path(self.db, p));
|
||||||
};
|
};
|
||||||
let pr = match pr {
|
let pr = match pr {
|
||||||
|
@ -145,10 +146,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);
|
||||||
|
@ -171,7 +194,24 @@ impl MirLowerCtx<'_> {
|
||||||
if index_ty != TyBuilder::usize()
|
if index_ty != TyBuilder::usize()
|
||||||
|| !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..))
|
|| !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..))
|
||||||
{
|
{
|
||||||
not_supported!("overloaded index");
|
let Some(index_fn) = self.infer.method_resolution(expr_id) else {
|
||||||
|
return Err(MirLowerError::UnresolvedMethod);
|
||||||
|
};
|
||||||
|
let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
let Some((index_operand, current)) = self.lower_expr_to_some_operand(*index, current)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
return self.lower_overloaded_index(
|
||||||
|
current,
|
||||||
|
base_place,
|
||||||
|
self.expr_ty_after_adjustments(*base),
|
||||||
|
self.expr_ty(expr_id),
|
||||||
|
index_operand,
|
||||||
|
expr_id.into(),
|
||||||
|
index_fn,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let Some((mut p_base, current)) =
|
let Some((mut p_base, current)) =
|
||||||
self.lower_expr_as_place(current, *base, true)? else {
|
self.lower_expr_as_place(current, *base, true)? else {
|
||||||
|
@ -188,6 +228,49 @@ impl MirLowerCtx<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lower_overloaded_index(
|
||||||
|
&mut self,
|
||||||
|
current: BasicBlockId,
|
||||||
|
place: Place,
|
||||||
|
base_ty: Ty,
|
||||||
|
result_ty: Ty,
|
||||||
|
index_operand: Operand,
|
||||||
|
span: MirSpan,
|
||||||
|
index_fn: (FunctionId, Substitution),
|
||||||
|
) -> Result<Option<(Place, BasicBlockId)>> {
|
||||||
|
let is_mutable = 'b: {
|
||||||
|
if let Some(index_mut_trait) = self.resolve_lang_item(LangItem::IndexMut)?.as_trait() {
|
||||||
|
if let Some(index_mut_fn) =
|
||||||
|
self.db.trait_data(index_mut_trait).method_by_name(&name![index_mut])
|
||||||
|
{
|
||||||
|
break 'b index_mut_fn == index_fn.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
};
|
||||||
|
let (mutability, borrow_kind) = match is_mutable {
|
||||||
|
true => (Mutability::Mut, BorrowKind::Mut { allow_two_phase_borrow: false }),
|
||||||
|
false => (Mutability::Not, BorrowKind::Shared),
|
||||||
|
};
|
||||||
|
let base_ref = TyKind::Ref(mutability, static_lifetime(), base_ty).intern(Interner);
|
||||||
|
let result_ref = TyKind::Ref(mutability, static_lifetime(), result_ty).intern(Interner);
|
||||||
|
let ref_place: Place = self.temp(base_ref)?.into();
|
||||||
|
self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span);
|
||||||
|
let mut result: Place = self.temp(result_ref)?.into();
|
||||||
|
let index_fn_op = Operand::const_zst(
|
||||||
|
TyKind::FnDef(
|
||||||
|
self.db.intern_callable_def(CallableDefId::FunctionId(index_fn.0)).into(),
|
||||||
|
index_fn.1,
|
||||||
|
)
|
||||||
|
.intern(Interner),
|
||||||
|
);
|
||||||
|
let Some(current) = self.lower_call(index_fn_op, vec![Operand::Copy(ref_place), index_operand], result.clone(), current, false)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
result.projection.push(ProjectionElem::Deref);
|
||||||
|
Ok(Some((result, current)))
|
||||||
|
}
|
||||||
|
|
||||||
fn lower_overloaded_deref(
|
fn lower_overloaded_deref(
|
||||||
&mut self,
|
&mut self,
|
||||||
current: BasicBlockId,
|
current: BasicBlockId,
|
||||||
|
|
399
crates/hir-ty/src/mir/lower/pattern_matching.rs
Normal file
399
crates/hir-ty/src/mir/lower/pattern_matching.rs
Normal file
|
@ -0,0 +1,399 @@
|
||||||
|
//! MIR lowering for patterns
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
macro_rules! not_supported {
|
||||||
|
($x: expr) => {
|
||||||
|
return Err(MirLowerError::NotSupported(format!($x)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) enum AdtPatternShape<'a> {
|
||||||
|
Tuple { args: &'a [PatId], ellipsis: Option<usize> },
|
||||||
|
Record { args: &'a [RecordFieldPat] },
|
||||||
|
Unit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MirLowerCtx<'_> {
|
||||||
|
/// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if
|
||||||
|
/// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which
|
||||||
|
/// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the
|
||||||
|
/// mismatched path block is `None`.
|
||||||
|
///
|
||||||
|
/// By default, it will create a new block for mismatched path. If you already have one, you can provide it with
|
||||||
|
/// `current_else` argument to save an unneccessary jump. If `current_else` isn't `None`, the result mismatched path
|
||||||
|
/// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block,
|
||||||
|
/// so it should be an empty block.
|
||||||
|
pub(super) fn pattern_match(
|
||||||
|
&mut self,
|
||||||
|
mut current: BasicBlockId,
|
||||||
|
mut current_else: Option<BasicBlockId>,
|
||||||
|
mut cond_place: Place,
|
||||||
|
mut cond_ty: Ty,
|
||||||
|
pattern: PatId,
|
||||||
|
mut binding_mode: BindingAnnotation,
|
||||||
|
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
||||||
|
Ok(match &self.body.pats[pattern] {
|
||||||
|
Pat::Missing => return Err(MirLowerError::IncompleteExpr),
|
||||||
|
Pat::Wild => (current, current_else),
|
||||||
|
Pat::Tuple { args, ellipsis } => {
|
||||||
|
pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
|
||||||
|
let subst = match cond_ty.kind(Interner) {
|
||||||
|
TyKind::Tuple(_, s) => s,
|
||||||
|
_ => {
|
||||||
|
return Err(MirLowerError::TypeError(
|
||||||
|
"non tuple type matched with tuple pattern",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.pattern_match_tuple_like(
|
||||||
|
current,
|
||||||
|
current_else,
|
||||||
|
args,
|
||||||
|
*ellipsis,
|
||||||
|
subst.iter(Interner).enumerate().map(|(i, x)| {
|
||||||
|
(PlaceElem::TupleField(i), x.assert_ty_ref(Interner).clone())
|
||||||
|
}),
|
||||||
|
&cond_place,
|
||||||
|
binding_mode,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
Pat::Or(pats) => {
|
||||||
|
let then_target = self.new_basic_block();
|
||||||
|
let mut finished = false;
|
||||||
|
for pat in &**pats {
|
||||||
|
let (next, next_else) = self.pattern_match(
|
||||||
|
current,
|
||||||
|
None,
|
||||||
|
cond_place.clone(),
|
||||||
|
cond_ty.clone(),
|
||||||
|
*pat,
|
||||||
|
binding_mode,
|
||||||
|
)?;
|
||||||
|
self.set_goto(next, then_target);
|
||||||
|
match next_else {
|
||||||
|
Some(t) => {
|
||||||
|
current = t;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
finished = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !finished {
|
||||||
|
let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
|
||||||
|
self.set_goto(current, ce);
|
||||||
|
}
|
||||||
|
(then_target, current_else)
|
||||||
|
}
|
||||||
|
Pat::Record { args, .. } => {
|
||||||
|
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
|
||||||
|
not_supported!("unresolved variant");
|
||||||
|
};
|
||||||
|
self.pattern_matching_variant(
|
||||||
|
cond_ty,
|
||||||
|
binding_mode,
|
||||||
|
cond_place,
|
||||||
|
variant,
|
||||||
|
current,
|
||||||
|
pattern.into(),
|
||||||
|
current_else,
|
||||||
|
AdtPatternShape::Record { args: &*args },
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
Pat::Range { .. } => not_supported!("range pattern"),
|
||||||
|
Pat::Slice { .. } => not_supported!("slice pattern"),
|
||||||
|
Pat::Path(_) => {
|
||||||
|
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
|
||||||
|
not_supported!("unresolved variant");
|
||||||
|
};
|
||||||
|
self.pattern_matching_variant(
|
||||||
|
cond_ty,
|
||||||
|
binding_mode,
|
||||||
|
cond_place,
|
||||||
|
variant,
|
||||||
|
current,
|
||||||
|
pattern.into(),
|
||||||
|
current_else,
|
||||||
|
AdtPatternShape::Unit,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
Pat::Lit(l) => match &self.body.exprs[*l] {
|
||||||
|
Expr::Literal(l) => {
|
||||||
|
let c = self.lower_literal_to_operand(cond_ty, l)?;
|
||||||
|
self.pattern_match_const(current_else, current, c, cond_place, pattern)?
|
||||||
|
}
|
||||||
|
_ => not_supported!("expression path literal"),
|
||||||
|
},
|
||||||
|
Pat::Bind { id, subpat } => {
|
||||||
|
let target_place = self.result.binding_locals[*id];
|
||||||
|
let mode = self.body.bindings[*id].mode;
|
||||||
|
if let Some(subpat) = subpat {
|
||||||
|
(current, current_else) = self.pattern_match(
|
||||||
|
current,
|
||||||
|
current_else,
|
||||||
|
cond_place.clone(),
|
||||||
|
cond_ty,
|
||||||
|
*subpat,
|
||||||
|
binding_mode,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
|
||||||
|
binding_mode = mode;
|
||||||
|
}
|
||||||
|
self.push_storage_live(*id, current);
|
||||||
|
self.push_assignment(
|
||||||
|
current,
|
||||||
|
target_place.into(),
|
||||||
|
match binding_mode {
|
||||||
|
BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
|
||||||
|
Operand::Copy(cond_place).into()
|
||||||
|
}
|
||||||
|
BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place),
|
||||||
|
BindingAnnotation::RefMut => Rvalue::Ref(
|
||||||
|
BorrowKind::Mut { allow_two_phase_borrow: false },
|
||||||
|
cond_place,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
pattern.into(),
|
||||||
|
);
|
||||||
|
(current, current_else)
|
||||||
|
}
|
||||||
|
Pat::TupleStruct { path: _, args, ellipsis } => {
|
||||||
|
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
|
||||||
|
not_supported!("unresolved variant");
|
||||||
|
};
|
||||||
|
self.pattern_matching_variant(
|
||||||
|
cond_ty,
|
||||||
|
binding_mode,
|
||||||
|
cond_place,
|
||||||
|
variant,
|
||||||
|
current,
|
||||||
|
pattern.into(),
|
||||||
|
current_else,
|
||||||
|
AdtPatternShape::Tuple { args, ellipsis: *ellipsis },
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
Pat::Ref { pat, mutability: _ } => {
|
||||||
|
if let Some((ty, _, _)) = cond_ty.as_reference() {
|
||||||
|
cond_ty = ty.clone();
|
||||||
|
cond_place.projection.push(ProjectionElem::Deref);
|
||||||
|
self.pattern_match(
|
||||||
|
current,
|
||||||
|
current_else,
|
||||||
|
cond_place,
|
||||||
|
cond_ty,
|
||||||
|
*pat,
|
||||||
|
binding_mode,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
return Err(MirLowerError::TypeError("& pattern for non reference"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pat::Box { .. } => not_supported!("box pattern"),
|
||||||
|
Pat::ConstBlock(_) => not_supported!("const block pattern"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pattern_match_const(
|
||||||
|
&mut self,
|
||||||
|
current_else: Option<BasicBlockId>,
|
||||||
|
current: BasicBlockId,
|
||||||
|
c: Operand,
|
||||||
|
cond_place: Place,
|
||||||
|
pattern: Idx<Pat>,
|
||||||
|
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
||||||
|
let then_target = self.new_basic_block();
|
||||||
|
let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
|
||||||
|
let discr: Place = self.temp(TyBuilder::bool())?.into();
|
||||||
|
self.push_assignment(
|
||||||
|
current,
|
||||||
|
discr.clone(),
|
||||||
|
Rvalue::CheckedBinaryOp(BinOp::Eq, c, Operand::Copy(cond_place)),
|
||||||
|
pattern.into(),
|
||||||
|
);
|
||||||
|
let discr = Operand::Copy(discr);
|
||||||
|
self.set_terminator(
|
||||||
|
current,
|
||||||
|
Terminator::SwitchInt {
|
||||||
|
discr,
|
||||||
|
targets: SwitchTargets::static_if(1, then_target, else_target),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Ok((then_target, Some(else_target)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn pattern_matching_variant(
|
||||||
|
&mut self,
|
||||||
|
mut cond_ty: Ty,
|
||||||
|
mut binding_mode: BindingAnnotation,
|
||||||
|
mut cond_place: Place,
|
||||||
|
variant: VariantId,
|
||||||
|
current: BasicBlockId,
|
||||||
|
span: MirSpan,
|
||||||
|
current_else: Option<BasicBlockId>,
|
||||||
|
shape: AdtPatternShape<'_>,
|
||||||
|
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
||||||
|
pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
|
||||||
|
let subst = match cond_ty.kind(Interner) {
|
||||||
|
TyKind::Adt(_, s) => s,
|
||||||
|
_ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")),
|
||||||
|
};
|
||||||
|
Ok(match variant {
|
||||||
|
VariantId::EnumVariantId(v) => {
|
||||||
|
let e = self.db.const_eval_discriminant(v)? as u128;
|
||||||
|
let next = self.new_basic_block();
|
||||||
|
let tmp = self.discr_temp_place();
|
||||||
|
self.push_assignment(
|
||||||
|
current,
|
||||||
|
tmp.clone(),
|
||||||
|
Rvalue::Discriminant(cond_place.clone()),
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
|
||||||
|
self.set_terminator(
|
||||||
|
current,
|
||||||
|
Terminator::SwitchInt {
|
||||||
|
discr: Operand::Copy(tmp),
|
||||||
|
targets: SwitchTargets::static_if(e, next, else_target),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let enum_data = self.db.enum_data(v.parent);
|
||||||
|
self.pattern_matching_variant_fields(
|
||||||
|
shape,
|
||||||
|
&enum_data.variants[v.local_id].variant_data,
|
||||||
|
variant,
|
||||||
|
subst,
|
||||||
|
next,
|
||||||
|
Some(else_target),
|
||||||
|
&cond_place,
|
||||||
|
binding_mode,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
VariantId::StructId(s) => {
|
||||||
|
let struct_data = self.db.struct_data(s);
|
||||||
|
self.pattern_matching_variant_fields(
|
||||||
|
shape,
|
||||||
|
&struct_data.variant_data,
|
||||||
|
variant,
|
||||||
|
subst,
|
||||||
|
current,
|
||||||
|
current_else,
|
||||||
|
&cond_place,
|
||||||
|
binding_mode,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
VariantId::UnionId(_) => {
|
||||||
|
return Err(MirLowerError::TypeError("pattern matching on union"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pattern_matching_variant_fields(
|
||||||
|
&mut self,
|
||||||
|
shape: AdtPatternShape<'_>,
|
||||||
|
variant_data: &VariantData,
|
||||||
|
v: VariantId,
|
||||||
|
subst: &Substitution,
|
||||||
|
current: BasicBlockId,
|
||||||
|
current_else: Option<BasicBlockId>,
|
||||||
|
cond_place: &Place,
|
||||||
|
binding_mode: BindingAnnotation,
|
||||||
|
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
||||||
|
let fields_type = self.db.field_types(v);
|
||||||
|
Ok(match shape {
|
||||||
|
AdtPatternShape::Record { args } => {
|
||||||
|
let it = args
|
||||||
|
.iter()
|
||||||
|
.map(|x| {
|
||||||
|
let field_id =
|
||||||
|
variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?;
|
||||||
|
Ok((
|
||||||
|
PlaceElem::Field(FieldId { parent: v.into(), local_id: field_id }),
|
||||||
|
x.pat,
|
||||||
|
fields_type[field_id].clone().substitute(Interner, subst),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
self.pattern_match_adt(
|
||||||
|
current,
|
||||||
|
current_else,
|
||||||
|
it.into_iter(),
|
||||||
|
cond_place,
|
||||||
|
binding_mode,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
AdtPatternShape::Tuple { args, ellipsis } => {
|
||||||
|
let fields = variant_data.fields().iter().map(|(x, _)| {
|
||||||
|
(
|
||||||
|
PlaceElem::Field(FieldId { parent: v.into(), local_id: x }),
|
||||||
|
fields_type[x].clone().substitute(Interner, subst),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
self.pattern_match_tuple_like(
|
||||||
|
current,
|
||||||
|
current_else,
|
||||||
|
args,
|
||||||
|
ellipsis,
|
||||||
|
fields,
|
||||||
|
cond_place,
|
||||||
|
binding_mode,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
AdtPatternShape::Unit => (current, current_else),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pattern_match_adt(
|
||||||
|
&mut self,
|
||||||
|
mut current: BasicBlockId,
|
||||||
|
mut current_else: Option<BasicBlockId>,
|
||||||
|
args: impl Iterator<Item = (PlaceElem, PatId, Ty)>,
|
||||||
|
cond_place: &Place,
|
||||||
|
binding_mode: BindingAnnotation,
|
||||||
|
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
||||||
|
for (proj, arg, ty) in args {
|
||||||
|
let mut cond_place = cond_place.clone();
|
||||||
|
cond_place.projection.push(proj);
|
||||||
|
(current, current_else) =
|
||||||
|
self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?;
|
||||||
|
}
|
||||||
|
Ok((current, current_else))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pattern_match_tuple_like(
|
||||||
|
&mut self,
|
||||||
|
current: BasicBlockId,
|
||||||
|
current_else: Option<BasicBlockId>,
|
||||||
|
args: &[PatId],
|
||||||
|
ellipsis: Option<usize>,
|
||||||
|
fields: impl DoubleEndedIterator<Item = (PlaceElem, Ty)> + Clone,
|
||||||
|
cond_place: &Place,
|
||||||
|
binding_mode: BindingAnnotation,
|
||||||
|
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
||||||
|
let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
|
||||||
|
let it = al
|
||||||
|
.iter()
|
||||||
|
.zip(fields.clone())
|
||||||
|
.chain(ar.iter().rev().zip(fields.rev()))
|
||||||
|
.map(|(x, y)| (y.0, *x, y.1));
|
||||||
|
self.pattern_match_adt(current, current_else, it, cond_place, binding_mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pattern_matching_dereference(
|
||||||
|
cond_ty: &mut Ty,
|
||||||
|
binding_mode: &mut BindingAnnotation,
|
||||||
|
cond_place: &mut Place,
|
||||||
|
) {
|
||||||
|
while let Some((ty, _, mu)) = cond_ty.as_reference() {
|
||||||
|
if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
|
||||||
|
*binding_mode = BindingAnnotation::RefMut;
|
||||||
|
} else {
|
||||||
|
*binding_mode = BindingAnnotation::Ref;
|
||||||
|
}
|
||||||
|
*cond_ty = ty.clone();
|
||||||
|
cond_place.projection.push(ProjectionElem::Deref);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
//! A pretty-printer for MIR.
|
//! A pretty-printer for MIR.
|
||||||
|
|
||||||
use std::fmt::{Display, Write};
|
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;
|
||||||
|
@ -23,6 +23,18 @@ impl MirBody {
|
||||||
ctx.for_body();
|
ctx.for_body();
|
||||||
ctx.result
|
ctx.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
impl Debug for StringDbg {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StringDbg(self.pretty_print(db))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MirPrettyCtx<'a> {
|
struct MirPrettyCtx<'a> {
|
||||||
|
@ -77,6 +89,7 @@ impl Display for LocalName {
|
||||||
|
|
||||||
impl<'a> MirPrettyCtx<'a> {
|
impl<'a> MirPrettyCtx<'a> {
|
||||||
fn for_body(&mut self) {
|
fn for_body(&mut self) {
|
||||||
|
wln!(self, "// {:?}", self.body.owner);
|
||||||
self.with_block(|this| {
|
self.with_block(|this| {
|
||||||
this.locals();
|
this.locals();
|
||||||
wln!(this);
|
wln!(this);
|
||||||
|
@ -300,9 +313,9 @@ impl<'a> MirPrettyCtx<'a> {
|
||||||
w!(self, ")");
|
w!(self, ")");
|
||||||
}
|
}
|
||||||
Rvalue::Cast(ck, op, ty) => {
|
Rvalue::Cast(ck, op, ty) => {
|
||||||
w!(self, "Discriminant({ck:?}");
|
w!(self, "Cast({ck:?}, ");
|
||||||
self.operand(op);
|
self.operand(op);
|
||||||
w!(self, "{})", ty.display(self.db));
|
w!(self, ", {})", ty.display(self.db));
|
||||||
}
|
}
|
||||||
Rvalue::CheckedBinaryOp(b, o1, o2) => {
|
Rvalue::CheckedBinaryOp(b, o1, o2) => {
|
||||||
self.operand(o1);
|
self.operand(o1);
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -396,10 +395,40 @@ fn test() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn coerce_fn_item_to_fn_ptr_in_array() {
|
||||||
|
check_no_mismatches(
|
||||||
|
r"
|
||||||
|
fn foo(x: u32) -> isize { 1 }
|
||||||
|
fn bar(x: u32) -> isize { 1 }
|
||||||
|
fn test() {
|
||||||
|
let f = [foo, bar];
|
||||||
|
// ^^^ adjustments: Pointer(ReifyFnPointer)
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn coerce_fn_items_in_match_arms() {
|
fn coerce_fn_items_in_match_arms() {
|
||||||
cov_mark::check!(coerce_fn_reification);
|
cov_mark::check!(coerce_fn_reification);
|
||||||
|
|
||||||
|
check_no_mismatches(
|
||||||
|
r"
|
||||||
|
fn foo1(x: u32) -> isize { 1 }
|
||||||
|
fn foo2(x: u32) -> isize { 2 }
|
||||||
|
fn foo3(x: u32) -> isize { 3 }
|
||||||
|
fn test() {
|
||||||
|
let x = match 1 {
|
||||||
|
1 => foo1,
|
||||||
|
// ^^^^ adjustments: Pointer(ReifyFnPointer)
|
||||||
|
2 => foo2,
|
||||||
|
// ^^^^ adjustments: Pointer(ReifyFnPointer)
|
||||||
|
_ => foo3,
|
||||||
|
// ^^^^ adjustments: Pointer(ReifyFnPointer)
|
||||||
|
};
|
||||||
|
x;
|
||||||
|
}",
|
||||||
|
);
|
||||||
check_types(
|
check_types(
|
||||||
r"
|
r"
|
||||||
fn foo1(x: u32) -> isize { 1 }
|
fn foo1(x: u32) -> isize { 1 }
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2696,6 +2696,21 @@ fn f() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_ref_to_raw_cast() {
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let s = &mut S;
|
||||||
|
let s = s as *mut _;
|
||||||
|
//^ *mut S
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_missing_type() {
|
fn infer_missing_type() {
|
||||||
check_types(
|
check_types(
|
||||||
|
@ -3258,25 +3273,6 @@ fn f<T>(t: Ark<T>) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME
|
|
||||||
#[test]
|
|
||||||
fn castable_to2() {
|
|
||||||
check_infer(
|
|
||||||
r#"
|
|
||||||
fn func() {
|
|
||||||
let x = &0u32 as *const _;
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
expect![[r#"
|
|
||||||
10..44 '{ ...t _; }': ()
|
|
||||||
20..21 'x': *const {unknown}
|
|
||||||
24..29 '&0u32': &u32
|
|
||||||
24..41 '&0u32 ...onst _': *const {unknown}
|
|
||||||
25..29 '0u32': u32
|
|
||||||
"#]],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn issue_14275() {
|
fn issue_14275() {
|
||||||
// FIXME: evaluate const generic
|
// FIXME: evaluate const generic
|
||||||
|
|
|
@ -206,19 +206,27 @@ fn test() {
|
||||||
fn infer_try_trait() {
|
fn infer_try_trait() {
|
||||||
check_types(
|
check_types(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: try, result
|
//- minicore: try, result, from
|
||||||
fn test() {
|
fn test() {
|
||||||
let r: Result<i32, u64> = Result::Ok(1);
|
let r: Result<i32, u64> = Result::Ok(1);
|
||||||
let v = r?;
|
let v = r?;
|
||||||
v;
|
v;
|
||||||
} //^ i32
|
} //^ i32
|
||||||
|
"#,
|
||||||
impl<O, E> core::ops::Try for Result<O, E> {
|
);
|
||||||
type Output = O;
|
|
||||||
type Error = Result<core::convert::Infallible, E>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, E, F: From<E>> core::ops::FromResidual<Result<core::convert::Infallible, E>> for Result<T, F> {}
|
#[test]
|
||||||
|
fn infer_try_block() {
|
||||||
|
// FIXME: We should test more cases, but it currently doesn't work, since
|
||||||
|
// our labeled block type inference is broken.
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
//- minicore: try, option
|
||||||
|
fn test() {
|
||||||
|
let x: Option<_> = try { Some(2)?; };
|
||||||
|
//^ Option<()>
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ use hir_def::{
|
||||||
lang_item::{LangItem, LangItemTarget},
|
lang_item::{LangItem, LangItemTarget},
|
||||||
TraitId,
|
TraitId,
|
||||||
};
|
};
|
||||||
|
use hir_expand::name::{name, Name};
|
||||||
use stdx::panic_context;
|
use stdx::panic_context;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -187,7 +188,15 @@ impl FnTrait {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
|
pub fn method_name(self) -> Name {
|
||||||
|
match self {
|
||||||
|
FnTrait::FnOnce => name!(call_once),
|
||||||
|
FnTrait::FnMut => name!(call_mut),
|
||||||
|
FnTrait::Fn => name!(call),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_id(self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
|
||||||
let target = db.lang_item(krate, self.lang_item())?;
|
let target = db.lang_item(krate, self.lang_item())?;
|
||||||
match target {
|
match target {
|
||||||
LangItemTarget::Trait(t) => Some(t),
|
LangItemTarget::Trait(t) => Some(t),
|
||||||
|
|
|
@ -130,7 +130,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(Tra
|
||||||
WherePredicate::Lifetime { .. } => None,
|
WherePredicate::Lifetime { .. } => None,
|
||||||
})
|
})
|
||||||
.filter(|(_, bound_modifier)| matches!(bound_modifier, TraitBoundModifier::None))
|
.filter(|(_, bound_modifier)| matches!(bound_modifier, TraitBoundModifier::None))
|
||||||
.filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) {
|
.filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path) {
|
||||||
Some(TypeNs::TraitId(t)) => Some(t),
|
Some(TypeNs::TraitId(t)) => Some(t),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1801,7 +1801,7 @@ impl Function {
|
||||||
let body = db
|
let body = db
|
||||||
.mir_body(self.id.into())
|
.mir_body(self.id.into())
|
||||||
.map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
|
.map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
|
||||||
interpret_mir(db, &body, false)?;
|
interpret_mir(db, &body, Substitution::empty(Interner), false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1947,7 +1947,7 @@ impl Const {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
|
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
|
||||||
let c = db.const_eval(self.id)?;
|
let c = db.const_eval(self.id, Substitution::empty(Interner))?;
|
||||||
let r = format!("{}", HexifiedConst(c).display(db));
|
let r = format!("{}", HexifiedConst(c).display(db));
|
||||||
// We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
|
// We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
|
||||||
// implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to
|
// implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to
|
||||||
|
|
|
@ -1076,10 +1076,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id);
|
let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id);
|
||||||
let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
|
let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
|
||||||
let hir_path = Path::from_src(path.clone(), &ctx)?;
|
let hir_path = Path::from_src(path.clone(), &ctx)?;
|
||||||
match analyze
|
match analyze.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), &hir_path)? {
|
||||||
.resolver
|
|
||||||
.resolve_path_in_type_ns_fully(self.db.upcast(), hir_path.mod_path())?
|
|
||||||
{
|
|
||||||
TypeNs::TraitId(id) => Some(Trait { id }),
|
TypeNs::TraitId(id) => Some(Trait { id }),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -420,7 +420,10 @@ impl SourceAnalyzer {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
// Shorthand syntax, resolve to the local
|
// Shorthand syntax, resolve to the local
|
||||||
let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone()));
|
let path = Path::from_known_path_with_no_generic(ModPath::from_segments(
|
||||||
|
PathKind::Plain,
|
||||||
|
once(local_name.clone()),
|
||||||
|
));
|
||||||
match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
|
match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
|
||||||
Some(ValueNs::LocalBinding(binding_id)) => {
|
Some(ValueNs::LocalBinding(binding_id)) => {
|
||||||
Some(Local { binding_id, parent: self.resolver.body_owner()? })
|
Some(Local { binding_id, parent: self.resolver.body_owner()? })
|
||||||
|
@ -461,7 +464,7 @@ impl SourceAnalyzer {
|
||||||
) -> Option<Macro> {
|
) -> Option<Macro> {
|
||||||
let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id);
|
let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id);
|
||||||
let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
|
let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
|
||||||
self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into())
|
self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()?).map(|it| it.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_bind_pat_to_const(
|
pub(crate) fn resolve_bind_pat_to_const(
|
||||||
|
@ -801,15 +804,11 @@ impl SourceAnalyzer {
|
||||||
func: FunctionId,
|
func: FunctionId,
|
||||||
substs: Substitution,
|
substs: Substitution,
|
||||||
) -> FunctionId {
|
) -> FunctionId {
|
||||||
let krate = self.resolver.krate();
|
|
||||||
let owner = match self.resolver.body_owner() {
|
let owner = match self.resolver.body_owner() {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return func,
|
None => return func,
|
||||||
};
|
};
|
||||||
let env = owner.as_generic_def_id().map_or_else(
|
let env = db.trait_environment_for_body(owner);
|
||||||
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
|
|
||||||
|d| db.trait_environment(d),
|
|
||||||
);
|
|
||||||
method_resolution::lookup_impl_method(db, env, func, substs).0
|
method_resolution::lookup_impl_method(db, env, func, substs).0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -819,15 +818,11 @@ impl SourceAnalyzer {
|
||||||
const_id: ConstId,
|
const_id: ConstId,
|
||||||
subs: Substitution,
|
subs: Substitution,
|
||||||
) -> ConstId {
|
) -> ConstId {
|
||||||
let krate = self.resolver.krate();
|
|
||||||
let owner = match self.resolver.body_owner() {
|
let owner = match self.resolver.body_owner() {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return const_id,
|
None => return const_id,
|
||||||
};
|
};
|
||||||
let env = owner.as_generic_def_id().map_or_else(
|
let env = db.trait_environment_for_body(owner);
|
||||||
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
|
|
||||||
|d| db.trait_environment(d),
|
|
||||||
);
|
|
||||||
method_resolution::lookup_impl_const(db, env, const_id, subs).0
|
method_resolution::lookup_impl_const(db, env, const_id, subs).0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -946,7 +941,7 @@ pub(crate) fn resolve_hir_path_as_macro(
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Option<Macro> {
|
) -> Option<Macro> {
|
||||||
resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(Into::into)
|
resolver.resolve_path_as_macro(db.upcast(), path.mod_path()?).map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_hir_path_(
|
fn resolve_hir_path_(
|
||||||
|
@ -962,8 +957,7 @@ fn resolve_hir_path_(
|
||||||
res.map(|ty_ns| (ty_ns, path.segments().first()))
|
res.map(|ty_ns| (ty_ns, path.segments().first()))
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let (ty, remaining_idx) =
|
let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
|
||||||
resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?;
|
|
||||||
match remaining_idx {
|
match remaining_idx {
|
||||||
Some(remaining_idx) => {
|
Some(remaining_idx) => {
|
||||||
if remaining_idx + 1 == path.segments().len() {
|
if remaining_idx + 1 == path.segments().len() {
|
||||||
|
@ -1019,7 +1013,7 @@ fn resolve_hir_path_(
|
||||||
|
|
||||||
let body_owner = resolver.body_owner();
|
let body_owner = resolver.body_owner();
|
||||||
let values = || {
|
let values = || {
|
||||||
resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| {
|
resolver.resolve_path_in_value_ns_fully(db.upcast(), path).and_then(|val| {
|
||||||
let res = match val {
|
let res = match val {
|
||||||
ValueNs::LocalBinding(binding_id) => {
|
ValueNs::LocalBinding(binding_id) => {
|
||||||
let var = Local { parent: body_owner?, binding_id };
|
let var = Local { parent: body_owner?, binding_id };
|
||||||
|
@ -1039,14 +1033,14 @@ fn resolve_hir_path_(
|
||||||
|
|
||||||
let items = || {
|
let items = || {
|
||||||
resolver
|
resolver
|
||||||
.resolve_module_path_in_items(db.upcast(), path.mod_path())
|
.resolve_module_path_in_items(db.upcast(), path.mod_path()?)
|
||||||
.take_types()
|
.take_types()
|
||||||
.map(|it| PathResolution::Def(it.into()))
|
.map(|it| PathResolution::Def(it.into()))
|
||||||
};
|
};
|
||||||
|
|
||||||
let macros = || {
|
let macros = || {
|
||||||
resolver
|
resolver
|
||||||
.resolve_path_as_macro(db.upcast(), path.mod_path())
|
.resolve_path_as_macro(db.upcast(), path.mod_path()?)
|
||||||
.map(|def| PathResolution::Def(ModuleDef::Macro(def.into())))
|
.map(|def| PathResolution::Def(ModuleDef::Macro(def.into())))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1074,7 +1068,7 @@ fn resolve_hir_path_qualifier(
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Option<PathResolution> {
|
) -> Option<PathResolution> {
|
||||||
resolver
|
resolver
|
||||||
.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path())
|
.resolve_path_in_type_ns_fully(db.upcast(), &path)
|
||||||
.map(|ty| match ty {
|
.map(|ty| match ty {
|
||||||
TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
|
TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
|
||||||
TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()),
|
TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()),
|
||||||
|
@ -1089,7 +1083,7 @@ fn resolve_hir_path_qualifier(
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
resolver
|
resolver
|
||||||
.resolve_module_path_in_items(db.upcast(), path.mod_path())
|
.resolve_module_path_in_items(db.upcast(), path.mod_path()?)
|
||||||
.take_types()
|
.take_types()
|
||||||
.map(|it| PathResolution::Def(it.into()))
|
.map(|it| PathResolution::Def(it.into()))
|
||||||
})
|
})
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::assist_context::{AssistContext, Assists};
|
||||||
// Replaces a `try` expression with a `match` expression.
|
// Replaces a `try` expression with a `match` expression.
|
||||||
//
|
//
|
||||||
// ```
|
// ```
|
||||||
// # //- minicore:option
|
// # //- minicore: try, option
|
||||||
// fn handle() {
|
// fn handle() {
|
||||||
// let pat = Some(true)$0?;
|
// let pat = Some(true)$0?;
|
||||||
// }
|
// }
|
||||||
|
@ -111,7 +111,7 @@ mod tests {
|
||||||
check_assist(
|
check_assist(
|
||||||
replace_try_expr_with_match,
|
replace_try_expr_with_match,
|
||||||
r#"
|
r#"
|
||||||
//- minicore:option
|
//- minicore: try, option
|
||||||
fn test() {
|
fn test() {
|
||||||
let pat = Some(true)$0?;
|
let pat = Some(true)$0?;
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ fn test() {
|
||||||
check_assist(
|
check_assist(
|
||||||
replace_try_expr_with_match,
|
replace_try_expr_with_match,
|
||||||
r#"
|
r#"
|
||||||
//- minicore:result
|
//- minicore: try, from, result
|
||||||
fn test() {
|
fn test() {
|
||||||
let pat = Ok(true)$0?;
|
let pat = Ok(true)$0?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2352,7 +2352,7 @@ fn doctest_replace_try_expr_with_match() {
|
||||||
check_doc_test(
|
check_doc_test(
|
||||||
"replace_try_expr_with_match",
|
"replace_try_expr_with_match",
|
||||||
r#####"
|
r#####"
|
||||||
//- minicore:option
|
//- minicore: try, option
|
||||||
fn handle() {
|
fn handle() {
|
||||||
let pat = Some(true)$0?;
|
let pat = Some(true)$0?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,9 +564,56 @@ fn f(x: [(i32, u8); 10]) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn overloaded_index() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: index
|
||||||
|
use core::ops::{Index, IndexMut};
|
||||||
|
|
||||||
|
struct Foo;
|
||||||
|
impl Index<usize> for Foo {
|
||||||
|
type Output = (i32, u8);
|
||||||
|
fn index(&self, index: usize) -> &(i32, u8) {
|
||||||
|
&(5, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl IndexMut<usize> for Foo {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut (i32, u8) {
|
||||||
|
&mut (5, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn f() {
|
||||||
|
let mut x = Foo;
|
||||||
|
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||||
|
let y = &x[2];
|
||||||
|
let x = Foo;
|
||||||
|
let y = &mut x[2];
|
||||||
|
//^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||||
|
let mut x = &mut Foo;
|
||||||
|
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||||
|
let y: &mut (i32, u8) = &mut x[2];
|
||||||
|
let x = Foo;
|
||||||
|
let ref mut y = x[7];
|
||||||
|
//^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||||
|
let (ref mut y, _) = x[3];
|
||||||
|
//^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||||
|
match x[10] {
|
||||||
|
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||||
|
(ref y, _) => (),
|
||||||
|
(_, ref mut y) => (),
|
||||||
|
}
|
||||||
|
let mut x = Foo;
|
||||||
|
let mut i = 5;
|
||||||
|
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||||
|
let y = &mut x[i];
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[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 +621,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) => (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -631,6 +692,31 @@ fn f(inp: (Foo, Foo, Foo, Foo)) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fn_traits() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: fn
|
||||||
|
fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 {
|
||||||
|
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||||
|
x(2)
|
||||||
|
}
|
||||||
|
fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 {
|
||||||
|
x(2)
|
||||||
|
//^ 💡 error: cannot mutate immutable variable `x`
|
||||||
|
}
|
||||||
|
fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 {
|
||||||
|
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||||
|
x(2)
|
||||||
|
}
|
||||||
|
fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
|
||||||
|
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||||
|
x(2)
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn respect_allow_unused_mut() {
|
fn respect_allow_unused_mut() {
|
||||||
// FIXME: respect
|
// FIXME: respect
|
||||||
|
|
|
@ -409,7 +409,6 @@ pub(super) fn definition(
|
||||||
}
|
}
|
||||||
match it.eval(db) {
|
match it.eval(db) {
|
||||||
Ok(()) => Some("pass".into()),
|
Ok(()) => Some("pass".into()),
|
||||||
Err(MirEvalError::Panic) => Some("fail".into()),
|
|
||||||
Err(MirEvalError::MirLowerError(f, e)) => {
|
Err(MirEvalError::MirLowerError(f, e)) => {
|
||||||
let name = &db.function_data(f).name;
|
let name = &db.function_data(f).name;
|
||||||
Some(format!("error: fail to lower {name} due {e:?}"))
|
Some(format!("error: fail to lower {name} due {e:?}"))
|
||||||
|
|
|
@ -5009,7 +5009,7 @@ fn foo() {
|
||||||
fn hover_try_expr_res() {
|
fn hover_try_expr_res() {
|
||||||
check_hover_range(
|
check_hover_range(
|
||||||
r#"
|
r#"
|
||||||
//- minicore:result
|
//- minicore: try, from, result
|
||||||
struct FooError;
|
struct FooError;
|
||||||
|
|
||||||
fn foo() -> Result<(), FooError> {
|
fn foo() -> Result<(), FooError> {
|
||||||
|
@ -5023,7 +5023,7 @@ fn foo() -> Result<(), FooError> {
|
||||||
);
|
);
|
||||||
check_hover_range(
|
check_hover_range(
|
||||||
r#"
|
r#"
|
||||||
//- minicore:result
|
//- minicore: try, from, result
|
||||||
struct FooError;
|
struct FooError;
|
||||||
struct BarError;
|
struct BarError;
|
||||||
|
|
||||||
|
@ -5044,6 +5044,7 @@ fn foo() -> Result<(), FooError> {
|
||||||
fn hover_try_expr() {
|
fn hover_try_expr() {
|
||||||
check_hover_range(
|
check_hover_range(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: try
|
||||||
struct NotResult<T, U>(T, U);
|
struct NotResult<T, U>(T, U);
|
||||||
struct Short;
|
struct Short;
|
||||||
struct Looooong;
|
struct Looooong;
|
||||||
|
@ -5061,6 +5062,7 @@ fn foo() -> NotResult<(), Looooong> {
|
||||||
);
|
);
|
||||||
check_hover_range(
|
check_hover_range(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: try
|
||||||
struct NotResult<T, U>(T, U);
|
struct NotResult<T, U>(T, U);
|
||||||
struct Short;
|
struct Short;
|
||||||
struct Looooong;
|
struct Looooong;
|
||||||
|
@ -5092,7 +5094,7 @@ fn foo() -> Option<()> {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
```rust
|
```rust
|
||||||
<Option<i32> as Try>::Output
|
i32
|
||||||
```"#]],
|
```"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
//! errors.
|
//! errors.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
env,
|
env,
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
@ -153,6 +154,10 @@ impl flags::AnalysisStats {
|
||||||
self.run_inference(&host, db, &vfs, &funcs, verbosity);
|
self.run_inference(&host, db, &vfs, &funcs, verbosity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.mir_stats {
|
||||||
|
self.lower_mir(db, &funcs);
|
||||||
|
}
|
||||||
|
|
||||||
let total_span = analysis_sw.elapsed();
|
let total_span = analysis_sw.elapsed();
|
||||||
eprintln!("{:<20} {total_span}", "Total:");
|
eprintln!("{:<20} {total_span}", "Total:");
|
||||||
report_metric("total time", total_span.time.as_millis() as u64, "ms");
|
report_metric("total time", total_span.time.as_millis() as u64, "ms");
|
||||||
|
@ -189,6 +194,24 @@ impl flags::AnalysisStats {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lower_mir(&self, db: &RootDatabase, funcs: &[Function]) {
|
||||||
|
let all = funcs.len();
|
||||||
|
let mut fail = 0;
|
||||||
|
let mut h: HashMap<String, usize> = HashMap::new();
|
||||||
|
for f in funcs {
|
||||||
|
let f = FunctionId::from(*f);
|
||||||
|
let Err(e) = db.mir_body(f.into()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let es = format!("{:?}", e);
|
||||||
|
*h.entry(es).or_default() += 1;
|
||||||
|
fail += 1;
|
||||||
|
}
|
||||||
|
let h = h.into_iter().sorted_by_key(|x| x.1).collect::<Vec<_>>();
|
||||||
|
eprintln!("Mir failed reasons: {:#?}", h);
|
||||||
|
eprintln!("Mir failed bodies: {fail} ({}%)", fail * 100 / all);
|
||||||
|
}
|
||||||
|
|
||||||
fn run_inference(
|
fn run_inference(
|
||||||
&self,
|
&self,
|
||||||
host: &AnalysisHost,
|
host: &AnalysisHost,
|
||||||
|
|
|
@ -66,6 +66,8 @@ xflags::xflags! {
|
||||||
optional --memory-usage
|
optional --memory-usage
|
||||||
/// Print the total length of all source and macro files (whitespace is not counted).
|
/// Print the total length of all source and macro files (whitespace is not counted).
|
||||||
optional --source-stats
|
optional --source-stats
|
||||||
|
/// Print the number of bodies that fail to lower to mir, in addition to failed reasons.
|
||||||
|
optional --mir-stats
|
||||||
|
|
||||||
/// Only analyze items matching this path.
|
/// Only analyze items matching this path.
|
||||||
optional -o, --only path: String
|
optional -o, --only path: String
|
||||||
|
@ -172,6 +174,7 @@ pub struct AnalysisStats {
|
||||||
pub parallel: bool,
|
pub parallel: bool,
|
||||||
pub memory_usage: bool,
|
pub memory_usage: bool,
|
||||||
pub source_stats: bool,
|
pub source_stats: bool,
|
||||||
|
pub mir_stats: bool,
|
||||||
pub only: Option<String>,
|
pub only: Option<String>,
|
||||||
pub with_deps: bool,
|
pub with_deps: bool,
|
||||||
pub no_sysroot: bool,
|
pub no_sysroot: bool,
|
||||||
|
|
|
@ -613,7 +613,7 @@ Pat =
|
||||||
| ConstBlockPat
|
| ConstBlockPat
|
||||||
|
|
||||||
LiteralPat =
|
LiteralPat =
|
||||||
Literal
|
'-'? Literal
|
||||||
|
|
||||||
IdentPat =
|
IdentPat =
|
||||||
Attr* 'ref'? 'mut'? Name ('@' Pat)?
|
Attr* 'ref'? 'mut'? Name ('@' Pat)?
|
||||||
|
|
|
@ -1375,6 +1375,7 @@ pub struct LiteralPat {
|
||||||
pub(crate) syntax: SyntaxNode,
|
pub(crate) syntax: SyntaxNode,
|
||||||
}
|
}
|
||||||
impl LiteralPat {
|
impl LiteralPat {
|
||||||
|
pub fn minus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![-]) }
|
||||||
pub fn literal(&self) -> Option<Literal> { support::child(&self.syntax) }
|
pub fn literal(&self) -> Option<Literal> { support::child(&self.syntax) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -535,6 +535,7 @@ impl Field {
|
||||||
"!" => "excl",
|
"!" => "excl",
|
||||||
"*" => "star",
|
"*" => "star",
|
||||||
"&" => "amp",
|
"&" => "amp",
|
||||||
|
"-" => "minus",
|
||||||
"_" => "underscore",
|
"_" => "underscore",
|
||||||
"." => "dot",
|
"." => "dot",
|
||||||
".." => "dotdot",
|
".." => "dotdot",
|
||||||
|
|
|
@ -181,7 +181,7 @@ pub mod convert {
|
||||||
}
|
}
|
||||||
// endregion:as_ref
|
// endregion:as_ref
|
||||||
// region:infallible
|
// region:infallible
|
||||||
pub enum Infallibe {}
|
pub enum Infallible {}
|
||||||
// endregion:infallible
|
// endregion:infallible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,16 +375,82 @@ 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
|
||||||
// region:try
|
// region:try
|
||||||
mod try_ {
|
mod try_ {
|
||||||
|
use super::super::convert::Infallible;
|
||||||
|
|
||||||
pub enum ControlFlow<B, C = ()> {
|
pub enum ControlFlow<B, C = ()> {
|
||||||
|
#[lang = "Continue"]
|
||||||
Continue(C),
|
Continue(C),
|
||||||
|
#[lang = "Break"]
|
||||||
Break(B),
|
Break(B),
|
||||||
}
|
}
|
||||||
pub trait FromResidual<R = Self::Residual> {
|
pub trait FromResidual<R = <Self as Try>::Residual> {
|
||||||
#[lang = "from_residual"]
|
#[lang = "from_residual"]
|
||||||
fn from_residual(residual: R) -> Self;
|
fn from_residual(residual: R) -> Self;
|
||||||
}
|
}
|
||||||
|
@ -400,14 +466,66 @@ pub mod ops {
|
||||||
|
|
||||||
impl<B, C> Try for ControlFlow<B, C> {
|
impl<B, C> Try for ControlFlow<B, C> {
|
||||||
type Output = C;
|
type Output = C;
|
||||||
type Residual = ControlFlow<B, convert::Infallible>;
|
type Residual = ControlFlow<B, Infallible>;
|
||||||
fn from_output(output: Self::Output) -> Self {}
|
fn from_output(output: Self::Output) -> Self {}
|
||||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {}
|
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B, C> FromResidual for ControlFlow<B, C> {
|
impl<B, C> FromResidual for ControlFlow<B, C> {
|
||||||
fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {}
|
fn from_residual(residual: ControlFlow<B, Infallible>) -> Self {}
|
||||||
}
|
}
|
||||||
|
// region:option
|
||||||
|
impl<T> Try for Option<T> {
|
||||||
|
type Output = T;
|
||||||
|
type Residual = Option<Infallible>;
|
||||||
|
fn from_output(output: Self::Output) -> Self {
|
||||||
|
Some(output)
|
||||||
|
}
|
||||||
|
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||||
|
match self {
|
||||||
|
Some(x) => ControlFlow::Continue(x),
|
||||||
|
None => ControlFlow::Break(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FromResidual for Option<T> {
|
||||||
|
fn from_residual(x: Option<Infallible>) -> Self {
|
||||||
|
match x {
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion:option
|
||||||
|
// region:result
|
||||||
|
// region:from
|
||||||
|
use super::super::convert::From;
|
||||||
|
|
||||||
|
impl<T, E> Try for Result<T, E> {
|
||||||
|
type Output = T;
|
||||||
|
type Residual = Result<Infallible, E>;
|
||||||
|
|
||||||
|
fn from_output(output: Self::Output) -> Self {
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||||
|
match self {
|
||||||
|
Ok(v) => ControlFlow::Continue(v),
|
||||||
|
Err(e) => ControlFlow::Break(Err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {
|
||||||
|
fn from_residual(residual: Result<Infallible, E>) -> Self {
|
||||||
|
match residual {
|
||||||
|
Err(e) => Err(From::from(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion:from
|
||||||
|
// endregion:result
|
||||||
}
|
}
|
||||||
pub use self::try_::{ControlFlow, FromResidual, Try};
|
pub use self::try_::{ControlFlow, FromResidual, Try};
|
||||||
// endregion:try
|
// endregion:try
|
||||||
|
@ -541,7 +659,10 @@ pub mod option {
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
pub fn unwrap_or(self, default: T) -> T {
|
pub fn unwrap_or(self, default: T) -> T {
|
||||||
loop {}
|
match self {
|
||||||
|
Some(val) => val,
|
||||||
|
None => default,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// region:fn
|
// region:fn
|
||||||
pub fn and_then<U, F>(self, f: F) -> Option<U>
|
pub fn and_then<U, F>(self, f: F) -> Option<U>
|
||||||
|
|
|
@ -295,7 +295,7 @@ impl<T> Arena<T> {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn iter(
|
pub fn iter(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator {
|
) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator + Clone {
|
||||||
self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawIdx(idx as u32)), value))
|
self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawIdx(idx as u32)), value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue