Unnamed repository; edit this file 'description' to name the repository.
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.
bors 2023-03-22
parent 6d75119 · parent 8a3ad7c · commit 3321799
-rw-r--r--crates/hir-def/src/body.rs5
-rw-r--r--crates/hir-def/src/body/lower.rs179
-rw-r--r--crates/hir-def/src/body/pretty.rs7
-rw-r--r--crates/hir-def/src/body/scope.rs3
-rw-r--r--crates/hir-def/src/expr.rs20
-rw-r--r--crates/hir-def/src/lang_item.rs21
-rw-r--r--crates/hir-def/src/path.rs83
-rw-r--r--crates/hir-def/src/path/lower.rs9
-rw-r--r--crates/hir-def/src/resolver.rs48
-rw-r--r--crates/hir-expand/src/name.rs16
-rw-r--r--crates/hir-ty/src/consteval.rs18
-rw-r--r--crates/hir-ty/src/consteval/tests.rs586
-rw-r--r--crates/hir-ty/src/consteval/tests/intrinsics.rs162
-rw-r--r--crates/hir-ty/src/db.rs6
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs2
-rw-r--r--crates/hir-ty/src/infer.rs56
-rw-r--r--crates/hir-ty/src/infer/coerce.rs17
-rw-r--r--crates/hir-ty/src/infer/expr.rs112
-rw-r--r--crates/hir-ty/src/infer/mutability.rs196
-rw-r--r--crates/hir-ty/src/infer/path.rs2
-rw-r--r--crates/hir-ty/src/infer/unify.rs41
-rw-r--r--crates/hir-ty/src/lib.rs6
-rw-r--r--crates/hir-ty/src/lower.rs88
-rw-r--r--crates/hir-ty/src/method_resolution.rs48
-rw-r--r--crates/hir-ty/src/mir/eval.rs751
-rw-r--r--crates/hir-ty/src/mir/lower.rs598
-rw-r--r--crates/hir-ty/src/mir/lower/as_place.rs91
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs399
-rw-r--r--crates/hir-ty/src/mir/pretty.rs19
-rw-r--r--crates/hir-ty/src/tests/coercion.rs33
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs3
-rw-r--r--crates/hir-ty/src/tests/simple.rs34
-rw-r--r--crates/hir-ty/src/tests/traits.rs20
-rw-r--r--crates/hir-ty/src/traits.rs11
-rw-r--r--crates/hir-ty/src/utils.rs2
-rw-r--r--crates/hir/src/lib.rs4
-rw-r--r--crates/hir/src/semantics.rs5
-rw-r--r--crates/hir/src/source_analyzer.rs34
-rw-r--r--crates/ide-assists/src/handlers/replace_try_expr_with_match.rs6
-rw-r--r--crates/ide-assists/src/tests/generated.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/mutability_errors.rs104
-rw-r--r--crates/ide/src/hover/render.rs1
-rw-r--r--crates/ide/src/hover/tests.rs8
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs12
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs23
-rw-r--r--crates/rust-analyzer/src/cli/flags.rs3
-rw-r--r--crates/syntax/rust.ungram2
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs1
-rw-r--r--crates/syntax/src/tests/sourcegen_ast.rs1
-rw-r--r--crates/test-utils/src/minicore.rs131
-rw-r--r--lib/la-arena/src/lib.rs2
52 files changed, 3098 insertions, 935 deletions
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index b70e658efd..7f9b9476dc 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/body.rs
@@ -391,7 +391,7 @@ impl Body {
}
};
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();
(Arc::new(body), Arc::new(source_map))
@@ -420,8 +420,9 @@ impl Body {
expander: Expander,
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
+ krate: CrateId,
) -> (Body, BodySourceMap) {
- lower::lower(db, expander, params, body)
+ lower::lower(db, expander, params, body, krate)
}
fn shrink_to_fit(&mut self) {
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index fedaf39559..a93fcb3b1d 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -3,6 +3,7 @@
use std::{mem, sync::Arc};
+use base_db::CrateId;
use either::Either;
use hir_expand::{
ast_id_map::AstIdMap,
@@ -18,7 +19,7 @@ use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use syntax::{
ast::{
- self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind,
+ self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName, LiteralKind,
SlicePatComponents,
},
AstNode, AstPtr, SyntaxNodePtr,
@@ -36,6 +37,7 @@ use crate::{
RecordFieldPat, RecordLitField, Statement,
},
item_scope::BuiltinShadowMode,
+ lang_item::LangItem,
path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef},
AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
@@ -80,9 +82,11 @@ pub(super) fn lower(
expander: Expander,
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
+ krate: CrateId,
) -> (Body, BodySourceMap) {
ExprCollector {
db,
+ krate,
source_map: BodySourceMap::default(),
ast_id_map: db.ast_id_map(expander.current_file_id),
body: Body {
@@ -96,6 +100,7 @@ pub(super) fn lower(
_c: Count::new(),
},
expander,
+ current_try_block: None,
is_lowering_assignee_expr: false,
is_lowering_generator: false,
}
@@ -107,7 +112,9 @@ struct ExprCollector<'a> {
expander: Expander,
ast_id_map: Arc<AstIdMap>,
body: Body,
+ krate: CrateId,
source_map: BodySourceMap,
+ current_try_block: Option<LabelId>,
is_lowering_assignee_expr: bool,
is_lowering_generator: bool,
}
@@ -176,8 +183,7 @@ impl ExprCollector<'_> {
self.source_map.expr_map.insert(src, id);
id
}
- // desugared exprs don't have ptr, that's wrong and should be fixed
- // somehow.
+ // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
self.body.exprs.alloc(expr)
}
@@ -199,6 +205,10 @@ impl ExprCollector<'_> {
self.source_map.pat_map.insert(src, 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 {
self.body.pats.alloc(Pat::Missing)
}
@@ -214,6 +224,10 @@ impl ExprCollector<'_> {
self.source_map.label_map.insert(src, 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 {
let id = self.body.labels.alloc(label);
self.source_map.label_map_back.insert(id, src);
@@ -251,13 +265,7 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
}
ast::Expr::BlockExpr(e) => match e.modifier() {
- Some(ast::BlockModifier::Try(_)) => {
- self.collect_block_(e, |id, statements, tail| Expr::TryBlock {
- id,
- statements,
- tail,
- })
- }
+ Some(ast::BlockModifier::Try(_)) => self.collect_try_block(e),
Some(ast::BlockModifier::Unsafe(_)) => {
self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
id,
@@ -437,10 +445,7 @@ impl ExprCollector<'_> {
let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
}
- ast::Expr::TryExpr(e) => {
- let expr = self.collect_expr_opt(e.expr());
- self.alloc_expr(Expr::Try { expr }, syntax_ptr)
- }
+ ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e),
ast::Expr::CastExpr(e) => {
let expr = self.collect_expr_opt(e.expr());
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>(
&mut self,
mcall: ast::MacroCall,
@@ -949,16 +1074,24 @@ impl ExprCollector<'_> {
.collect(),
}
}
- ast::Pat::LiteralPat(lit) => {
- if let Some(ast_lit) = lit.literal() {
- let expr = Expr::Literal(ast_lit.kind().into());
+ // FIXME: rustfmt removes this label if it is a block and not a loop
+ ast::Pat::LiteralPat(lit) => 'b: loop {
+ break if let Some(ast_lit) = lit.literal() {
+ let mut hir_lit: Literal = ast_lit.kind().into();
+ if lit.minus_token().is_some() {
+ let Some(h) = hir_lit.negate() else {
+ 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_id = self.alloc_expr(expr, expr_ptr);
Pat::Lit(expr_id)
} else {
Pat::Missing
- }
- }
+ };
+ },
ast::Pat::RestPat(_) => {
// `RestPat` requires special handling and should not be mapped
// to a Pat. Here we are using `Pat::Missing` as a fallback for
@@ -1063,11 +1196,11 @@ impl From<ast::LiteralKind> for Literal {
FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
builtin,
)
- } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinInt::from_suffix) {
- Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
- } else {
- let builtin = lit.suffix().and_then(BuiltinUint::from_suffix);
+ } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
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) => {
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs
index 5a9b825a25..8c9d77620e 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/body/pretty.rs
@@ -288,10 +288,6 @@ impl<'a> Printer<'a> {
self.print_expr(*expr);
w!(self, ".await");
}
- Expr::Try { expr } => {
- self.print_expr(*expr);
- w!(self, "?");
- }
Expr::Cast { expr, type_ref } => {
self.print_expr(*expr);
w!(self, " as ");
@@ -424,9 +420,6 @@ impl<'a> Printer<'a> {
Expr::Unsafe { id: _, 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 } => {
self.print_block(Some("async "), statements, tail);
}
diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs
index 12fc1f116d..8ddb89a472 100644
--- a/crates/hir-def/src/body/scope.rs
+++ b/crates/hir-def/src/body/scope.rs
@@ -202,8 +202,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
}
Expr::Unsafe { id, statements, tail }
| Expr::Async { id, statements, tail }
- | Expr::Const { id, statements, tail }
- | Expr::TryBlock { id, statements, tail } => {
+ | Expr::Const { id, statements, tail } => {
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
// via the block itself (important for blocks that only contain items, no expressions).
diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs
index 19fa6b2541..7ede19cc3c 100644
--- a/crates/hir-def/src/expr.rs
+++ b/crates/hir-def/src/expr.rs
@@ -92,6 +92,16 @@ pub enum Literal {
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)]
pub enum Expr {
/// This is produced if the syntax tree does not have a required expression piece.
@@ -112,11 +122,6 @@ pub enum Expr {
tail: Option<ExprId>,
label: Option<LabelId>,
},
- TryBlock {
- id: BlockId,
- statements: Box<[Statement]>,
- tail: Option<ExprId>,
- },
Async {
id: BlockId,
statements: Box<[Statement]>,
@@ -192,9 +197,6 @@ pub enum Expr {
Await {
expr: ExprId,
},
- Try {
- expr: ExprId,
- },
Cast {
expr: ExprId,
type_ref: Interned<TypeRef>,
@@ -303,7 +305,6 @@ impl Expr {
f(*expr);
}
Expr::Block { statements, tail, .. }
- | Expr::TryBlock { statements, tail, .. }
| Expr::Unsafe { statements, tail, .. }
| Expr::Async { statements, tail, .. }
| Expr::Const { statements, tail, .. } => {
@@ -383,7 +384,6 @@ impl Expr {
}
Expr::Field { expr, .. }
| Expr::Await { expr }
- | Expr::Try { expr }
| Expr::Cast { expr, .. }
| Expr::Ref { expr, .. }
| Expr::UnaryOp { expr, .. }
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index 4096e0a382..818054188b 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -8,8 +8,8 @@ use rustc_hash::FxHashMap;
use syntax::SmolStr;
use crate::{
- db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId,
- ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
+ db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
+ FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -221,11 +221,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`]
pub fn from_str(name: &str) -> Option<Self> {
match name {
$( stringify!($name) => Some(LangItem::$variant), )*
@@ -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! {
// Variant name, Name, Getter method name, Target Generic requirements;
Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs
index f3197d1800..c67c29818f 100644
--- a/crates/hir-def/src/path.rs
+++ b/crates/hir-def/src/path.rs
@@ -8,6 +8,7 @@ use std::{
use crate::{
body::LowerCtx,
+ lang_item::LangItemTarget,
type_ref::{ConstRefOrPath, LifetimeRef},
};
use hir_expand::name::Name;
@@ -36,13 +37,19 @@ impl Display for ImportAlias {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Path {
- /// Type based path like `<T>::foo`.
- /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
- type_anchor: Option<Interned<TypeRef>>,
- 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>>]>>,
+pub enum Path {
+ /// A normal path
+ Normal {
+ /// Type based path like `<T>::foo`.
+ /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
+ type_anchor: Option<Interned<TypeRef>>,
+ 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
@@ -102,51 +109,77 @@ impl Path {
) -> Path {
let generic_args = generic_args.into();
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 {
- &self.mod_path.kind
+ match self {
+ Path::Normal { mod_path, .. } => &mod_path.kind,
+ Path::LangItem(_) => &PathKind::Abs,
+ }
}
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<'_> {
- let s = PathSegments {
- segments: self.mod_path.segments(),
- generic_args: self.generic_args.as_deref(),
+ let Path::Normal { mod_path, generic_args, .. } = self else {
+ return PathSegments {
+ 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 {
assert_eq!(s.segments.len(), generic_args.len());
}
s
}
- pub fn mod_path(&self) -> &ModPath {
- &self.mod_path
+ pub fn mod_path(&self) -> Option<&ModPath> {
+ match self {
+ Path::Normal { mod_path, .. } => Some(&mod_path),
+ Path::LangItem(_) => None,
+ }
}
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;
}
- let res = Path {
- type_anchor: self.type_anchor.clone(),
+ let res = Path::Normal {
+ type_anchor: type_anchor.clone(),
mod_path: Interned::new(ModPath::from_segments(
- self.mod_path.kind,
- self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(),
+ mod_path.kind,
+ 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)
}
pub fn is_self_type(&self) -> bool {
- self.type_anchor.is_none()
- && self.generic_args.as_deref().is_none()
- && self.mod_path.is_Self()
+ let Path::Normal { mod_path, generic_args, type_anchor } = self else {
+ return false;
+ };
+ 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 {
fn from(name: Name) -> Path {
- Path {
+ Path::Normal {
type_anchor: None,
mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
generic_args: None,
diff --git a/crates/hir-def/src/path/lower.rs b/crates/hir-def/src/path/lower.rs
index b7542bd777..407f38daad 100644
--- a/crates/hir-def/src/path/lower.rs
+++ b/crates/hir-def/src/path/lower.rs
@@ -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
Some(trait_ref) => {
- let Path { mod_path, generic_args: path_generic_args, .. } =
- Path::from_src(trait_ref.path()?, ctx)?;
+ let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
+ Path::from_src(trait_ref.path()?, ctx)? else
+ {
+ return None;
+ };
let num_segments = mod_path.segments().len();
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));
- return Some(Path {
+ return Some(Path::Normal {
type_anchor,
mod_path,
generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 61e64fc103..958882d43e 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -15,8 +15,9 @@ use crate::{
expr::{BindingId, ExprId, LabelId},
generics::{GenericParams, TypeOrConstParamData},
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
+ lang_item::LangItemTarget,
nameres::DefMap,
- path::{ModPath, PathKind},
+ path::{ModPath, Path, PathKind},
per_ns::PerNs,
visibility::{RawVisibility, Visibility},
AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
@@ -176,8 +177,27 @@ impl Resolver {
pub fn resolve_path_in_type_ns(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> 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 skip_to_mod = path.kind != PathKind::Plain;
if skip_to_mod {
@@ -217,7 +237,7 @@ impl Resolver {
pub fn resolve_path_in_type_ns_fully(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<TypeNs> {
let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
if unresolved.is_some() {
@@ -245,8 +265,24 @@ impl Resolver {
pub fn resolve_path_in_value_ns(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> 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 tmp = name![self];
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(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<ValueNs> {
match self.resolve_path_in_value_ns(db, path)? {
ResolveValueResult::ValueNs(it) => Some(it),
@@ -441,7 +477,7 @@ impl Resolver {
&Scope::ImplDefScope(impl_) => {
if let Some(target_trait) = &db.impl_data(impl_).target_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_);
}
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index c3462beac7..8099c20b02 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -78,7 +78,7 @@ impl Name {
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 {
Name::new_text(SmolStr::new_inline(text))
}
@@ -112,6 +112,18 @@ impl 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.
pub fn as_tuple_index(&self) -> Option<usize> {
match self.0 {
@@ -343,6 +355,8 @@ pub mod known {
feature,
// known methods of lang items
call_once,
+ call_mut,
+ call,
eq,
ne,
ge,
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 5830c48988..7e69971fee 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -4,7 +4,7 @@ use base_db::CrateId;
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
use hir_def::{
expr::Expr,
- path::ModPath,
+ path::Path,
resolver::{Resolver, ValueNs},
type_ref::ConstRef,
ConstId, EnumVariantId,
@@ -15,7 +15,7 @@ use stdx::never;
use crate::{
db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
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};
@@ -72,7 +72,7 @@ impl From<MirEvalError> for ConstEvalError {
pub(crate) fn path_to_const(
db: &dyn HirDatabase,
resolver: &Resolver,
- path: &ModPath,
+ path: &Path,
mode: ParamLoweringMode,
args_lazy: impl FnOnce() -> Generics,
debruijn: DebruijnIndex,
@@ -89,7 +89,7 @@ pub(crate) fn path_to_const(
Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)),
None => {
never!(
- "Generic list doesn't contain this param: {:?}, {}, {:?}",
+ "Generic list doesn't contain this param: {:?}, {:?}, {:?}",
args,
path,
p
@@ -169,6 +169,7 @@ pub(crate) fn const_eval_recover(
_: &dyn HirDatabase,
_: &[String],
_: &ConstId,
+ _: &Substitution,
) -> Result<Const, ConstEvalError> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
@@ -184,10 +185,11 @@ pub(crate) fn const_eval_discriminant_recover(
pub(crate) fn const_eval_query(
db: &dyn HirDatabase,
const_id: ConstId,
+ subst: Substitution,
) -> Result<Const, ConstEvalError> {
let def = const_id.into();
let body = db.mir_body(def)?;
- let c = interpret_mir(db, &body, false)?;
+ let c = interpret_mir(db, &body, subst, false)?;
Ok(c)
}
@@ -210,7 +212,7 @@ pub(crate) fn const_eval_discriminant_variant(
return Ok(value);
}
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;
Ok(c)
}
@@ -228,13 +230,13 @@ pub(crate) fn eval_to_const(
let db = ctx.db;
if let Expr::Path(p) = &ctx.body.exprs[expr] {
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;
}
}
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(result) = interpret_mir(db, &mir_body, true) {
+ if let Ok(result) = interpret_mir(db, &mir_body, Substitution::empty(Interner), true) {
return result;
}
}
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 6a29e8ce52..a0efc7541e 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -1,8 +1,10 @@
use base_db::fixture::WithFixture;
+use chalk_ir::Substitution;
use hir_def::db::DefDatabase;
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::{
@@ -10,6 +12,8 @@ use super::{
ConstEvalError,
};
+mod intrinsics;
+
fn simplify(e: ConstEvalError) -> ConstEvalError {
match e {
ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => {
@@ -30,7 +34,12 @@ fn check_number(ra_fixture: &str, answer: i128) {
match &r.data(Interner).value {
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
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),
},
@@ -56,7 +65,7 @@ fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
_ => None,
})
.unwrap();
- db.const_eval(const_id)
+ db.const_eval(const_id, Substitution::empty(Interner))
}
#[test]
@@ -77,6 +86,49 @@ fn bit_op() {
}
#[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]
fn locals() {
check_number(
r#"
@@ -166,8 +218,7 @@ fn reference_autoderef() {
#[test]
fn overloaded_deref() {
- // FIXME: We should support this.
- check_fail(
+ check_number(
r#"
//- minicore: deref_mut
struct Foo;
@@ -185,9 +236,7 @@ fn overloaded_deref() {
*y + *x
};
"#,
- ConstEvalError::MirLowerError(MirLowerError::NotSupported(
- "explicit overloaded deref".into(),
- )),
+ 10,
);
}
@@ -219,6 +268,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]
fn function_call() {
check_number(
r#"
@@ -241,20 +326,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]
fn trait_basic() {
check_number(
r#"
@@ -483,6 +554,42 @@ fn loops() {
"#,
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]
@@ -523,6 +630,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]
fn recursion() {
check_number(
r#"
@@ -555,6 +674,38 @@ fn structs() {
"#,
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]
@@ -599,13 +750,14 @@ fn tuples() {
);
check_number(
r#"
- struct TupleLike(i32, u8, i64, u16);
- const GOAL: u8 = {
+ struct TupleLike(i32, i64, u8, u16);
+ const GOAL: i64 = {
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(
r#"
@@ -653,6 +805,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]
fn pattern_matching_ergonomics() {
check_number(
r#"
@@ -665,6 +847,16 @@ fn pattern_matching_ergonomics() {
"#,
5,
);
+ check_number(
+ r#"
+ const GOAL: u8 = {
+ let a = &(2, 3);
+ let &(x, y) = a;
+ x + y
+ };
+ "#,
+ 5,
+ );
}
#[test]
@@ -749,6 +941,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]
fn options() {
check_number(
r#"
@@ -802,6 +1021,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]
fn or_pattern() {
check_number(
r#"
@@ -840,6 +1144,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]
fn array_and_index() {
check_number(
r#"
@@ -867,6 +1385,17 @@ fn array_and_index() {
check_number(
r#"
//- 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();"#,
5,
);
@@ -991,8 +1520,7 @@ fn const_generic_subst_fn() {
#[test]
fn const_generic_subst_assoc_const_impl() {
- // FIXME: this should evaluate to 5
- check_fail(
+ check_number(
r#"
struct Adder<const N: usize, const M: usize>;
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;
"#,
- ConstEvalError::MirEvalError(MirEvalError::TypeError("missing generic arg")),
+ 5,
);
}
diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs
new file mode 100644
index 0000000000..371d5cab33
--- /dev/null
+++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -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,
+ );
+}
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 304c78767f..000944e0b5 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -57,7 +57,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::consteval::const_eval_query)]
#[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::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)]
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)]
fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>;
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index d25c0ccf00..664822ee6f 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -73,7 +73,7 @@ fn walk_unsafe(
}
Expr::Path(path) => {
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 db.static_data(id).mutable {
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 7de5b4295f..38b7dee75f 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -25,22 +25,22 @@ use hir_def::{
expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
lang_item::{LangItem, LangItemTarget},
layout::Integer,
- path::Path,
+ path::{ModPath, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::TypeRef,
- AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
- ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
+ AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, ItemContainerId, Lookup,
+ TraitId, TypeAliasId, VariantId,
};
use hir_expand::name::{name, Name};
use la_arena::ArenaMap;
use rustc_hash::{FxHashMap, FxHashSet};
-use stdx::always;
+use stdx::{always, never};
use crate::{
db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
- lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
- GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
- TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
+ lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, AliasEq, AliasTy, Const,
+ DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId,
+ Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
};
// This lint has a false positive here. See the link below for details.
@@ -57,6 +57,7 @@ mod expr;
mod pat;
mod coerce;
mod closure;
+mod mutability;
/// The entry point of type inference.
pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
@@ -99,6 +100,8 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
ctx.infer_body();
+ ctx.infer_mut_body();
+
Arc::new(ctx.resolve_all())
}
@@ -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) {
return ty;
}
- let krate = owner.module(db.upcast()).krate();
- let trait_env = owner
- .as_generic_def_id()
- .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
+ let trait_env = db.trait_environment_for_body(owner);
let mut table = unify::InferenceTable::new(db, trait_env);
let ty_with_vars = table.normalize_associated_types_in(ty);
@@ -276,6 +276,13 @@ pub struct Adjustment {
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)]
pub enum Adjust {
/// Go from ! to any type.
@@ -506,10 +513,7 @@ impl<'a> InferenceContext<'a> {
body: &'a Body,
resolver: Resolver,
) -> Self {
- let krate = owner.module(db.upcast()).krate();
- let trait_env = owner
- .as_generic_def_id()
- .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
+ let trait_env = db.trait_environment_for_body(owner);
InferenceContext {
result: InferenceResult::default(),
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:
// https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
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 {
ValueNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
@@ -872,11 +876,15 @@ impl<'a> InferenceContext<'a> {
None => return (self.err_ty(), None),
}
} 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,
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 {
TypeNs::AdtId(AdtId::StructId(strukt)) => {
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 substs = generics.placeholder_subst(self.db);
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) => {
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)
.fill_with_inference_vars(&mut self.table)
.build();
- self.resolve_variant_on_alias(ty, unresolved, path)
+ self.resolve_variant_on_alias(ty, unresolved, mod_path)
}
TypeNs::AdtSelfType(_) => {
// FIXME this could happen in array size expressions, once we're checking them
@@ -953,9 +961,9 @@ impl<'a> InferenceContext<'a> {
&mut self,
ty: Ty,
unresolved: Option<usize>,
- path: &Path,
+ path: &ModPath,
) -> (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 {
None => {
let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
@@ -969,7 +977,7 @@ impl<'a> InferenceContext<'a> {
(ty, variant)
}
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
if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
let enum_data = self.db.enum_data(enum_id);
@@ -1017,10 +1025,6 @@ impl<'a> InferenceContext<'a> {
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> {
self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?)
}
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 48c9153026..6e899249b6 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -51,11 +51,12 @@ fn success(
pub(super) struct CoerceMany {
expected_ty: Ty,
final_ty: Option<Ty>,
+ expressions: Vec<ExprId>,
}
impl CoerceMany {
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
@@ -125,8 +126,15 @@ impl CoerceMany {
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
if let (Ok(result1), Ok(result2)) = (result1, result2) {
- ctx.table.register_infer_ok(result1);
- ctx.table.register_infer_ok(result2);
+ ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
+ 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);
}
}
@@ -148,6 +156,9 @@ impl CoerceMany {
}
cov_mark::hit!(coerce_merge_fail_fallback);
}
+ if let Some(expr) = expr {
+ self.expressions.push(expr);
+ }
}
}
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index ee186673ee..d52188bb28 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -34,6 +34,7 @@ use crate::{
method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
primitive::{self, UintTy},
static_lifetime, to_chalk_trait_id,
+ traits::FnTrait,
utils::{generics, Generics},
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt,
@@ -158,26 +159,6 @@ impl<'a> InferenceContext<'a> {
})
.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 } => {
let ret_ty = self.table.new_type_var();
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
@@ -385,16 +366,46 @@ impl<'a> InferenceContext<'a> {
|| res.is_none();
let (param_tys, ret_ty) = match res {
Some((func, params, ret_ty)) => {
- let adjustments = auto_deref_adjust_steps(&derefs);
- // FIXME: Handle call adjustments for Fn/FnMut
- self.write_expr_adj(*callee, adjustments);
- if let Some((trait_, func)) = func {
- 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());
+ let mut adjustments = auto_deref_adjust_steps(&derefs);
+ if let Some(fn_x) = func {
+ match fn_x {
+ FnTrait::FnOnce => (),
+ FnTrait::FnMut => {
+ if !matches!(
+ derefed_callee.kind(Interner),
+ TyKind::Ref(Mutability::Mut, _, _)
+ ) {
+ adjustments.push(Adjustment::borrow(
+ Mutability::Mut,
+ derefed_callee.clone(),
+ ));
+ }
+ }
+ FnTrait::Fn => {
+ if !matches!(
+ derefed_callee.kind(Interner),
+ TyKind::Ref(Mutability::Not, _, _)
+ ) {
+ adjustments.push(Adjustment::borrow(
+ Mutability::Not,
+ derefed_callee.clone(),
+ ));
+ }
+ }
+ }
+ let trait_ = fn_x
+ .get_id(self.db, self.trait_env.krate)
+ .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)
}
None => {
@@ -601,26 +612,18 @@ impl<'a> InferenceContext<'a> {
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
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 } => {
let cast_ty = self.make_ty(type_ref);
// FIXME: propagate the "castable to" expectation
- let _inner_ty = self.infer_expr_no_expect(*expr);
- // FIXME check the cast...
+ let inner_ty = self.infer_expr_no_expect(*expr);
+ 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
}
Expr::Ref { expr, rawness, mutability } => {
@@ -656,6 +659,23 @@ impl<'a> InferenceContext<'a> {
// FIXME: Note down method resolution her
match op {
UnaryOp::Deref => {
+ if let Some(deref_trait) = self
+ .db
+ .lang_item(self.table.trait_env.krate, LangItem::Deref)
+ .and_then(|l| l.as_trait())
+ {
+ if let Some(deref_fn) =
+ self.db.trait_data(deref_trait).method_by_name(&name![deref])
+ {
+ // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that
+ // the mutability is not wrong, and will be fixed in `self.infer_mut`).
+ self.write_method_resolution(
+ tgt_expr,
+ deref_fn,
+ Substitution::empty(Interner),
+ );
+ }
+ }
autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty())
}
UnaryOp::Neg => {
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
new file mode 100644
index 0000000000..7ed21d230c
--- /dev/null
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -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
+ }
+}
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 2267fedaa8..266e410187 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -39,7 +39,7 @@ impl<'a> InferenceContext<'a> {
} else {
// FIXME: report error, unresolved first path segment
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 {
ResolveValueResult::ValueNs(it) => (it, None),
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 504f0743aa..0e516b9399 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -8,16 +8,15 @@ use chalk_ir::{
};
use chalk_solve::infer::ParameterEnaVariableExt;
use ena::unify::UnifyKey;
-use hir_def::{FunctionId, TraitId};
use hir_expand::name;
use stdx::never;
use super::{InferOk, InferResult, InferenceContext, TypeError};
use crate::{
- db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar,
- Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment,
- InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution,
- Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
+ db::HirDatabase, fold_tys, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq,
+ AliasTy, BoundVar, Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance,
+ InEnvironment, InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt,
+ Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
};
impl<'a> InferenceContext<'a> {
@@ -631,10 +630,13 @@ impl<'a> InferenceTable<'a> {
&mut self,
ty: &Ty,
num_args: usize,
- ) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
+ ) -> Option<(Option<FnTrait>, Vec<Ty>, Ty)> {
match ty.callable_sig(self.db) {
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,
ty: &Ty,
num_args: usize,
- ) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
+ ) -> Option<(FnTrait, Vec<Ty>, Ty)> {
let krate = self.trait_env.krate;
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
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 mut trait_ref = projection.trait_ref(self.db);
let obligation = InEnvironment {
- goal: projection.trait_ref(self.db).cast(Interner),
- environment: trait_env,
+ 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() {
self.register_obligation(obligation.goal);
let return_ty = self.normalize_projection_ty(projection);
- Some((
- Some(fn_once_trait).zip(trait_data.method_by_name(&name!(call_once))),
- arg_tys,
- return_ty,
- ))
+ for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
+ let fn_x_trait = fn_x.get_id(self.db, krate)?;
+ trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
+ 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 {
None
}
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 9c63d67ab1..782a8ab4aa 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -576,10 +576,14 @@ where
}
pub fn callable_sig_from_fnonce(
- self_ty: &Ty,
+ mut self_ty: &Ty,
env: Arc<TraitEnvironment>,
db: &dyn HirDatabase,
) -> 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 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])?;
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 23b15087e3..e7490087e7 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -25,12 +25,12 @@ use hir_def::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
},
lang_item::{lang_attr, LangItem},
- path::{GenericArg, ModPath, Path, PathKind, PathSegment, PathSegments},
+ path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
resolver::{HasResolver, Resolver, TypeNs},
type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
- AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId,
- HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, StructId,
- TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
+ AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
+ GenericDefId, HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId,
+ StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
};
use hir_expand::{name::Name, ExpandResult};
use intern::Interned;
@@ -425,11 +425,10 @@ impl<'a> TyLoweringContext<'a> {
if path.segments().len() > 1 {
return None;
}
- let resolution =
- match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
- Some((it, None)) => it,
- _ => return None,
- };
+ let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
+ Some((it, None)) => it,
+ _ => return None,
+ };
match resolution {
TypeNs::GenericParam(param_id) => Some(param_id.into()),
_ => None,
@@ -608,7 +607,7 @@ impl<'a> TyLoweringContext<'a> {
}
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,
None => return (TyKind::Error.intern(Interner), None),
};
@@ -716,7 +715,7 @@ impl<'a> TyLoweringContext<'a> {
resolved: ValueTyDefId,
infer_args: bool,
) -> 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 {
ValueTyDefId::FunctionId(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 penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx));
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,
};
(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(
@@ -748,6 +754,21 @@ impl<'a> TyLoweringContext<'a> {
infer_args: bool,
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 {
// Remember that the item's own generic args come before its parent's.
let mut substs = Vec::new();
let def = if let Some(d) = def {
@@ -780,7 +801,7 @@ impl<'a> TyLoweringContext<'a> {
};
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 {
fill_self_params();
}
@@ -879,12 +900,11 @@ impl<'a> TyLoweringContext<'a> {
path: &Path,
explicit_self_ty: Option<Ty>,
) -> Option<TraitRef> {
- let resolved =
- 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.
- TypeNs::TraitId(tr) => tr,
- _ => return None,
- };
+ let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? {
+ // FIXME(trait_alias): We need to handle trait alias here.
+ TypeNs::TraitId(tr) => tr,
+ _ => return None,
+ };
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))
}
@@ -1381,9 +1401,7 @@ pub(crate) fn generic_predicates_for_param_query(
Some(it) => it,
None => return true,
};
- let tr = match resolver
- .resolve_path_in_type_ns_fully(db.upcast(), path.mod_path())
- {
+ let tr = match resolver.resolve_path_in_type_ns_fully(db.upcast(), path) {
Some(TypeNs::TraitId(tr)) => tr,
_ => return false,
};
@@ -1423,6 +1441,17 @@ pub(crate) fn generic_predicates_for_param_recover(
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(
db: &dyn HirDatabase,
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
// it works.
if let TypeRef::Path(p) = t {
- let p = p.mod_path();
+ let p = p.mod_path()?;
if p.kind == PathKind::Plain {
if let [n] = p.segments() {
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::Path(n) => {
let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
- path_to_const(db, resolver, &path, mode, args, debruijn)
- .unwrap_or_else(|| unknown_const(expected_ty))
+ path_to_const(
+ db,
+ resolver,
+ &Path::from_known_path_with_no_generic(path),
+ mode,
+ args,
+ debruijn,
+ )
+ .unwrap_or_else(|| unknown_const(expected_ty))
}
}
}
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index f3a27632bf..2003d24038 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -5,7 +5,7 @@
use std::{ops::ControlFlow, sync::Arc};
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::{
data::ImplData, item_scope::ItemScope, lang_item::LangItem, nameres::DefMap, AssocItemId,
BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId,
@@ -20,7 +20,7 @@ use crate::{
autoderef::{self, AutoderefKind},
db::HirDatabase,
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},
static_lifetime, to_chalk_trait_id,
utils::all_super_traits,
@@ -600,9 +600,9 @@ impl ReceiverAdjustments {
}
}
if let Some(m) = self.autoref {
- ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
- adjust
- .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() });
+ let a = Adjustment::borrow(m, ty);
+ ty = a.target.clone();
+ adjust.push(a);
}
if self.unsize_array {
ty = 'x: {
@@ -692,6 +692,39 @@ pub fn lookup_impl_const(
.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`.
///
/// 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,
fn_subst: Substitution,
) -> (FunctionId, Substitution) {
- let trait_id = match func.lookup(db.upcast()).container {
- ItemContainerId::TraitId(id) => id,
- _ => return (func, fn_subst),
+ let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
+ return (func, fn_subst)
};
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
let fn_params = fn_subst.len(Interner) - trait_params;
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index c5d843d9eb..7b83645fae 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -1,6 +1,6 @@
//! This module provides a MIR interpreter, which is used in const eval.
-use std::{borrow::Cow, collections::HashMap, iter};
+use std::{borrow::Cow, collections::HashMap, iter, ops::Range, sync::Arc};
use base_db::CrateId;
use chalk_ir::{
@@ -11,7 +11,7 @@ use hir_def::{
builtin_type::BuiltinType,
lang_item::{lang_attr, LangItem},
layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding, Variants},
- AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, Lookup, VariantId,
+ AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, VariantId,
};
use intern::Interned;
use la_arena::ArenaMap;
@@ -23,8 +23,10 @@ use crate::{
infer::{normalize, PointerCast},
layout::layout_of_ty,
mapping::from_chalk,
- method_resolution::lookup_impl_method,
- CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, Ty, TyBuilder, TyExt,
+ method_resolution::{is_dyn_method, lookup_impl_method},
+ traits::FnTrait,
+ CallableDefId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, Substitution,
+ TraitEnvironment, Ty, TyBuilder, TyExt,
};
use super::{
@@ -32,10 +34,51 @@ use super::{
Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator, UnOp,
};
+macro_rules! from_bytes {
+ ($ty:tt, $value:expr) => {
+ ($ty::from_le_bytes(match ($value).try_into() {
+ Ok(x) => x,
+ Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
+ }))
+ };
+}
+
+#[derive(Debug, Default)]
+struct VTableMap {
+ ty_to_id: HashMap<Ty, usize>,
+ id_to_ty: Vec<Ty>,
+}
+
+impl VTableMap {
+ fn id(&mut self, ty: Ty) -> usize {
+ if let Some(x) = self.ty_to_id.get(&ty) {
+ return *x;
+ }
+ let id = self.id_to_ty.len();
+ self.id_to_ty.push(ty.clone());
+ self.ty_to_id.insert(ty, id);
+ id
+ }
+
+ fn ty(&self, id: usize) -> Result<&Ty> {
+ self.id_to_ty.get(id).ok_or(MirEvalError::InvalidVTableId(id))
+ }
+
+ fn ty_of_bytes(&self, bytes: &[u8]) -> Result<&Ty> {
+ let id = from_bytes!(usize, bytes);
+ self.ty(id)
+ }
+}
+
pub struct Evaluator<'a> {
db: &'a dyn HirDatabase,
+ trait_env: Arc<TraitEnvironment>,
stack: Vec<u8>,
heap: Vec<u8>,
+ /// We don't really have function pointers, i.e. pointers to some assembly instructions that we can run. Instead, we
+ /// store the type as an interned id in place of function and vtable pointers, and we recover back the type at the
+ /// time of use.
+ vtable_map: VTableMap,
crate_id: CrateId,
// FIXME: This is a workaround, see the comment on `interpret_mir`
assert_placeholder_ty_is_unused: bool,
@@ -53,11 +96,18 @@ enum Address {
use Address::*;
+#[derive(Debug, Clone, Copy)]
struct Interval {
addr: Address,
size: usize,
}
+#[derive(Debug, Clone)]
+struct IntervalAndTy {
+ interval: Interval,
+ ty: Ty,
+}
+
impl Interval {
fn new(addr: Address, size: usize) -> Self {
Self { addr, size }
@@ -66,6 +116,36 @@ impl Interval {
fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
memory.read_memory(self.addr, self.size)
}
+
+ fn write_from_bytes(&self, memory: &mut Evaluator<'_>, bytes: &[u8]) -> Result<()> {
+ memory.write_memory(self.addr, bytes)
+ }
+
+ fn write_from_interval(&self, memory: &mut Evaluator<'_>, interval: Interval) -> Result<()> {
+ // FIXME: this could be more efficent
+ let bytes = &interval.get(memory)?.to_vec();
+ memory.write_memory(self.addr, bytes)
+ }
+
+ fn slice(self, range: Range<usize>) -> Interval {
+ Interval { addr: self.addr.offset(range.start), size: range.len() }
+ }
+}
+
+impl IntervalAndTy {
+ fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
+ memory.read_memory(self.interval.addr, self.interval.size)
+ }
+
+ fn new(
+ addr: Address,
+ ty: Ty,
+ evaluator: &Evaluator<'_>,
+ locals: &Locals<'_>,
+ ) -> Result<IntervalAndTy> {
+ let size = evaluator.size_of_sized(&ty, locals, "type of interval")?;
+ Ok(IntervalAndTy { interval: Interval { addr, size }, ty })
+ }
}
enum IntervalOrOwned {
@@ -81,15 +161,6 @@ impl IntervalOrOwned {
}
}
-macro_rules! from_bytes {
- ($ty:tt, $value:expr) => {
- ($ty::from_le_bytes(match ($value).try_into() {
- Ok(x) => x,
- Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
- }))
- };
-}
-
impl Address {
fn from_bytes(x: &[u8]) -> Result<Self> {
Ok(Address::from_usize(from_bytes!(usize, x)))
@@ -97,7 +168,7 @@ impl Address {
fn from_usize(x: usize) -> Self {
if x > usize::MAX / 2 {
- Stack(usize::MAX - x)
+ Stack(x - usize::MAX / 2)
} else {
Heap(x)
}
@@ -109,7 +180,7 @@ impl Address {
fn to_usize(&self) -> usize {
let as_num = match self {
- Stack(x) => usize::MAX - *x,
+ Stack(x) => *x + usize::MAX / 2,
Heap(x) => *x,
};
as_num
@@ -136,7 +207,7 @@ pub enum MirEvalError {
/// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected
/// then use this type of error.
UndefinedBehavior(&'static str),
- Panic,
+ Panic(String),
MirLowerError(FunctionId, MirLowerError),
TypeIsUnsized(Ty, &'static str),
NotSupported(String),
@@ -145,6 +216,7 @@ pub enum MirEvalError {
ExecutionLimitExceeded,
StackOverflow,
TargetDataLayoutNotAvailable,
+ InvalidVTableId(usize),
}
impl std::fmt::Debug for MirEvalError {
@@ -158,7 +230,7 @@ impl std::fmt::Debug for MirEvalError {
Self::UndefinedBehavior(arg0) => {
f.debug_tuple("UndefinedBehavior").field(arg0).finish()
}
- Self::Panic => write!(f, "Panic"),
+ Self::Panic(msg) => write!(f, "Panic with message:\n{msg:?}"),
Self::TargetDataLayoutNotAvailable => write!(f, "TargetDataLayoutNotAvailable"),
Self::TypeIsUnsized(ty, it) => write!(f, "{ty:?} is unsized. {it} should be sized."),
Self::ExecutionLimitExceeded => write!(f, "execution limit exceeded"),
@@ -166,6 +238,7 @@ impl std::fmt::Debug for MirEvalError {
Self::MirLowerError(arg0, arg1) => {
f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
}
+ Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
Self::InvalidConst(arg0) => {
let data = &arg0.data(Interner);
@@ -209,6 +282,7 @@ struct Locals<'a> {
pub fn interpret_mir(
db: &dyn HirDatabase,
body: &MirBody,
+ subst: Substitution,
// FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now
// they share their body with their parent, so in MIR lowering we have locals of the parent body, which
// might have placeholders. With this argument, we (wrongly) assume that every placeholder type has
@@ -217,13 +291,12 @@ pub fn interpret_mir(
assert_placeholder_ty_is_unused: bool,
) -> Result<Const> {
let ty = body.locals[return_slot()].ty.clone();
- let mut evaluator =
- Evaluator::new(db, body.owner.module(db.upcast()).krate(), assert_placeholder_ty_is_unused);
- let bytes = evaluator.interpret_mir_with_no_arg(&body)?;
+ let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused);
+ let bytes = evaluator.interpret_mir(&body, None.into_iter(), subst.clone())?;
let memory_map = evaluator.create_memory_map(
&bytes,
&ty,
- &Locals { ptr: &ArenaMap::new(), body: &body, subst: &Substitution::empty(Interner) },
+ &Locals { ptr: &ArenaMap::new(), body: &body, subst: &subst },
)?;
return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
}
@@ -231,13 +304,17 @@ pub fn interpret_mir(
impl Evaluator<'_> {
pub fn new<'a>(
db: &'a dyn HirDatabase,
- crate_id: CrateId,
+ body: &MirBody,
assert_placeholder_ty_is_unused: bool,
) -> Evaluator<'a> {
+ let crate_id = body.owner.module(db.upcast()).krate();
+ let trait_env = db.trait_environment_for_body(body.owner);
Evaluator {
stack: vec![0],
heap: vec![0],
+ vtable_map: VTableMap::default(),
db,
+ trait_env,
crate_id,
assert_placeholder_ty_is_unused,
stack_depth_limit: 100,
@@ -246,7 +323,19 @@ impl Evaluator<'_> {
}
fn place_addr(&self, p: &Place, locals: &Locals<'_>) -> Result<Address> {
- Ok(self.place_addr_and_ty(p, locals)?.0)
+ Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0)
+ }
+
+ fn place_interval(&self, p: &Place, locals: &Locals<'_>) -> Result<Interval> {
+ let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?;
+ Ok(Interval {
+ addr: place_addr_and_ty.0,
+ size: self.size_of_sized(
+ &place_addr_and_ty.1,
+ locals,
+ "Type of place that we need its interval",
+ )?,
+ })
}
fn ptr_size(&self) -> usize {
@@ -256,10 +345,15 @@ impl Evaluator<'_> {
}
}
- fn place_addr_and_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<(Address, Ty)> {
+ fn place_addr_and_ty_and_metadata<'a>(
+ &'a self,
+ p: &Place,
+ locals: &'a Locals<'a>,
+ ) -> Result<(Address, Ty, Option<Interval>)> {
let mut addr = locals.ptr[p.local];
let mut ty: Ty =
self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?;
+ let mut metadata = None; // locals are always sized
for proj in &p.projection {
match proj {
ProjectionElem::Deref => {
@@ -271,12 +365,18 @@ impl Evaluator<'_> {
))
}
};
+ metadata = if self.size_of(&ty, locals)?.is_none() {
+ Some(Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() })
+ } else {
+ None
+ };
let x = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?);
addr = Address::from_usize(x);
}
ProjectionElem::Index(op) => {
let offset =
from_bytes!(usize, self.read_memory(locals.ptr[*op], self.ptr_size())?);
+ metadata = None; // Result of index is always sized
match &ty.data(Interner).kind {
TyKind::Ref(_, _, inner) => match &inner.data(Interner).kind {
TyKind::Slice(inner) => {
@@ -314,6 +414,7 @@ impl Evaluator<'_> {
.clone();
let offset = layout.fields.offset(f).bytes_usize();
addr = addr.offset(offset);
+ metadata = None; // tuple field is always sized
}
_ => return Err(MirEvalError::TypeError("Only tuple has tuple fields")),
},
@@ -343,6 +444,8 @@ impl Evaluator<'_> {
.offset(u32::from(f.local_id.into_raw()) as usize)
.bytes_usize();
addr = addr.offset(offset);
+ // FIXME: support structs with unsized fields
+ metadata = None;
}
_ => return Err(MirEvalError::TypeError("Only adt has fields")),
},
@@ -353,7 +456,7 @@ impl Evaluator<'_> {
ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"),
}
}
- Ok((addr, ty))
+ Ok((addr, ty, metadata))
}
fn layout(&self, ty: &Ty) -> Result<Layout> {
@@ -368,16 +471,23 @@ impl Evaluator<'_> {
}
fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<Ty> {
- Ok(self.place_addr_and_ty(p, locals)?.1)
+ Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1)
}
- fn operand_ty<'a>(&'a self, o: &'a Operand, locals: &'a Locals<'a>) -> Result<Ty> {
+ fn operand_ty(&self, o: &Operand, locals: &Locals<'_>) -> Result<Ty> {
Ok(match o {
Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?,
Operand::Constant(c) => c.data(Interner).ty.clone(),
})
}
+ fn operand_ty_and_eval(&mut self, o: &Operand, locals: &Locals<'_>) -> Result<IntervalAndTy> {
+ Ok(IntervalAndTy {
+ interval: self.eval_operand(o, locals)?,
+ ty: self.operand_ty(o, locals)?,
+ })
+ }
+
fn interpret_mir(
&mut self,
body: &MirBody,
@@ -455,116 +565,23 @@ impl Evaluator<'_> {
cleanup: _,
from_hir_call: _,
} => {
+ let destination = self.place_interval(destination, &locals)?;
let fn_ty = self.operand_ty(func, &locals)?;
+ let args = args
+ .iter()
+ .map(|x| self.operand_ty_and_eval(x, &locals))
+ .collect::<Result<Vec<_>>>()?;
match &fn_ty.data(Interner).kind {
+ TyKind::Function(_) => {
+ let bytes = self.eval_operand(func, &locals)?;
+ self.exec_fn_pointer(bytes, destination, &args, &locals)?;
+ }
TyKind::FnDef(def, generic_args) => {
- let def: CallableDefId = from_chalk(self.db, *def);
- let generic_args = self.subst_filler(generic_args, &locals);
- match def {
- CallableDefId::FunctionId(def) => {
- let arg_bytes = args
- .iter()
- .map(|x| {
- Ok(self
- .eval_operand(x, &locals)?
- .get(&self)?
- .to_owned())
- })
- .collect::<Result<Vec<_>>>()?
- .into_iter();
- let function_data = self.db.function_data(def);
- let is_intrinsic = match &function_data.abi {
- Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
- None => match def.lookup(self.db.upcast()).container {
- hir_def::ItemContainerId::ExternBlockId(block) => {
- let id = block.lookup(self.db.upcast()).id;
- id.item_tree(self.db.upcast())[id.value]
- .abi
- .as_deref()
- == Some("rust-intrinsic")
- }
- _ => false,
- },
- };
- let result = if is_intrinsic {
- self.exec_intrinsic(
- function_data
- .name
- .as_text()
- .unwrap_or_default()
- .as_str(),
- arg_bytes,
- generic_args,
- &locals,
- )?
- } else if let Some(x) = self.detect_lang_function(def) {
- self.exec_lang_item(x, arg_bytes)?
- } else {
- let trait_env = {
- let Some(d) = body.owner.as_generic_def_id() else {
- not_supported!("trait resolving in non generic def id");
- };
- self.db.trait_environment(d)
- };
- let (imp, generic_args) = lookup_impl_method(
- self.db,
- trait_env,
- def,
- generic_args.clone(),
- );
- let generic_args =
- self.subst_filler(&generic_args, &locals);
- let def = imp.into();
- let mir_body = self
- .db
- .mir_body(def)
- .map_err(|e| MirEvalError::MirLowerError(imp, e))?;
- self.interpret_mir(&mir_body, arg_bytes, generic_args)
- .map_err(|e| {
- MirEvalError::InFunction(imp, Box::new(e))
- })?
- };
- let dest_addr = self.place_addr(destination, &locals)?;
- self.write_memory(dest_addr, &result)?;
- }
- CallableDefId::StructId(id) => {
- let (size, variant_layout, tag) = self.layout_of_variant(
- id.into(),
- generic_args.clone(),
- &locals,
- )?;
- let result = self.make_by_layout(
- size,
- &variant_layout,
- tag,
- args,
- &locals,
- )?;
- let dest_addr = self.place_addr(destination, &locals)?;
- self.write_memory(dest_addr, &result)?;
- }
- CallableDefId::EnumVariantId(id) => {
- let (size, variant_layout, tag) = self.layout_of_variant(
- id.into(),
- generic_args.clone(),
- &locals,
- )?;
- let result = self.make_by_layout(
- size,
- &variant_layout,
- tag,
- args,
- &locals,
- )?;
- let dest_addr = self.place_addr(destination, &locals)?;
- self.write_memory(dest_addr, &result)?;
- }
- }
- current_block_idx =
- target.expect("broken mir, function without target");
+ self.exec_fn_def(*def, generic_args, destination, &args, &locals)?;
}
- _ => not_supported!("unknown function type"),
+ x => not_supported!("unknown function type {x:?}"),
}
+ current_block_idx = target.expect("broken mir, function without target");
}
Terminator::SwitchInt { discr, targets } => {
let val = u128::from_le_bytes(pad16(
@@ -584,7 +601,7 @@ impl Evaluator<'_> {
.to_owned());
}
Terminator::Unreachable => {
- return Err(MirEvalError::UndefinedBehavior("unreachable executed"))
+ return Err(MirEvalError::UndefinedBehavior("unreachable executed"));
}
_ => not_supported!("unknown terminator"),
}
@@ -600,8 +617,12 @@ impl Evaluator<'_> {
Ok(match r {
Rvalue::Use(x) => Borrowed(self.eval_operand(x, locals)?),
Rvalue::Ref(_, p) => {
- let addr = self.place_addr(p, locals)?;
- Owned(addr.to_bytes())
+ let (addr, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?;
+ let mut r = addr.to_bytes();
+ if let Some(metadata) = metadata {
+ r.extend(metadata.get(self)?);
+ }
+ Owned(r)
}
Rvalue::Len(_) => not_supported!("rvalue len"),
Rvalue::UnaryOp(op, val) => {
@@ -640,7 +661,14 @@ impl Evaluator<'_> {
let mut ty = self.operand_ty(lhs, locals)?;
while let TyKind::Ref(_, _, z) = ty.kind(Interner) {
ty = z.clone();
- let size = self.size_of_sized(&ty, locals, "operand of binary op")?;
+ let size = if ty.kind(Interner) == &TyKind::Str {
+ let ns = from_bytes!(usize, &lc[self.ptr_size()..self.ptr_size() * 2]);
+ lc = &lc[..self.ptr_size()];
+ rc = &rc[..self.ptr_size()];
+ ns
+ } else {
+ self.size_of_sized(&ty, locals, "operand of binary op")?
+ };
lc = self.read_memory(Address::from_bytes(lc)?, size)?;
rc = self.read_memory(Address::from_bytes(rc)?, size)?;
}
@@ -672,8 +700,12 @@ impl Evaluator<'_> {
let r = match op {
BinOp::Add => l128.overflowing_add(r128).0,
BinOp::Mul => l128.overflowing_mul(r128).0,
- BinOp::Div => l128.checked_div(r128).ok_or(MirEvalError::Panic)?,
- BinOp::Rem => l128.checked_rem(r128).ok_or(MirEvalError::Panic)?,
+ BinOp::Div => l128.checked_div(r128).ok_or_else(|| {
+ MirEvalError::Panic(format!("Overflow in {op:?}"))
+ })?,
+ BinOp::Rem => l128.checked_rem(r128).ok_or_else(|| {
+ MirEvalError::Panic(format!("Overflow in {op:?}"))
+ })?,
BinOp::Sub => l128.overflowing_sub(r128).0,
BinOp::BitAnd => l128 & r128,
BinOp::BitOr => l128 | r128,
@@ -683,16 +715,16 @@ impl Evaluator<'_> {
let r = r.to_le_bytes();
for &k in &r[lc.len()..] {
if k != 0 && (k != 255 || !is_signed) {
- return Err(MirEvalError::Panic);
+ return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
}
}
Owned(r[0..lc.len()].into())
}
BinOp::Shl | BinOp::Shr => {
let shift_amout = if r128 < 0 {
- return Err(MirEvalError::Panic);
+ return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
} else if r128 > 128 {
- return Err(MirEvalError::Panic);
+ return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
} else {
r128 as u8
};
@@ -710,8 +742,24 @@ impl Evaluator<'_> {
let ty = self.place_ty(p, locals)?;
let bytes = self.eval_place(p, locals)?.get(&self)?;
let layout = self.layout(&ty)?;
+ let enum_id = 'b: {
+ match ty.kind(Interner) {
+ TyKind::Adt(e, _) => match e.0 {
+ AdtId::EnumId(e) => break 'b e,
+ _ => (),
+ },
+ _ => (),
+ }
+ return Ok(Owned(0u128.to_le_bytes().to_vec()));
+ };
match layout.variants {
- Variants::Single { .. } => Owned(0u128.to_le_bytes().to_vec()),
+ Variants::Single { index } => {
+ let r = self.db.const_eval_discriminant(EnumVariantId {
+ parent: enum_id,
+ local_id: index.0,
+ })?;
+ Owned(r.to_le_bytes().to_vec())
+ }
Variants::Multiple { tag, tag_encoding, .. } => {
let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else {
not_supported!("missing target data layout");
@@ -727,13 +775,6 @@ impl Evaluator<'_> {
let tag = &bytes[offset..offset + size];
let candidate_discriminant = i128::from_le_bytes(pad16(tag, false))
.wrapping_sub(niche_start as i128);
- let enum_id = match ty.kind(Interner) {
- TyKind::Adt(e, _) => match e.0 {
- AdtId::EnumId(e) => e,
- _ => not_supported!("Non enum with multi variant layout"),
- },
- _ => not_supported!("Non adt with multi variant layout"),
- };
let enum_data = self.db.enum_data(enum_id);
let result = 'b: {
for (local_id, _) in enum_data.variants.iter() {
@@ -759,48 +800,65 @@ impl Evaluator<'_> {
}
Rvalue::ShallowInitBox(_, _) => not_supported!("shallow init box"),
Rvalue::CopyForDeref(_) => not_supported!("copy for deref"),
- Rvalue::Aggregate(kind, values) => match kind {
- AggregateKind::Array(_) => {
- let mut r = vec![];
- for x in values {
- let value = self.eval_operand(x, locals)?.get(&self)?;
- r.extend(value);
+ Rvalue::Aggregate(kind, values) => {
+ let values = values
+ .iter()
+ .map(|x| self.eval_operand(x, locals))
+ .collect::<Result<Vec<_>>>()?;
+ match kind {
+ AggregateKind::Array(_) => {
+ let mut r = vec![];
+ for x in values {
+ let value = x.get(&self)?;
+ r.extend(value);
+ }
+ Owned(r)
+ }
+ AggregateKind::Tuple(ty) => {
+ let layout = self.layout(&ty)?;
+ Owned(self.make_by_layout(
+ layout.size.bytes_usize(),
+ &layout,
+ None,
+ values.iter().copied(),
+ )?)
+ }
+ AggregateKind::Union(x, f) => {
+ let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?;
+ let offset = layout
+ .fields
+ .offset(u32::from(f.local_id.into_raw()) as usize)
+ .bytes_usize();
+ let op = values[0].get(&self)?;
+ let mut result = vec![0; layout.size.bytes_usize()];
+ result[offset..offset + op.len()].copy_from_slice(op);
+ Owned(result)
+ }
+ AggregateKind::Adt(x, subst) => {
+ let subst = self.subst_filler(subst, locals);
+ let (size, variant_layout, tag) =
+ self.layout_of_variant(*x, subst, locals)?;
+ Owned(self.make_by_layout(
+ size,
+ &variant_layout,
+ tag,
+ values.iter().copied(),
+ )?)
}
- Owned(r)
- }
- AggregateKind::Tuple(ty) => {
- let layout = self.layout(&ty)?;
- Owned(self.make_by_layout(
- layout.size.bytes_usize(),
- &layout,
- None,
- values,
- locals,
- )?)
- }
- AggregateKind::Union(x, f) => {
- let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?;
- let offset = layout
- .fields
- .offset(u32::from(f.local_id.into_raw()) as usize)
- .bytes_usize();
- let op = self.eval_operand(&values[0], locals)?.get(&self)?;
- let mut result = vec![0; layout.size.bytes_usize()];
- result[offset..offset + op.len()].copy_from_slice(op);
- Owned(result)
- }
- AggregateKind::Adt(x, subst) => {
- let (size, variant_layout, tag) =
- self.layout_of_variant(*x, subst.clone(), locals)?;
- Owned(self.make_by_layout(size, &variant_layout, tag, values, locals)?)
}
- },
+ }
Rvalue::Cast(kind, operand, target_ty) => match kind {
- CastKind::PointerExposeAddress => not_supported!("exposing pointer address"),
- CastKind::PointerFromExposedAddress => {
- not_supported!("creating pointer from exposed address")
- }
CastKind::Pointer(cast) => match cast {
+ PointerCast::ReifyFnPointer => {
+ let current_ty = self.operand_ty(operand, locals)?;
+ if let TyKind::FnDef(_, _) = &current_ty.data(Interner).kind {
+ let id = self.vtable_map.id(current_ty);
+ let ptr_size = self.ptr_size();
+ Owned(id.to_le_bytes()[0..ptr_size].to_vec())
+ } else {
+ not_supported!("ReifyFnPointer cast of a non FnDef type");
+ }
+ }
PointerCast::Unsize => {
let current_ty = self.operand_ty(operand, locals)?;
match &target_ty.data(Interner).kind {
@@ -826,7 +884,18 @@ impl Evaluator<'_> {
}
_ => not_supported!("slice unsizing from non pointers"),
},
- TyKind::Dyn(_) => not_supported!("dyn pointer unsize cast"),
+ TyKind::Dyn(_) => match &current_ty.data(Interner).kind {
+ TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
+ let vtable = self.vtable_map.id(ty.clone());
+ let addr =
+ self.eval_operand(operand, locals)?.get(&self)?;
+ let mut r = Vec::with_capacity(16);
+ r.extend(addr.iter().copied());
+ r.extend(vtable.to_le_bytes().into_iter());
+ Owned(r)
+ }
+ _ => not_supported!("dyn unsizing from non pointers"),
+ },
_ => not_supported!("unknown unsized cast"),
}
}
@@ -836,7 +905,9 @@ impl Evaluator<'_> {
x => not_supported!("pointer cast {x:?}"),
},
CastKind::DynStar => not_supported!("dyn star cast"),
- CastKind::IntToInt => {
+ CastKind::IntToInt
+ | CastKind::PointerExposeAddress
+ | CastKind::PointerFromExposedAddress => {
// FIXME: handle signed cast
let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false);
let dest_size =
@@ -846,7 +917,12 @@ impl Evaluator<'_> {
CastKind::FloatToInt => not_supported!("float to int cast"),
CastKind::FloatToFloat => not_supported!("float to float cast"),
CastKind::IntToFloat => not_supported!("float to int cast"),
- CastKind::PtrToPtr => not_supported!("ptr to ptr cast"),
+ CastKind::PtrToPtr => {
+ let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false);
+ let dest_size =
+ self.size_of_sized(target_ty, locals, "destination of ptr to ptr cast")?;
+ Owned(current[0..dest_size].to_vec())
+ }
CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"),
},
})
@@ -913,16 +989,15 @@ impl Evaluator<'_> {
size: usize, // Not neccessarily equal to variant_layout.size
variant_layout: &Layout,
tag: Option<(usize, usize, i128)>,
- values: &Vec<Operand>,
- locals: &Locals<'_>,
+ values: impl Iterator<Item = Interval>,
) -> Result<Vec<u8>> {
let mut result = vec![0; size];
if let Some((offset, size, value)) = tag {
result[offset..offset + size].copy_from_slice(&value.to_le_bytes()[0..size]);
}
- for (i, op) in values.iter().enumerate() {
+ for (i, op) in values.enumerate() {
let offset = variant_layout.fields.offset(i).bytes_usize();
- let op = self.eval_operand(op, locals)?.get(&self)?;
+ let op = op.get(&self)?;
result[offset..offset + op.len()].copy_from_slice(op);
}
Ok(result)
@@ -1124,12 +1199,27 @@ impl Evaluator<'_> {
}
fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
+ use LangItem::*;
let candidate = lang_attr(self.db.upcast(), def)?;
- // filter normal lang functions out
- if [LangItem::IntoIterIntoIter, LangItem::IteratorNext].contains(&candidate) {
+ // We want to execute these functions with special logic
+ if [PanicFmt, BeginPanic, SliceLen].contains(&candidate) {
+ return Some(candidate);
+ }
+ None
+ }
+
+ fn detect_fn_trait(&self, def: FunctionId) -> Option<FnTrait> {
+ use LangItem::*;
+ let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else {
return None;
+ };
+ let l = lang_attr(self.db.upcast(), parent)?;
+ match l {
+ FnOnce => Some(FnTrait::FnOnce),
+ FnMut => Some(FnTrait::FnMut),
+ Fn => Some(FnTrait::Fn),
+ _ => None,
}
- Some(candidate)
}
fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result<MemoryMap> {
@@ -1199,35 +1289,276 @@ impl Evaluator<'_> {
}
fn exec_intrinsic(
- &self,
+ &mut self,
as_str: &str,
- _arg_bytes: impl Iterator<Item = Vec<u8>>,
+ args: &[IntervalAndTy],
generic_args: Substitution,
+ destination: Interval,
locals: &Locals<'_>,
- ) -> Result<Vec<u8>> {
+ ) -> Result<()> {
match as_str {
"size_of" => {
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
};
- let size = self.size_of(ty, locals)?;
- match size {
- Some(x) => Ok(x.to_le_bytes().to_vec()),
- None => return Err(MirEvalError::TypeError("size_of arg is unsized")),
+ let size = self.size_of_sized(ty, locals, "size_of arg")?;
+ destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size])
+ }
+ "wrapping_add" => {
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.wrapping_add(rhs);
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "copy" | "copy_nonoverlapping" => {
+ let [src, dst, offset] = args else {
+ return Err(MirEvalError::TypeError("copy_nonoverlapping args are not provided"));
+ };
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("copy_nonoverlapping generic arg is not provided"));
+ };
+ let src = Address::from_bytes(src.get(self)?)?;
+ let dst = Address::from_bytes(dst.get(self)?)?;
+ let offset = from_bytes!(usize, offset.get(self)?);
+ let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?;
+ let size = offset * size;
+ let src = Interval { addr: src, size };
+ let dst = Interval { addr: dst, size };
+ dst.write_from_interval(self, src)
+ }
+ "offset" | "arith_offset" => {
+ let [ptr, offset] = args else {
+ return Err(MirEvalError::TypeError("offset args are not provided"));
+ };
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("offset generic arg is not provided"));
+ };
+ let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false));
+ let offset = u128::from_le_bytes(pad16(offset.get(self)?, false));
+ let size = self.size_of_sized(ty, locals, "offset ptr type")? as u128;
+ let ans = ptr + offset * size;
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" => {
+ // FIXME: We should actually implement these checks
+ Ok(())
+ }
+ "forget" => {
+ // We don't call any drop glue yet, so there is nothing here
+ Ok(())
+ }
+ "transmute" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("trasmute arg is not provided"));
+ };
+ destination.write_from_interval(self, arg.interval)
+ }
+ "const_eval_select" => {
+ let [tuple, const_fn, _] = args else {
+ return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
+ };
+ let mut args = vec![const_fn.clone()];
+ let TyKind::Tuple(_, fields) = tuple.ty.kind(Interner) else {
+ return Err(MirEvalError::TypeError("const_eval_select arg[0] is not a tuple"));
+ };
+ let layout = self.layout(&tuple.ty)?;
+ for (i, field) in fields.iter(Interner).enumerate() {
+ let field = field.assert_ty_ref(Interner).clone();
+ let offset = layout.fields.offset(i).bytes_usize();
+ let addr = tuple.interval.addr.offset(offset);
+ args.push(IntervalAndTy::new(addr, field, self, locals)?);
}
+ self.exec_fn_trait(&args, destination, locals)
}
_ => not_supported!("unknown intrinsic {as_str}"),
}
}
- pub(crate) fn exec_lang_item(
- &self,
- x: LangItem,
- mut args: std::vec::IntoIter<Vec<u8>>,
- ) -> Result<Vec<u8>> {
+ fn exec_fn_pointer(
+ &mut self,
+ bytes: Interval,
+ destination: Interval,
+ args: &[IntervalAndTy],
+ locals: &Locals<'_>,
+ ) -> Result<()> {
+ let id = from_bytes!(usize, bytes.get(self)?);
+ let next_ty = self.vtable_map.ty(id)?.clone();
+ if let TyKind::FnDef(def, generic_args) = &next_ty.data(Interner).kind {
+ self.exec_fn_def(*def, generic_args, destination, args, &locals)?;
+ } else {
+ return Err(MirEvalError::TypeError("function pointer to non function"));
+ }
+ Ok(())
+ }
+
+ fn exec_fn_def(
+ &mut self,
+ def: FnDefId,
+ generic_args: &Substitution,
+ destination: Interval,
+ args: &[IntervalAndTy],
+ locals: &Locals<'_>,
+ ) -> Result<()> {
+ let def: CallableDefId = from_chalk(self.db, def);
+ let generic_args = self.subst_filler(generic_args, &locals);
+ match def {
+ CallableDefId::FunctionId(def) => {
+ if let Some(_) = self.detect_fn_trait(def) {
+ self.exec_fn_trait(&args, destination, locals)?;
+ return Ok(());
+ }
+ self.exec_fn_with_args(def, args, generic_args, locals, destination)?;
+ }
+ CallableDefId::StructId(id) => {
+ let (size, variant_layout, tag) =
+ self.layout_of_variant(id.into(), generic_args.clone(), &locals)?;
+ let result = self.make_by_layout(
+ size,
+ &variant_layout,
+ tag,
+ args.iter().map(|x| x.interval),
+ )?;
+ destination.write_from_bytes(self, &result)?;
+ }
+ CallableDefId::EnumVariantId(id) => {
+ let (size, variant_layout, tag) =
+ self.layout_of_variant(id.into(), generic_args.clone(), &locals)?;
+ let result = self.make_by_layout(
+ size,
+ &variant_layout,
+ tag,
+ args.iter().map(|x| x.interval),
+ )?;
+ destination.write_from_bytes(self, &result)?;
+ }
+ }
+ Ok(())
+ }
+
+ fn exec_fn_with_args(
+ &mut self,
+ def: FunctionId,
+ args: &[IntervalAndTy],
+ generic_args: Substitution,
+ locals: &Locals<'_>,
+ destination: Interval,
+ ) -> Result<()> {
+ let function_data = self.db.function_data(def);
+ let is_intrinsic = match &function_data.abi {
+ Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
+ None => match def.lookup(self.db.upcast()).container {
+ hir_def::ItemContainerId::ExternBlockId(block) => {
+ let id = block.lookup(self.db.upcast()).id;
+ id.item_tree(self.db.upcast())[id.value].abi.as_deref()
+ == Some("rust-intrinsic")
+ }
+ _ => false,
+ },
+ };
+ if is_intrinsic {
+ return self.exec_intrinsic(
+ function_data.name.as_text().unwrap_or_default().as_str(),
+ args,
+ generic_args,
+ destination,
+ &locals,
+ );
+ }
+ let arg_bytes =
+ args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::<Result<Vec<_>>>()?;
+ let result = if let Some(x) = self.detect_lang_function(def) {
+ self.exec_lang_item(x, &arg_bytes)?
+ } else {
+ if let Some(self_ty_idx) =
+ is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone())
+ {
+ // In the layout of current possible receiver, which at the moment of writing this code is one of
+ // `&T`, `&mut T`, `Box<T>`, `Rc<T>`, `Arc<T>`, and `Pin<P>` where `P` is one of possible recievers,
+ // the vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on
+ // the type.
+ let ty = self
+ .vtable_map
+ .ty_of_bytes(&arg_bytes[0][self.ptr_size()..self.ptr_size() * 2])?;
+ let mut args_for_target = args.to_vec();
+ args_for_target[0] = IntervalAndTy {
+ interval: args_for_target[0].interval.slice(0..self.ptr_size()),
+ ty: ty.clone(),
+ };
+ let ty = GenericArgData::Ty(ty.clone()).intern(Interner);
+ let generics_for_target = Substitution::from_iter(
+ Interner,
+ generic_args.iter(Interner).enumerate().map(|(i, x)| {
+ if i == self_ty_idx {
+ &ty
+ } else {
+ x
+ }
+ }),
+ );
+ return self.exec_fn_with_args(
+ def,
+ &args_for_target,
+ generics_for_target,
+ locals,
+ destination,
+ );
+ }
+ let (imp, generic_args) =
+ lookup_impl_method(self.db, self.trait_env.clone(), def, generic_args.clone());
+ let generic_args = self.subst_filler(&generic_args, &locals);
+ let def = imp.into();
+ let mir_body =
+ self.db.mir_body(def).map_err(|e| MirEvalError::MirLowerError(imp, e))?;
+ self.interpret_mir(&mir_body, arg_bytes.iter().cloned(), generic_args)
+ .map_err(|e| MirEvalError::InFunction(imp, Box::new(e)))?
+ };
+ destination.write_from_bytes(self, &result)?;
+ Ok(())
+ }
+
+ fn exec_fn_trait(
+ &mut self,
+ args: &[IntervalAndTy],
+ destination: Interval,
+ locals: &Locals<'_>,
+ ) -> Result<()> {
+ let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?;
+ let mut func_ty = func.ty.clone();
+ let mut func_data = func.interval;
+ while let TyKind::Ref(_, _, z) = func_ty.kind(Interner) {
+ func_ty = z.clone();
+ if matches!(func_ty.kind(Interner), TyKind::Dyn(_)) {
+ let id =
+ from_bytes!(usize, &func_data.get(self)?[self.ptr_size()..self.ptr_size() * 2]);
+ func_data = func_data.slice(0..self.ptr_size());
+ func_ty = self.vtable_map.ty(id)?.clone();
+ }
+ let size = self.size_of_sized(&func_ty, locals, "self type of fn trait")?;
+ func_data = Interval { addr: Address::from_bytes(func_data.get(self)?)?, size };
+ }
+ match &func_ty.data(Interner).kind {
+ TyKind::FnDef(def, subst) => {
+ self.exec_fn_def(*def, subst, destination, &args[1..], locals)?;
+ }
+ TyKind::Function(_) => {
+ self.exec_fn_pointer(func_data, destination, &args[1..], locals)?;
+ }
+ x => not_supported!("Call FnTrait methods with type {x:?}"),
+ }
+ Ok(())
+ }
+
+ fn exec_lang_item(&self, x: LangItem, args: &[Vec<u8>]) -> Result<Vec<u8>> {
use LangItem::*;
+ let mut args = args.iter();
match x {
- PanicFmt | BeginPanic => Err(MirEvalError::Panic),
+ // FIXME: we want to find the panic message from arguments, but it wouldn't work
+ // currently even if we do that, since macro expansion of panic related macros
+ // is dummy.
+ PanicFmt | BeginPanic => Err(MirEvalError::Panic("<format-args>".to_string())),
SliceLen => {
let arg = args
.next()
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index c4dd7c0ace..1821796be3 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -4,19 +4,21 @@ use std::{iter, mem, sync::Arc};
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
use hir_def::{
+ adt::{StructKind, VariantData},
body::Body,
expr::{
Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
- RecordLitField,
+ RecordFieldPat, RecordLitField,
},
lang_item::{LangItem, LangItemTarget},
layout::LayoutError,
path::Path,
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
- DefWithBodyId, EnumVariantId, HasModule,
+ AdtId, DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, LocalFieldId, TraitId,
};
use hir_expand::name::Name;
use la_arena::ArenaMap;
+use rustc_hash::FxHashMap;
use crate::{
consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
@@ -27,18 +29,25 @@ use crate::{
use super::*;
mod as_place;
+mod pattern_matching;
-#[derive(Debug, Clone, Copy)]
+use pattern_matching::AdtPatternShape;
+
+#[derive(Debug, Clone)]
struct LoopBlocks {
begin: BasicBlockId,
/// `None` for loops that are not terminating
end: Option<BasicBlockId>,
+ place: Place,
}
struct MirLowerCtx<'a> {
result: MirBody,
owner: DefWithBodyId,
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>,
db: &'a dyn HirDatabase,
body: &'a Body,
@@ -50,6 +59,8 @@ pub enum MirLowerError {
ConstEvalError(Box<ConstEvalError>),
LayoutError(LayoutError),
IncompleteExpr,
+ /// Trying to lower a trait function, instead of an implementation
+ TraitFunctionDefinition(TraitId, Name),
UnresolvedName(String),
RecordLiteralWithoutPath,
UnresolvedMethod,
@@ -66,6 +77,7 @@ pub enum MirLowerError {
ImplementationError(&'static str),
LangItemNotFound(LangItem),
MutatingRvalue,
+ UnresolvedLabel,
}
macro_rules! not_supported {
@@ -200,26 +212,42 @@ impl MirLowerCtx<'_> {
mut current: BasicBlockId,
) -> Result<Option<BasicBlockId>> {
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) => {
let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
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)?;
let pr = match pr {
ResolveValueResult::ValueNs(v) => v,
ResolveValueResult::Partial(..) => {
- if let Some(assoc) = self
+ if let Some((assoc, subst)) = self
.infer
.assoc_resolutions_for_expr(expr_id)
{
- match assoc.0 {
+ match assoc {
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))
+ },
+ 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")
},
- _ => not_supported!("associated functions and types"),
}
} else if let Some(variant) = self
.infer
@@ -246,19 +274,23 @@ impl MirLowerCtx<'_> {
Ok(Some(current))
}
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))
}
ValueNs::EnumVariantId(variant_id) => {
- let ty = self.infer.type_of_expr[expr_id].clone();
- let current = self.lower_enum_variant(
- variant_id,
- current,
- place,
- ty,
- vec![],
- expr_id.into(),
- )?;
+ let variant_data = &self.db.enum_data(variant_id.parent).variants[variant_id.local_id];
+ if variant_data.variant_data.kind() == StructKind::Unit {
+ let ty = self.infer.type_of_expr[expr_id].clone();
+ current = self.lower_enum_variant(
+ variant_id,
+ current,
+ place,
+ 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))
}
ValueNs::GenericParam(p) => {
@@ -287,7 +319,7 @@ impl MirLowerCtx<'_> {
);
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.
Ok(Some(current))
}
@@ -349,19 +381,29 @@ impl MirLowerCtx<'_> {
Ok(self.merge_blocks(Some(then_target), else_target))
}
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 } => {
- 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)? {
this.set_goto(block, begin);
}
Ok(())
}),
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 {
return Ok(());
};
@@ -412,7 +454,7 @@ impl MirLowerCtx<'_> {
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.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)?
else {
return Ok(());
@@ -426,7 +468,8 @@ impl MirLowerCtx<'_> {
current,
pat.into(),
Some(end),
- &[pat], &None)?;
+ AdtPatternShape::Tuple { args: &[pat], ellipsis: None },
+ )?;
if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
this.set_goto(block, begin);
}
@@ -434,36 +477,36 @@ impl MirLowerCtx<'_> {
})
},
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);
match &callee_ty.data(Interner).kind {
chalk_ir::TyKind::FnDef(..) => {
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))
}
- TyKind::Scalar(_)
- | TyKind::Tuple(_, _)
- | TyKind::Array(_, _)
- | TyKind::Adt(_, _)
- | TyKind::Str
- | TyKind::Foreign(_)
- | TyKind::Slice(_) => {
- return Err(MirLowerError::TypeError("function call on data type"))
+ chalk_ir::TyKind::Function(_) => {
+ let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
+ return Ok(None);
+ };
+ self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
}
TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
- TyKind::AssociatedType(_, _)
- | 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"),
+ _ => return Err(MirLowerError::TypeError("function call on bad type")),
}
}
Expr::MethodCall { receiver, args, .. } => {
@@ -491,10 +534,7 @@ impl MirLowerCtx<'_> {
let cond_ty = self.expr_ty_after_adjustments(*expr);
let mut end = None;
for MatchArm { pat, guard, expr } in arms.iter() {
- if guard.is_some() {
- not_supported!("pattern matching with guard");
- }
- let (then, otherwise) = self.pattern_match(
+ let (then, mut otherwise) = self.pattern_match(
current,
None,
cond_place.clone(),
@@ -502,6 +542,16 @@ impl MirLowerCtx<'_> {
*pat,
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)? {
let r = end.get_or_insert_with(|| self.new_basic_block());
self.set_goto(block, *r);
@@ -524,24 +574,28 @@ impl MirLowerCtx<'_> {
Some(_) => not_supported!("continue with label"),
None => {
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);
Ok(None)
}
},
Expr::Break { expr, label } => {
- if expr.is_some() {
- not_supported!("break with value");
- }
- match label {
- Some(_) => not_supported!("break with label"),
- None => {
- let end =
- self.current_loop_end()?;
- self.set_goto(current, end);
- Ok(None)
- }
+ if let Some(expr) = expr {
+ let loop_data = match label {
+ Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
+ None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?,
+ };
+ let Some(c) = self.lower_expr_to_place(*expr, loop_data.place.clone(), current)? else {
+ return Ok(None);
+ };
+ current = c;
}
+ 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 } => {
if let Some(expr) = expr {
@@ -555,7 +609,17 @@ impl MirLowerCtx<'_> {
Ok(None)
}
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
.infer
.variant_resolution_for_expr(expr_id)
@@ -585,9 +649,24 @@ impl MirLowerCtx<'_> {
place,
Rvalue::Aggregate(
AggregateKind::Adt(variant_id, subst),
- operands.into_iter().map(|x| x).collect::<Option<_>>().ok_or(
- MirLowerError::TypeError("missing field in record literal"),
- )?,
+ match spread_place {
+ 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(),
);
@@ -608,9 +687,7 @@ impl MirLowerCtx<'_> {
}
}
Expr::Await { .. } => not_supported!("await"),
- Expr::Try { .. } => not_supported!("? operator"),
Expr::Yeet { .. } => not_supported!("yeet"),
- Expr::TryBlock { .. } => not_supported!("try block"),
Expr::Async { .. } => not_supported!("async block"),
Expr::Const { .. } => not_supported!("anonymous const block"),
Expr::Cast { expr, type_ref: _ } => {
@@ -703,7 +780,49 @@ impl MirLowerCtx<'_> {
);
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::Tuple { exprs, is_assignee_expr: _ } => {
let Some(values) = exprs
@@ -832,9 +951,10 @@ impl MirLowerCtx<'_> {
const_id: hir_def::ConstId,
prev_block: BasicBlockId,
place: Place,
+ subst: Substitution,
span: MirSpan,
) -> 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)
}
@@ -872,7 +992,7 @@ impl MirLowerCtx<'_> {
) -> Result<BasicBlockId> {
let subst = match ty.kind(Interner) {
TyKind::Adt(_, subst) => subst.clone(),
- _ => not_supported!("Non ADT enum"),
+ _ => implementation_error!("Non ADT enum"),
};
self.push_assignment(
prev_block,
@@ -970,287 +1090,6 @@ impl MirLowerCtx<'_> {
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 {
match &self.discr_temp {
Some(x) => x.clone(),
@@ -1266,19 +1105,34 @@ impl MirLowerCtx<'_> {
fn lower_loop(
&mut self,
prev_block: BasicBlockId,
+ place: Place,
label: Option<LabelId>,
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
) -> Result<Option<BasicBlockId>> {
- if label.is_some() {
- not_supported!("loop with label");
- }
let begin = self.new_basic_block();
- let prev =
- mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end: None }));
+ let prev = mem::replace(
+ &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);
f(self, begin)?;
let my = mem::replace(&mut self.current_loop_blocks, prev)
.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)
}
@@ -1366,15 +1220,11 @@ impl MirLowerCtx<'_> {
fn lower_block_to_place(
&mut self,
- label: Option<LabelId>,
statements: &[hir_def::expr::Statement],
mut current: BasicBlockId,
tail: Option<ExprId>,
place: Place,
) -> Result<Option<Idx<BasicBlock>>> {
- if label.is_some() {
- not_supported!("block with label");
- }
for statement in statements.iter() {
match statement {
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> {
Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) {
(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,
(_, _) => 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
(TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
CastKind::IntToInt
@@ -1552,6 +1391,7 @@ pub fn lower_to_mir(
body,
owner,
current_loop_blocks: None,
+ labeled_loop_blocks: Default::default(),
discr_temp: None,
};
let mut current = start_block;
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index fe8147dcd3..425904850b 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -1,6 +1,7 @@
//! MIR lowering for places
use super::*;
+use hir_def::FunctionId;
use hir_expand::name;
macro_rules! not_supported {
@@ -125,7 +126,7 @@ impl MirLowerCtx<'_> {
match &self.body.exprs[expr_id] {
Expr::Path(p) => {
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));
};
let pr = match pr {
@@ -145,10 +146,32 @@ impl MirLowerCtx<'_> {
self.expr_ty(*expr).kind(Interner),
TyKind::Ref(..) | TyKind::Raw(..)
) {
- let Some(_) = self.lower_expr_as_place(current, *expr, true)? else {
+ let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None);
};
- not_supported!("explicit overloaded deref");
+ return self.lower_overloaded_deref(
+ current,
+ p,
+ self.expr_ty_after_adjustments(*expr),
+ self.expr_ty(expr_id),
+ expr_id.into(),
+ 'b: {
+ if let Some((f, _)) = self.infer.method_resolution(expr_id) {
+ if let Some(deref_trait) =
+ self.resolve_lang_item(LangItem::DerefMut)?.as_trait()
+ {
+ if let Some(deref_fn) = self
+ .db
+ .trait_data(deref_trait)
+ .method_by_name(&name![deref_mut])
+ {
+ break 'b deref_fn == f;
+ }
+ }
+ }
+ false
+ },
+ );
}
let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None);
@@ -171,7 +194,24 @@ impl MirLowerCtx<'_> {
if index_ty != TyBuilder::usize()
|| !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)) =
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(
&mut self,
current: BasicBlockId,
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
new file mode 100644
index 0000000000..c3ced82aab
--- /dev/null
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -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);
+ }
+}
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index ffc08b7e34..9ec2913dce 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -1,6 +1,6 @@
//! A pretty-printer for MIR.
-use std::fmt::{Display, Write};
+use std::fmt::{Debug, Display, Write};
use hir_def::{body::Body, expr::BindingId};
use hir_expand::name::Name;
@@ -23,6 +23,18 @@ impl MirBody {
ctx.for_body();
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> {
@@ -77,6 +89,7 @@ impl Display for LocalName {
impl<'a> MirPrettyCtx<'a> {
fn for_body(&mut self) {
+ wln!(self, "// {:?}", self.body.owner);
self.with_block(|this| {
this.locals();
wln!(this);
@@ -300,9 +313,9 @@ impl<'a> MirPrettyCtx<'a> {
w!(self, ")");
}
Rvalue::Cast(ck, op, ty) => {
- w!(self, "Discriminant({ck:?}");
+ w!(self, "Cast({ck:?}, ");
self.operand(op);
- w!(self, "{})", ty.display(self.db));
+ w!(self, ", {})", ty.display(self.db));
}
Rvalue::CheckedBinaryOp(b, o1, o2) => {
self.operand(o1);
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index b524922b6c..9f624cc32c 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -258,7 +258,6 @@ fn test() {
#[test]
fn coerce_autoderef_block() {
- // FIXME: We should know mutability in overloaded deref
check_no_mismatches(
r#"
//- minicore: deref
@@ -268,7 +267,7 @@ fn takes_ref_str(x: &str) {}
fn returns_string() -> String { loop {} }
fn test() {
takes_ref_str(&{ returns_string() });
- // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(None))), Borrow(Ref(Not))
+ // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
}
"#,
);
@@ -397,9 +396,39 @@ 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]
fn coerce_fn_items_in_match_arms() {
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(
r"
fn foo1(x: u32) -> isize { 1 }
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index 378d478336..f3ca93672d 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1255,7 +1255,6 @@ fn foo<T: Trait>(a: &T) {
#[test]
fn autoderef_visibility_field() {
- // FIXME: We should know mutability in overloaded deref
check(
r#"
//- minicore: deref
@@ -1277,7 +1276,7 @@ mod a {
mod b {
fn foo() {
let x = super::a::Bar::new().0;
- // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None)))
+ // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not))))
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
}
}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 13cc3fea52..8322b9e1ca 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -2697,6 +2697,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]
fn infer_missing_type() {
check_types(
r#"
@@ -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]
fn issue_14275() {
// FIXME: evaluate const generic
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index da76d7fd83..97ec1bb871 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -206,19 +206,27 @@ fn test() {
fn infer_try_trait() {
check_types(
r#"
-//- minicore: try, result
+//- minicore: try, result, from
fn test() {
let r: Result<i32, u64> = Result::Ok(1);
let v = r?;
v;
} //^ 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<()>
+}
"#,
);
}
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index 3ab85c68f5..e7fffc4cc7 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -11,6 +11,7 @@ use hir_def::{
lang_item::{LangItem, LangItemTarget},
TraitId,
};
+use hir_expand::name::{name, Name};
use stdx::panic_context;
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())?;
match target {
LangItemTarget::Trait(t) => Some(t),
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 34d957e26e..a6967414aa 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -130,7 +130,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(Tra
WherePredicate::Lifetime { .. } => 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),
_ => None,
})
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 35424feec8..dbf618afa6 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1801,7 +1801,7 @@ impl Function {
let body = db
.mir_body(self.id.into())
.map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
- interpret_mir(db, &body, false)?;
+ interpret_mir(db, &body, Substitution::empty(Interner), false)?;
Ok(())
}
}
@@ -1947,7 +1947,7 @@ impl Const {
}
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));
// 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
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 407ba6f658..9709970db1 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -1076,10 +1076,7 @@ impl<'db> SemanticsImpl<'db> {
let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id);
let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
let hir_path = Path::from_src(path.clone(), &ctx)?;
- match analyze
- .resolver
- .resolve_path_in_type_ns_fully(self.db.upcast(), hir_path.mod_path())?
- {
+ match analyze.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), &hir_path)? {
TypeNs::TraitId(id) => Some(Trait { id }),
_ => None,
}
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index c24d196e1b..5b18e44572 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -420,7 +420,10 @@ impl SourceAnalyzer {
None
} else {
// 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) {
Some(ValueNs::LocalBinding(binding_id)) => {
Some(Local { binding_id, parent: self.resolver.body_owner()? })
@@ -461,7 +464,7 @@ impl SourceAnalyzer {
) -> Option<Macro> {
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))?;
- 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(
@@ -801,15 +804,11 @@ impl SourceAnalyzer {
func: FunctionId,
substs: Substitution,
) -> FunctionId {
- let krate = self.resolver.krate();
let owner = match self.resolver.body_owner() {
Some(it) => it,
None => return func,
};
- let env = owner.as_generic_def_id().map_or_else(
- || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
- |d| db.trait_environment(d),
- );
+ let env = db.trait_environment_for_body(owner);
method_resolution::lookup_impl_method(db, env, func, substs).0
}
@@ -819,15 +818,11 @@ impl SourceAnalyzer {
const_id: ConstId,
subs: Substitution,
) -> ConstId {
- let krate = self.resolver.krate();
let owner = match self.resolver.body_owner() {
Some(it) => it,
None => return const_id,
};
- let env = owner.as_generic_def_id().map_or_else(
- || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
- |d| db.trait_environment(d),
- );
+ let env = db.trait_environment_for_body(owner);
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,
path: &Path,
) -> 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_(
@@ -962,8 +957,7 @@ fn resolve_hir_path_(
res.map(|ty_ns| (ty_ns, path.segments().first()))
}
None => {
- let (ty, remaining_idx) =
- resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?;
+ let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
match remaining_idx {
Some(remaining_idx) => {
if remaining_idx + 1 == path.segments().len() {
@@ -1019,7 +1013,7 @@ fn resolve_hir_path_(
let body_owner = resolver.body_owner();
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 {
ValueNs::LocalBinding(binding_id) => {
let var = Local { parent: body_owner?, binding_id };
@@ -1039,14 +1033,14 @@ fn resolve_hir_path_(
let items = || {
resolver
- .resolve_module_path_in_items(db.upcast(), path.mod_path())
+ .resolve_module_path_in_items(db.upcast(), path.mod_path()?)
.take_types()
.map(|it| PathResolution::Def(it.into()))
};
let macros = || {
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())))
};
@@ -1074,7 +1068,7 @@ fn resolve_hir_path_qualifier(
path: &Path,
) -> Option<PathResolution> {
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 {
TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()),
@@ -1089,7 +1083,7 @@ fn resolve_hir_path_qualifier(
})
.or_else(|| {
resolver
- .resolve_module_path_in_items(db.upcast(), path.mod_path())
+ .resolve_module_path_in_items(db.upcast(), path.mod_path()?)
.take_types()
.map(|it| PathResolution::Def(it.into()))
})
diff --git a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
index 38fccb3382..2e26f59d03 100644
--- a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
+++ b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
@@ -20,7 +20,7 @@ use crate::assist_context::{AssistContext, Assists};
// Replaces a `try` expression with a `match` expression.
//
// ```
-// # //- minicore:option
+// # //- minicore: try, option
// fn handle() {
// let pat = Some(true)$0?;
// }
@@ -111,7 +111,7 @@ mod tests {
check_assist(
replace_try_expr_with_match,
r#"
-//- minicore:option
+//- minicore: try, option
fn test() {
let pat = Some(true)$0?;
}
@@ -132,7 +132,7 @@ fn test() {
check_assist(
replace_try_expr_with_match,
r#"
-//- minicore:result
+//- minicore: try, from, result
fn test() {
let pat = Ok(true)$0?;
}
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index e5a8d675a9..aff11367de 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -2352,7 +2352,7 @@ fn doctest_replace_try_expr_with_match() {
check_doc_test(
"replace_try_expr_with_match",
r#####"
-//- minicore:option
+//- minicore: try, option
fn handle() {
let pat = Some(true)$0?;
}
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 96470265d1..17a70f5701 100644
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -565,8 +565,55 @@ 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]
fn overloaded_deref() {
- // FIXME: check for false negative
check_diagnostics(
r#"
//- minicore: deref_mut
@@ -574,22 +621,36 @@ use core::ops::{Deref, DerefMut};
struct Foo;
impl Deref for Foo {
- type Target = i32;
- fn deref(&self) -> &i32 {
- &5
+ type Target = (i32, u8);
+ fn deref(&self) -> &(i32, u8) {
+ &(5, 2)
}
}
impl DerefMut for Foo {
- fn deref_mut(&mut self) -> &mut i32 {
- &mut 5
+ fn deref_mut(&mut self) -> &mut (i32, u8) {
+ &mut (5, 2)
}
}
fn f() {
- let x = Foo;
+ let mut x = Foo;
+ //^^^^^ 💡 weak: variable does not need to be mutable
let y = &*x;
let x = Foo;
- let mut x = Foo;
- let y: &mut i32 = &mut x;
+ let y = &mut *x;
+ //^^ 💡 error: cannot mutate immutable variable `x`
+ let x = Foo;
+ let x = Foo;
+ let y: &mut (i32, u8) = &mut x;
+ //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let ref mut y = *x;
+ //^^ 💡 error: cannot mutate immutable variable `x`
+ let (ref mut y, _) = *x;
+ //^^ 💡 error: cannot mutate immutable variable `x`
+ match *x {
+ //^^ 💡 error: cannot mutate immutable variable `x`
+ (ref y, _) => (),
+ (_, ref mut y) => (),
+ }
}
"#,
);
@@ -632,6 +693,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]
fn respect_allow_unused_mut() {
// FIXME: respect
check_diagnostics(
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index da725ce502..fb7b15e05d 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -409,7 +409,6 @@ pub(super) fn definition(
}
match it.eval(db) {
Ok(()) => Some("pass".into()),
- Err(MirEvalError::Panic) => Some("fail".into()),
Err(MirEvalError::MirLowerError(f, e)) => {
let name = &db.function_data(f).name;
Some(format!("error: fail to lower {name} due {e:?}"))
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 57bf0f9ad5..70ec915e96 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -5009,7 +5009,7 @@ fn foo() {
fn hover_try_expr_res() {
check_hover_range(
r#"
-//- minicore:result
+//- minicore: try, from, result
struct FooError;
fn foo() -> Result<(), FooError> {
@@ -5023,7 +5023,7 @@ fn foo() -> Result<(), FooError> {
);
check_hover_range(
r#"
-//- minicore:result
+//- minicore: try, from, result
struct FooError;
struct BarError;
@@ -5044,6 +5044,7 @@ fn foo() -> Result<(), FooError> {
fn hover_try_expr() {
check_hover_range(
r#"
+//- minicore: try
struct NotResult<T, U>(T, U);
struct Short;
struct Looooong;
@@ -5061,6 +5062,7 @@ fn foo() -> NotResult<(), Looooong> {
);
check_hover_range(
r#"
+//- minicore: try
struct NotResult<T, U>(T, U);
struct Short;
struct Looooong;
@@ -5092,7 +5094,7 @@ fn foo() -> Option<()> {
"#,
expect![[r#"
```rust
- <Option<i32> as Try>::Output
+ i32
```"#]],
);
}
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index 1e1771259b..11e6dc05fa 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -435,7 +435,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 3415..3423,
+ range: 5805..5813,
},
),
tooltip: "",
@@ -448,7 +448,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 3447..3451,
+ range: 5837..5841,
},
),
tooltip: "",
@@ -468,7 +468,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 3415..3423,
+ range: 5805..5813,
},
),
tooltip: "",
@@ -481,7 +481,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 3447..3451,
+ range: 5837..5841,
},
),
tooltip: "",
@@ -501,7 +501,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 3415..3423,
+ range: 5805..5813,
},
),
tooltip: "",
@@ -514,7 +514,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 3447..3451,
+ range: 5837..5841,
},
),
tooltip: "",
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index ac9bd8e39d..5cc3bad04b 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -1126,5 +1126,5 @@ fn benchmark_syntax_highlighting_parser() {
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
.count()
};
- assert_eq!(hash, 1608);
+ assert_eq!(hash, 1170);
}
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 6ce1de5d32..e1504743bf 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -2,6 +2,7 @@
//! errors.
use std::{
+ collections::HashMap,
env,
time::{SystemTime, UNIX_EPOCH},
};
@@ -153,6 +154,10 @@ impl flags::AnalysisStats {
self.run_inference(&host, db, &vfs, &funcs, verbosity);
}
+ if self.mir_stats {
+ self.lower_mir(db, &funcs);
+ }
+
let total_span = analysis_sw.elapsed();
eprintln!("{:<20} {total_span}", "Total:");
report_metric("total time", total_span.time.as_millis() as u64, "ms");
@@ -189,6 +194,24 @@ impl flags::AnalysisStats {
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(
&self,
host: &AnalysisHost,
diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs
index 770612cc94..b085a0a892 100644
--- a/crates/rust-analyzer/src/cli/flags.rs
+++ b/crates/rust-analyzer/src/cli/flags.rs
@@ -66,6 +66,8 @@ xflags::xflags! {
optional --memory-usage
/// Print the total length of all source and macro files (whitespace is not counted).
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.
optional -o, --only path: String
@@ -172,6 +174,7 @@ pub struct AnalysisStats {
pub parallel: bool,
pub memory_usage: bool,
pub source_stats: bool,
+ pub mir_stats: bool,
pub only: Option<String>,
pub with_deps: bool,
pub no_sysroot: bool,
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 548b5ba8b8..1c15a606f9 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -613,7 +613,7 @@ Pat =
| ConstBlockPat
LiteralPat =
- Literal
+ '-'? Literal
IdentPat =
Attr* 'ref'? 'mut'? Name ('@' Pat)?
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index fe32484536..0e84aca5c7 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -1375,6 +1375,7 @@ pub struct LiteralPat {
pub(crate) syntax: SyntaxNode,
}
impl LiteralPat {
+ pub fn minus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![-]) }
pub fn literal(&self) -> Option<Literal> { support::child(&self.syntax) }
}
diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs
index e954b58251..77a8363a18 100644
--- a/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/crates/syntax/src/tests/sourcegen_ast.rs
@@ -535,6 +535,7 @@ impl Field {
"!" => "excl",
"*" => "star",
"&" => "amp",
+ "-" => "minus",
"_" => "underscore",
"." => "dot",
".." => "dotdot",
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index ca6de4061a..118b9ad631 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -181,7 +181,7 @@ pub mod convert {
}
// endregion:as_ref
// region:infallible
- pub enum Infallibe {}
+ pub enum Infallible {}
// endregion:infallible
}
@@ -375,16 +375,82 @@ pub mod ops {
type Output;
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
+
+ mod impls {
+ use crate::marker::Tuple;
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const Fn<A> for &F
+ where
+ F: ~const Fn<A>,
+ {
+ extern "rust-call" fn call(&self, args: A) -> F::Output {
+ (**self).call(args)
+ }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const FnMut<A> for &F
+ where
+ F: ~const Fn<A>,
+ {
+ extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output {
+ (**self).call(args)
+ }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const FnOnce<A> for &F
+ where
+ F: ~const Fn<A>,
+ {
+ type Output = F::Output;
+
+ extern "rust-call" fn call_once(self, args: A) -> F::Output {
+ (*self).call(args)
+ }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const FnMut<A> for &mut F
+ where
+ F: ~const FnMut<A>,
+ {
+ extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output {
+ (*self).call_mut(args)
+ }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const FnOnce<A> for &mut F
+ where
+ F: ~const FnMut<A>,
+ {
+ type Output = F::Output;
+ extern "rust-call" fn call_once(self, args: A) -> F::Output {
+ (*self).call_mut(args)
+ }
+ }
+ }
}
pub use self::function::{Fn, FnMut, FnOnce};
// endregion:fn
// region:try
mod try_ {
+ use super::super::convert::Infallible;
+
pub enum ControlFlow<B, C = ()> {
+ #[lang = "Continue"]
Continue(C),
+ #[lang = "Break"]
Break(B),
}
- pub trait FromResidual<R = Self::Residual> {
+ pub trait FromResidual<R = <Self as Try>::Residual> {
#[lang = "from_residual"]
fn from_residual(residual: R) -> Self;
}
@@ -400,14 +466,66 @@ pub mod ops {
impl<B, C> Try for ControlFlow<B, C> {
type Output = C;
- type Residual = ControlFlow<B, convert::Infallible>;
+ type Residual = ControlFlow<B, Infallible>;
fn from_output(output: Self::Output) -> Self {}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {}
}
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};
// endregion:try
@@ -541,7 +659,10 @@ pub mod option {
loop {}
}
pub fn unwrap_or(self, default: T) -> T {
- loop {}
+ match self {
+ Some(val) => val,
+ None => default,
+ }
}
// region:fn
pub fn and_then<U, F>(self, f: F) -> Option<U>
diff --git a/lib/la-arena/src/lib.rs b/lib/la-arena/src/lib.rs
index ccaaf39917..f6597efd8f 100644
--- a/lib/la-arena/src/lib.rs
+++ b/lib/la-arena/src/lib.rs
@@ -295,7 +295,7 @@ impl<T> Arena<T> {
/// ```
pub fn iter(
&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))
}