Merge branch 'master' into alias-based-completion2

This commit is contained in:
hecatia-elegua 2023-04-11 21:14:52 +02:00 committed by GitHub
commit 398af0259f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
172 changed files with 5165 additions and 5934 deletions

View File

@ -24,6 +24,9 @@ jobs:
runs-on: ${{ matrix.os }}
env:
CC: deny_c
# we want to build r-a on stable to check that it keeps building on stable,
# but we also want to test our proc-macro-srv which depends on nightly features
RUSTC_BOOTSTRAP: 1
strategy:
fail-fast: false
@ -50,15 +53,15 @@ jobs:
run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml
- name: Compile (tests)
run: cargo test --no-run --locked
run: cargo test --no-run --locked --features sysroot-abi
# It's faster to `test` before `build` ¯\_(ツ)_/¯
- name: Compile (rust-analyzer)
if: matrix.os == 'ubuntu-latest'
run: cargo build --quiet
run: cargo build --quiet --features sysroot-abi
- name: Test
run: cargo test -- --nocapture --quiet
run: cargo test --features sysroot-abi -- --nocapture --quiet
- name: Run analysis-stats on rust-analyzer
if: matrix.os == 'ubuntu-latest'

7
Cargo.lock generated
View File

@ -1250,6 +1250,7 @@ dependencies = [
name = "proc-macro-srv-cli"
version = "0.0.0"
dependencies = [
"proc-macro-api",
"proc-macro-srv",
]
@ -1457,7 +1458,7 @@ dependencies = [
"parking_lot 0.12.1",
"parking_lot_core 0.9.6",
"proc-macro-api",
"proc-macro-srv",
"proc-macro-srv-cli",
"profile",
"project-model",
"rayon",
@ -1641,9 +1642,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "smol_str"
version = "0.1.25"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d268d24892b932cac466d214af6ec8a3ec99873f0f8664d9a384b49596db682"
checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c"
dependencies = [
"serde",
]

View File

@ -77,6 +77,7 @@ vfs = { path = "./crates/vfs", version = "0.0.0" }
# non-local crates
smallvec = { version = "1.10.0", features = ["const_new", "union", "const_generics"] }
smol_str = "0.2.0"
# the following crates are pinned to prevent us from pulling in syn 2 until all our dependencies have moved
serde = { version = "=1.0.156", features = ["derive"] }
serde_json = "1.0.94"

View File

@ -4,7 +4,8 @@ use std::{mem, str::FromStr, sync::Arc};
use cfg::CfgOptions;
use rustc_hash::FxHashMap;
use test_utils::{
extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER,
extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER,
ESCAPED_CURSOR_MARKER,
};
use tt::token_id::{Leaf, Subtree, TokenTree};
use vfs::{file_set::FileSet, VfsPath};
@ -12,7 +13,7 @@ use vfs::{file_set::FileSet, VfsPath};
use crate::{
input::{CrateName, CrateOrigin, LangCrateOrigin},
Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition,
FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros,
FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, ReleaseChannel,
SourceDatabaseExt, SourceRoot, SourceRootId,
};
@ -102,7 +103,14 @@ impl ChangeFixture {
ra_fixture: &str,
mut proc_macro_defs: Vec<(String, ProcMacro)>,
) -> ChangeFixture {
let (mini_core, proc_macro_names, fixture) = Fixture::parse(ra_fixture);
let FixtureWithProjectMeta { fixture, mini_core, proc_macro_names, toolchain } =
FixtureWithProjectMeta::parse(ra_fixture);
let toolchain = toolchain
.map(|it| {
ReleaseChannel::from_str(&it)
.unwrap_or_else(|| panic!("unknown release channel found: {it}"))
})
.unwrap_or(ReleaseChannel::Stable);
let mut change = Change::new();
let mut files = Vec::new();
@ -166,7 +174,7 @@ impl ChangeFixture {
.as_deref()
.map(Arc::from)
.ok_or_else(|| "target_data_layout unset".into()),
None,
Some(toolchain),
);
let prev = crates.insert(crate_name.clone(), crate_id);
assert!(prev.is_none());
@ -205,7 +213,7 @@ impl ChangeFixture {
default_target_data_layout
.map(|x| x.into())
.ok_or_else(|| "target_data_layout unset".into()),
None,
Some(toolchain),
);
} else {
for (from, to, prelude) in crate_deps {
@ -247,7 +255,7 @@ impl ChangeFixture {
false,
CrateOrigin::Lang(LangCrateOrigin::Core),
target_layout.clone(),
None,
Some(toolchain),
);
for krate in all_crates {
@ -286,7 +294,7 @@ impl ChangeFixture {
true,
CrateOrigin::Local { repo: None, name: None },
target_layout,
None,
Some(toolchain),
);
proc_macros.insert(proc_macros_crate, Ok(proc_macro));

View File

@ -1,5 +1,7 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
pub mod builtin;
#[cfg(test)]
mod tests;
@ -267,6 +269,10 @@ impl Attrs {
pub fn is_proc_macro_derive(&self) -> bool {
self.by_key("proc_macro_derive").exists()
}
pub fn is_unstable(&self) -> bool {
self.by_key("unstable").exists()
}
}
use std::slice::Iter as SliceIter;

View File

@ -13,10 +13,12 @@ use cfg::{CfgExpr, CfgOptions};
use drop_bomb::DropBomb;
use either::Either;
use hir_expand::{
attrs::RawAttrs, hygiene::Hygiene, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId,
ast_id_map::AstIdMap, attrs::RawAttrs, hygiene::Hygiene, name::Name, AstId, ExpandError,
ExpandResult, HirFileId, InFile, MacroCallId,
};
use la_arena::{Arena, ArenaMap};
use limit::Limit;
use once_cell::unsync::OnceCell;
use profile::Count;
use rustc_hash::FxHashMap;
use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
@ -24,7 +26,7 @@ use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
use crate::{
attr::Attrs,
db::DefDatabase,
expr::{
hir::{
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
},
item_scope::BuiltinShadowMode,
@ -36,7 +38,43 @@ use crate::{
UnresolvedMacro,
};
pub use lower::LowerCtx;
pub struct LowerCtx<'a> {
pub db: &'a dyn DefDatabase,
hygiene: Hygiene,
ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
}
impl<'a> LowerCtx<'a> {
pub fn new(db: &'a dyn DefDatabase, hygiene: &Hygiene, file_id: HirFileId) -> Self {
LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: Some((file_id, OnceCell::new())) }
}
pub fn with_file_id(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
LowerCtx {
db,
hygiene: Hygiene::new(db.upcast(), file_id),
ast_id_map: Some((file_id, OnceCell::new())),
}
}
pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
}
pub(crate) fn hygiene(&self) -> &Hygiene {
&self.hygiene
}
pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
Path::from_src(ast, self)
}
pub(crate) fn ast_id<N: syntax::AstNode>(&self, item: &N) -> Option<AstId<N>> {
let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
Some(InFile::new(file_id, ast_id_map.ast_id(item)))
}
}
/// A subset of Expander that only deals with cfg attributes. We only need it to
/// avoid cyclic queries in crate def map during enum processing.
@ -76,6 +114,10 @@ impl CfgExpander {
let attrs = self.parse_attrs(db, owner);
attrs.is_cfg_enabled(&self.cfg_options)
}
pub(crate) fn hygiene(&self) -> &Hygiene {
&self.hygiene
}
}
impl Expander {
@ -180,6 +222,10 @@ impl Expander {
mark.bomb.defuse();
}
pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> {
LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id)
}
pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
InFile { file_id: self.current_file_id, value }
}
@ -232,7 +278,7 @@ impl Expander {
// The overflow error should have been reported when it occurred (see the next branch),
// so don't return overflow error here to avoid diagnostics duplication.
cov_mark::hit!(overflow_but_not_me);
return ExpandResult::only_err(ExpandError::RecursionOverflowPosioned);
return ExpandResult::only_err(ExpandError::RecursionOverflowPoisoned);
} else if self.recursion_limit(db).check(self.recursion_depth + 1).is_err() {
self.recursion_depth = usize::MAX;
cov_mark::hit!(your_stack_belongs_to_me);
@ -343,6 +389,8 @@ pub enum BodyDiagnostic {
MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId },
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
}
impl Body {

View File

@ -7,77 +7,38 @@ use base_db::CrateId;
use either::Either;
use hir_expand::{
ast_id_map::AstIdMap,
hygiene::Hygiene,
name::{name, AsName, Name},
AstId, ExpandError, HirFileId, InFile,
AstId, ExpandError, InFile,
};
use intern::Interned;
use la_arena::Arena;
use once_cell::unsync::OnceCell;
use profile::Count;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use syntax::{
ast::{
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName, LiteralKind,
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName,
SlicePatComponents,
},
AstNode, AstPtr, SyntaxNodePtr,
};
use crate::{
adt::StructKind,
body::{Body, BodySourceMap, Expander, ExprPtr, LabelPtr, LabelSource, PatPtr},
body::{BodyDiagnostic, ExprSource, PatSource},
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
body::{Body, BodyDiagnostic, BodySourceMap, Expander, ExprPtr, LabelPtr, LowerCtx, PatPtr},
data::adt::StructKind,
db::DefDatabase,
expr::{
dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId,
FloatTypeWrapper, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId,
RecordFieldPat, RecordLitField, Statement,
hir::{
dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Expr,
ExprId, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat,
RecordLitField, Statement,
},
item_scope::BuiltinShadowMode,
item_tree::ItemTree,
lang_item::LangItem,
path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef},
AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
};
pub struct LowerCtx<'a> {
pub db: &'a dyn DefDatabase,
hygiene: Hygiene,
ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
}
impl<'a> LowerCtx<'a> {
pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
LowerCtx {
db,
hygiene: Hygiene::new(db.upcast(), file_id),
ast_id_map: Some((file_id, OnceCell::new())),
}
}
pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
}
pub(crate) fn hygiene(&self) -> &Hygiene {
&self.hygiene
}
pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
Path::from_src(ast, self)
}
pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<AstId<N>> {
let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
Some(InFile::new(file_id, ast_id_map.ast_id(item)))
}
}
pub(super) fn lower(
db: &dyn DefDatabase,
expander: Expander,
@ -102,9 +63,11 @@ pub(super) fn lower(
_c: Count::new(),
},
expander,
current_try_block: None,
current_try_block_label: None,
is_lowering_assignee_expr: false,
is_lowering_generator: false,
label_ribs: Vec::new(),
current_binding_owner: None,
}
.collect(params, body, is_async_fn)
}
@ -113,12 +76,57 @@ struct ExprCollector<'a> {
db: &'a dyn DefDatabase,
expander: Expander,
ast_id_map: Arc<AstIdMap>,
body: Body,
krate: CrateId,
body: Body,
source_map: BodySourceMap,
current_try_block: Option<LabelId>,
is_lowering_assignee_expr: bool,
is_lowering_generator: bool,
current_try_block_label: Option<LabelId>,
// points to the expression that a try expression will target (replaces current_try_block_label)
// catch_scope: Option<ExprId>,
// points to the expression that an unlabeled control flow will target
// loop_scope: Option<ExprId>,
// needed to diagnose non label control flow in while conditions
// is_in_loop_condition: bool,
// resolution
label_ribs: Vec<LabelRib>,
current_binding_owner: Option<ExprId>,
}
#[derive(Clone, Debug)]
struct LabelRib {
kind: RibKind,
// Once we handle macro hygiene this will need to be a map
label: Option<(Name, LabelId)>,
}
impl LabelRib {
fn new(kind: RibKind) -> Self {
LabelRib { kind, label: None }
}
fn new_normal(label: (Name, LabelId)) -> Self {
LabelRib { kind: RibKind::Normal, label: Some(label) }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum RibKind {
Normal,
Closure,
Constant,
}
impl RibKind {
/// This rib forbids referring to labels defined in upwards ribs.
fn is_label_barrier(self) -> bool {
match self {
RibKind::Normal => false,
RibKind::Closure | RibKind::Constant => true,
}
}
}
#[derive(Debug, Default)]
@ -162,97 +170,35 @@ impl ExprCollector<'_> {
self.body.params.push(param_pat);
}
for pat in param_list
.params()
.zip(attr_enabled)
.filter_map(|(param, enabled)| param.pat().filter(|_| enabled))
for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
{
let param_pat = self.collect_pat(pat);
let param_pat = self.collect_pat_top(param.pat());
self.body.params.push(param_pat);
}
};
self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
if is_async_fn {
match body {
Some(e) => {
let expr = this.collect_expr(e);
this.alloc_expr_desugared(Expr::Async {
id: None,
statements: Box::new([]),
tail: Some(expr),
})
}
None => this.missing_expr(),
}
} else {
this.collect_expr_opt(body)
}
});
self.body.body_expr = if is_async_fn {
self.current_try_block =
Some(self.alloc_label_desugared(Label { name: Name::generate_new_name() }));
let expr = self.collect_expr_opt(body);
let expr = self.alloc_expr_desugared(Expr::Block {
id: None,
statements: Box::new([]),
tail: Some(expr),
label: self.current_try_block,
});
let expr = self.alloc_expr_desugared(Expr::Async {
id: None,
statements: Box::new([]),
tail: Some(expr),
});
expr
} else {
self.collect_expr_opt(body)
};
(self.body, self.source_map)
}
fn ctx(&self) -> LowerCtx<'_> {
LowerCtx::new(self.db, self.expander.current_file_id)
}
fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
let src = self.expander.to_source(ptr);
let id = self.make_expr(expr, src.clone());
self.source_map.expr_map.insert(src, id);
id
}
// 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)
}
fn missing_expr(&mut self) -> ExprId {
self.alloc_expr_desugared(Expr::Missing)
}
fn make_expr(&mut self, expr: Expr, src: ExprSource) -> ExprId {
let id = self.body.exprs.alloc(expr);
self.source_map.expr_map_back.insert(id, src);
id
}
fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
self.body.bindings.alloc(Binding { name, mode, definitions: SmallVec::new() })
}
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
let src = self.expander.to_source(ptr);
let id = self.make_pat(pat, src.clone());
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)
}
fn make_pat(&mut self, pat: Pat, src: PatSource) -> PatId {
let id = self.body.pats.alloc(pat);
self.source_map.pat_map_back.insert(id, src);
id
}
fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
let src = self.expander.to_source(ptr);
let id = self.make_label(label, src.clone());
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);
id
self.expander.ctx(self.db)
}
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@ -264,6 +210,7 @@ impl ExprCollector<'_> {
let syntax_ptr = AstPtr::new(&expr);
self.check_cfg(&expr)?;
// FIXME: Move some of these arms out into separate methods for clarity
Some(match expr {
ast::Expr::IfExpr(e) => {
let then_branch = self.collect_block_opt(e.then_branch());
@ -281,12 +228,12 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
}
ast::Expr::LetExpr(e) => {
let pat = self.collect_pat_opt(e.pat());
let pat = self.collect_pat_top(e.pat());
let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
}
ast::Expr::BlockExpr(e) => match e.modifier() {
Some(ast::BlockModifier::Try(_)) => self.collect_try_block(e),
Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e),
Some(ast::BlockModifier::Unsafe(_)) => {
self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
id,
@ -296,28 +243,48 @@ impl ExprCollector<'_> {
}
Some(ast::BlockModifier::Label(label)) => {
let label = self.collect_label(label);
self.collect_block_(e, |id, statements, tail| Expr::Block {
id,
statements,
tail,
label: Some(label),
self.with_labeled_rib(label, |this| {
this.collect_block_(e, |id, statements, tail| Expr::Block {
id,
statements,
tail,
label: Some(label),
})
})
}
Some(ast::BlockModifier::Async(_)) => {
self.with_label_rib(RibKind::Closure, |this| {
this.collect_block_(e, |id, statements, tail| Expr::Async {
id,
statements,
tail,
})
})
}
Some(ast::BlockModifier::Const(_)) => {
self.with_label_rib(RibKind::Constant, |this| {
this.collect_as_a_binding_owner_bad(
|this| {
this.collect_block_(e, |id, statements, tail| Expr::Const {
id,
statements,
tail,
})
},
syntax_ptr,
)
})
}
Some(ast::BlockModifier::Async(_)) => self
.collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }),
Some(ast::BlockModifier::Const(_)) => self
.collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }),
None => self.collect_block(e),
},
ast::Expr::LoopExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
let body = self.collect_block_opt(e.loop_body());
let body = self.collect_labelled_block_opt(label, e.loop_body());
self.alloc_expr(Expr::Loop { body, label }, syntax_ptr)
}
ast::Expr::WhileExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
let body = self.collect_block_opt(e.loop_body());
let body = self.collect_labelled_block_opt(label, e.loop_body());
let condition = self.collect_expr_opt(e.condition());
self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
@ -325,8 +292,8 @@ impl ExprCollector<'_> {
ast::Expr::ForExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
let iterable = self.collect_expr_opt(e.iterable());
let pat = self.collect_pat_opt(e.pat());
let body = self.collect_block_opt(e.loop_body());
let pat = self.collect_pat_top(e.pat());
let body = self.collect_labelled_block_opt(label, e.loop_body());
self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
}
ast::Expr::CallExpr(e) => {
@ -365,7 +332,7 @@ impl ExprCollector<'_> {
.arms()
.filter_map(|arm| {
self.check_cfg(&arm).map(|()| MatchArm {
pat: self.collect_pat_opt(arm.pat()),
pat: self.collect_pat_top(arm.pat()),
expr: self.collect_expr_opt(arm.expr()),
guard: arm
.guard()
@ -386,16 +353,20 @@ impl ExprCollector<'_> {
.unwrap_or(Expr::Missing);
self.alloc_expr(path, syntax_ptr)
}
ast::Expr::ContinueExpr(e) => self.alloc_expr(
Expr::Continue { label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
syntax_ptr,
),
ast::Expr::ContinueExpr(e) => {
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
self.source_map.diagnostics.push(e);
None
});
self.alloc_expr(Expr::Continue { label }, syntax_ptr)
}
ast::Expr::BreakExpr(e) => {
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
self.source_map.diagnostics.push(e);
None
});
let expr = e.expr().map(|e| self.collect_expr(e));
self.alloc_expr(
Expr::Break { expr, label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
syntax_ptr,
)
self.alloc_expr(Expr::Break { expr, label }, syntax_ptr)
}
ast::Expr::ParenExpr(e) => {
let inner = self.collect_expr_opt(e.expr());
@ -496,14 +467,16 @@ impl ExprCollector<'_> {
None => self.alloc_expr(Expr::Missing, syntax_ptr),
}
}
ast::Expr::ClosureExpr(e) => {
ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
let (result_expr_id, prev_binding_owner) =
this.initialize_binding_owner(syntax_ptr);
let mut args = Vec::new();
let mut arg_types = Vec::new();
if let Some(pl) = e.param_list() {
for param in pl.params() {
let pat = self.collect_pat_opt(param.pat());
let pat = this.collect_pat_top(param.pat());
let type_ref =
param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
param.ty().map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
args.push(pat);
arg_types.push(type_ref);
}
@ -511,14 +484,13 @@ impl ExprCollector<'_> {
let ret_type = e
.ret_type()
.and_then(|r| r.ty())
.map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
.map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
let prev_is_lowering_generator = self.is_lowering_generator;
self.is_lowering_generator = false;
let prev_is_lowering_generator = mem::take(&mut this.is_lowering_generator);
let body = self.collect_expr_opt(e.body());
let body = this.collect_expr_opt(e.body());
let closure_kind = if self.is_lowering_generator {
let closure_kind = if this.is_lowering_generator {
let movability = if e.static_token().is_some() {
Movability::Static
} else {
@ -530,19 +502,21 @@ impl ExprCollector<'_> {
} else {
ClosureKind::Closure
};
self.is_lowering_generator = prev_is_lowering_generator;
self.alloc_expr(
Expr::Closure {
args: args.into(),
arg_types: arg_types.into(),
ret_type,
body,
closure_kind,
},
syntax_ptr,
)
}
this.is_lowering_generator = prev_is_lowering_generator;
let capture_by =
if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
this.is_lowering_generator = prev_is_lowering_generator;
this.current_binding_owner = prev_binding_owner;
this.body.exprs[result_expr_id] = Expr::Closure {
args: args.into(),
arg_types: arg_types.into(),
ret_type,
body,
closure_kind,
capture_by,
};
result_expr_id
}),
ast::Expr::BinExpr(e) => {
let op = e.op_kind();
if let Some(ast::BinaryOp::Assignment { op: None }) = op {
@ -581,7 +555,17 @@ impl ExprCollector<'_> {
}
ArrayExprKind::Repeat { initializer, repeat } => {
let initializer = self.collect_expr_opt(initializer);
let repeat = self.collect_expr_opt(repeat);
let repeat = self.with_label_rib(RibKind::Constant, |this| {
if let Some(repeat) = repeat {
let syntax_ptr = AstPtr::new(&repeat);
this.collect_as_a_binding_owner_bad(
|this| this.collect_expr(repeat),
syntax_ptr,
)
} else {
this.missing_expr()
}
});
self.alloc_expr(
Expr::Array(Array::Repeat { initializer, repeat }),
syntax_ptr,
@ -627,23 +611,53 @@ impl ExprCollector<'_> {
})
}
fn initialize_binding_owner(
&mut self,
syntax_ptr: AstPtr<ast::Expr>,
) -> (ExprId, Option<ExprId>) {
let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
let prev_binding_owner = self.current_binding_owner.take();
self.current_binding_owner = Some(result_expr_id);
(result_expr_id, prev_binding_owner)
}
/// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently
/// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have
/// their own body. Don't add more usage for this function so that we can remove this function after
/// separating those bodies.
fn collect_as_a_binding_owner_bad(
&mut self,
job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId,
syntax_ptr: AstPtr<ast::Expr>,
) -> ExprId {
let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
let tmp = job(self);
self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing);
self.current_binding_owner = prev_owner;
id
}
/// 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 {
fn desugar_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);
return self.collect_block(e);
};
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 label = self.alloc_label_desugared(Label { name: Name::generate_new_name() });
let old_label = self.current_try_block_label.replace(label);
let (btail, expr_id) = self.with_labeled_rib(label, |this| {
let mut btail = None;
let block = this.collect_block_(e, |id, statements, tail| {
btail = tail;
Expr::Block { id, statements, tail, label: Some(label) }
});
(btail, block)
});
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 {
let next_tail = match btail {
Some(tail) => self.alloc_expr_desugared(Expr::Call {
callee,
args: Box::new([tail]),
@ -662,10 +676,10 @@ impl ExprCollector<'_> {
}
};
let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
unreachable!("It is the output of collect block");
unreachable!("block was lowered to non-block");
};
*tail = Some(next_tail);
self.current_try_block = prev_try_block;
self.current_try_block_label = old_label;
expr_id
}
@ -735,12 +749,13 @@ impl ExprCollector<'_> {
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())
}
self.alloc_expr(
match self.current_try_block_label {
Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
None => Expr::Return { expr: Some(result) },
},
syntax_ptr.clone(),
)
},
};
let arms = Box::new([continue_arm, break_arm]);
@ -785,7 +800,7 @@ impl ExprCollector<'_> {
krate: *krate,
});
}
Some(ExpandError::RecursionOverflowPosioned) => {
Some(ExpandError::RecursionOverflowPoisoned) => {
// Recursion limit has been reached in the macro expansion tree, but not in
// this very macro call. Don't add diagnostics to avoid duplication.
}
@ -866,7 +881,7 @@ impl ExprCollector<'_> {
if self.check_cfg(&stmt).is_none() {
return;
}
let pat = self.collect_pat_opt(stmt.pat());
let pat = self.collect_pat_top(stmt.pat());
let type_ref =
stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
@ -911,10 +926,20 @@ impl ExprCollector<'_> {
block: ast::BlockExpr,
mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
) -> ExprId {
let file_local_id = self.ast_id_map.ast_id(&block);
let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
let block_has_items = {
let statement_has_item = block.statements().any(|stmt| match stmt {
ast::Stmt::Item(_) => true,
// Macro calls can be both items and expressions. The syntax library always treats
// them as expressions here, so we undo that.
ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))),
_ => false,
});
statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
};
let block_id = if ItemTree::block_has_items(self.db, ast_id.file_id, &block) {
let block_id = if block_has_items {
let file_local_id = self.ast_id_map.ast_id(&block);
let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
Some(self.db.intern_block(BlockLoc {
ast_id,
module: self.expander.def_map.module_id(self.expander.module),
@ -966,32 +991,34 @@ impl ExprCollector<'_> {
}
}
fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
let label = Label {
name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
};
self.alloc_label(label, AstPtr::new(&ast_label))
fn collect_labelled_block_opt(
&mut self,
label: Option<LabelId>,
expr: Option<ast::BlockExpr>,
) -> ExprId {
match label {
Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)),
None => self.collect_block_opt(expr),
}
}
fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
self.collect_pat_(pat, &mut BindingList::default())
}
// region: patterns
fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
fn collect_pat_top(&mut self, pat: Option<ast::Pat>) -> PatId {
match pat {
Some(pat) => self.collect_pat(pat),
Some(pat) => self.collect_pat(pat, &mut BindingList::default()),
None => self.missing_pat(),
}
}
fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
let pattern = match &pat {
ast::Pat::IdentPat(bp) => {
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
let annotation =
BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat, binding_list));
let subpat = bp.pat().map(|subpat| self.collect_pat(subpat, binding_list));
let is_simple_ident_pat =
annotation == BindingAnnotation::Unannotated && subpat.is_none();
@ -1045,7 +1072,7 @@ impl ExprCollector<'_> {
Pat::TupleStruct { path, args, ellipsis }
}
ast::Pat::RefPat(p) => {
let pat = self.collect_pat_opt_(p.pat(), binding_list);
let pat = self.collect_pat_opt(p.pat(), binding_list);
let mutability = Mutability::from_mutable(p.mut_token().is_some());
Pat::Ref { pat, mutability }
}
@ -1055,10 +1082,10 @@ impl ExprCollector<'_> {
path.map(Pat::Path).unwrap_or(Pat::Missing)
}
ast::Pat::OrPat(p) => {
let pats = p.pats().map(|p| self.collect_pat_(p, binding_list)).collect();
let pats = p.pats().map(|p| self.collect_pat(p, binding_list)).collect();
Pat::Or(pats)
}
ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat(), binding_list),
ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
ast::Pat::TuplePat(p) => {
let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
Pat::Tuple { args, ellipsis }
@ -1073,7 +1100,7 @@ impl ExprCollector<'_> {
.fields()
.filter_map(|f| {
let ast_pat = f.pat()?;
let pat = self.collect_pat_(ast_pat, binding_list);
let pat = self.collect_pat(ast_pat, binding_list);
let name = f.field_name()?.as_name();
Some(RecordFieldPat { name, pat })
})
@ -1092,15 +1119,9 @@ impl ExprCollector<'_> {
// FIXME properly handle `RestPat`
Pat::Slice {
prefix: prefix
.into_iter()
.map(|p| self.collect_pat_(p, binding_list))
.collect(),
slice: slice.map(|p| self.collect_pat_(p, binding_list)),
suffix: suffix
.into_iter()
.map(|p| self.collect_pat_(p, binding_list))
.collect(),
prefix: prefix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
slice: slice.map(|p| self.collect_pat(p, binding_list)),
suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
}
}
#[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676
@ -1131,12 +1152,18 @@ impl ExprCollector<'_> {
Pat::Missing
}
ast::Pat::BoxPat(boxpat) => {
let inner = self.collect_pat_opt_(boxpat.pat(), binding_list);
let inner = self.collect_pat_opt(boxpat.pat(), binding_list);
Pat::Box { inner }
}
ast::Pat::ConstBlockPat(const_block_pat) => {
if let Some(expr) = const_block_pat.block_expr() {
let expr_id = self.collect_block(expr);
if let Some(block) = const_block_pat.block_expr() {
let expr_id = self.with_label_rib(RibKind::Constant, |this| {
let syntax_ptr = AstPtr::new(&block.clone().into());
this.collect_as_a_binding_owner_bad(
|this| this.collect_block(block),
syntax_ptr,
)
});
Pat::ConstBlock(expr_id)
} else {
Pat::Missing
@ -1148,7 +1175,7 @@ impl ExprCollector<'_> {
let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
let pat =
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
this.collect_pat_opt_(expanded_pat, binding_list)
this.collect_pat_opt(expanded_pat, binding_list)
});
self.source_map.pat_map.insert(src, pat);
return pat;
@ -1162,9 +1189,9 @@ impl ExprCollector<'_> {
self.alloc_pat(pattern, Either::Left(ptr))
}
fn collect_pat_opt_(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
fn collect_pat_opt(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
match pat {
Some(pat) => self.collect_pat_(pat, binding_list),
Some(pat) => self.collect_pat(pat, binding_list),
None => self.missing_pat(),
}
}
@ -1180,12 +1207,14 @@ impl ExprCollector<'_> {
// We want to skip the `..` pattern here, since we account for it above.
let args = args
.filter(|p| !matches!(p, ast::Pat::RestPat(_)))
.map(|p| self.collect_pat_(p, binding_list))
.map(|p| self.collect_pat(p, binding_list))
.collect();
(args, ellipsis)
}
// endregion: patterns
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
/// not.
fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> {
@ -1213,42 +1242,118 @@ impl ExprCollector<'_> {
fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
self.body.bindings[binding_id].definitions.push(pat_id);
}
}
impl From<ast::LiteralKind> for Literal {
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
match ast_lit_kind {
// FIXME: these should have actual values filled in, but unsure on perf impact
LiteralKind::IntNumber(lit) => {
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
Literal::Float(
FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
builtin,
)
} 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)
// region: labels
fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
let label = Label {
name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
};
self.alloc_label(label, AstPtr::new(&ast_label))
}
fn resolve_label(
&self,
lifetime: Option<ast::Lifetime>,
) -> Result<Option<LabelId>, BodyDiagnostic> {
let Some(lifetime) = lifetime else {
return Ok(None)
};
let name = Name::new_lifetime(&lifetime);
for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
if let Some((label_name, id)) = &rib.label {
if *label_name == name {
return if self.is_label_valid_from_rib(rib_idx) {
Ok(Some(*id))
} else {
Err(BodyDiagnostic::UnreachableLabel {
name,
node: InFile::new(
self.expander.current_file_id,
AstPtr::new(&lifetime),
),
})
};
}
}
LiteralKind::FloatNumber(lit) => {
let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
}
LiteralKind::ByteString(bs) => {
let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
Literal::ByteString(text)
}
LiteralKind::String(s) => {
let text = s.value().map(Box::from).unwrap_or_else(Default::default);
Literal::String(text)
}
LiteralKind::Byte(b) => {
Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
}
LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
LiteralKind::Bool(val) => Literal::Bool(val),
}
Err(BodyDiagnostic::UndeclaredLabel {
name,
node: InFile::new(self.expander.current_file_id, AstPtr::new(&lifetime)),
})
}
fn is_label_valid_from_rib(&self, rib_index: usize) -> bool {
!self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
}
fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
self.label_ribs.push(LabelRib::new(kind));
let res = f(self);
self.label_ribs.pop();
res
}
fn with_labeled_rib<T>(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T {
self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label)));
let res = f(self);
self.label_ribs.pop();
res
}
// endregion: labels
}
impl ExprCollector<'_> {
fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
let src = self.expander.to_source(ptr);
let id = self.body.exprs.alloc(expr);
self.source_map.expr_map_back.insert(id, src.clone());
self.source_map.expr_map.insert(src, id);
id
}
// 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)
}
fn missing_expr(&mut self) -> ExprId {
self.alloc_expr_desugared(Expr::Missing)
}
fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
self.body.bindings.alloc(Binding {
name,
mode,
definitions: SmallVec::new(),
owner: self.current_binding_owner,
})
}
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
let src = self.expander.to_source(ptr);
let id = self.body.pats.alloc(pat);
self.source_map.pat_map_back.insert(id, src.clone());
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)
}
fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
let src = self.expander.to_source(ptr);
let id = self.body.labels.alloc(label);
self.source_map.label_map_back.insert(id, src.clone());
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)
}
}

View File

@ -5,7 +5,9 @@ use std::fmt::{self, Write};
use syntax::ast::HasName;
use crate::{
expr::{Array, BindingAnnotation, BindingId, ClosureKind, Literal, Movability, Statement},
hir::{
Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, Movability, Statement,
},
pretty::{print_generic_args, print_path, print_type_ref},
type_ref::TypeRef,
};
@ -13,20 +15,16 @@ use crate::{
use super::*;
pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
let needs_semi;
let header = match owner {
DefWithBodyId::FunctionId(it) => {
needs_semi = false;
let item_tree_id = it.lookup(db).id;
format!("fn {}(…) ", item_tree_id.item_tree(db)[item_tree_id.value].name)
format!("fn {}", item_tree_id.item_tree(db)[item_tree_id.value].name)
}
DefWithBodyId::StaticId(it) => {
needs_semi = true;
let item_tree_id = it.lookup(db).id;
format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name)
}
DefWithBodyId::ConstId(it) => {
needs_semi = true;
let item_tree_id = it.lookup(db).id;
let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name {
Some(name) => name.to_string(),
@ -35,7 +33,6 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
format!("const {name} = ")
}
DefWithBodyId::VariantId(it) => {
needs_semi = false;
let src = it.parent.child_source(db);
let variant = &src.value[it.local_id];
let name = match &variant.name() {
@ -47,8 +44,18 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
};
let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };
if let DefWithBodyId::FunctionId(it) = owner {
p.buf.push('(');
body.params.iter().zip(&db.function_data(it).params).for_each(|(&param, ty)| {
p.print_pat(param);
p.buf.push(':');
p.print_type_ref(ty);
});
p.buf.push(')');
p.buf.push(' ');
}
p.print_expr(body.body_expr);
if needs_semi {
if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) {
p.buf.push(';');
}
p.buf
@ -219,14 +226,14 @@ impl<'a> Printer<'a> {
}
Expr::Continue { label } => {
w!(self, "continue");
if let Some(label) = label {
w!(self, " {}", label);
if let Some(lbl) = label {
w!(self, " {}", self.body[*lbl].name);
}
}
Expr::Break { expr, label } => {
w!(self, "break");
if let Some(label) = label {
w!(self, " {}", label);
if let Some(lbl) = label {
w!(self, " {}", self.body[*lbl].name);
}
if let Some(expr) = expr {
self.whitespace();
@ -355,7 +362,7 @@ impl<'a> Printer<'a> {
self.print_expr(*index);
w!(self, "]");
}
Expr::Closure { args, arg_types, ret_type, body, closure_kind } => {
Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => {
match closure_kind {
ClosureKind::Generator(Movability::Static) => {
w!(self, "static ");
@ -365,6 +372,12 @@ impl<'a> Printer<'a> {
}
_ => (),
}
match capture_by {
CaptureBy::Value => {
w!(self, "move ");
}
CaptureBy::Ref => (),
}
w!(self, "|");
for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
if i != 0 {

View File

@ -8,7 +8,7 @@ use rustc_hash::FxHashMap;
use crate::{
body::Body,
db::DefDatabase,
expr::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
BlockId, DefWithBodyId,
};

View File

@ -10,9 +10,8 @@ use syntax::ast::HasDocComments;
use crate::{
db::DefDatabase,
dyn_map::DynMap,
dyn_map::{keys, DynMap},
item_scope::ItemScope,
keys,
src::{HasChildSource, HasSource},
AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId,
ModuleDefId, ModuleId, TraitId, VariantId,

View File

@ -1,5 +1,7 @@
//! Contains basic data about various HIR declarations.
pub mod adt;
use std::sync::Arc;
use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
@ -28,7 +30,7 @@ use crate::{
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunctionData {
pub name: Name,
pub params: Vec<(Option<Name>, Interned<TypeRef>)>,
pub params: Vec<Interned<TypeRef>>,
pub ret_type: Interned<TypeRef>,
pub attrs: Attrs,
pub visibility: RawVisibility,
@ -98,7 +100,7 @@ impl FunctionData {
params: enabled_params
.clone()
.filter_map(|id| match &item_tree[id] {
Param::Normal(name, ty) => Some((name.clone(), ty.clone())),
Param::Normal(ty) => Some(ty.clone()),
Param::Varargs => None,
})
.collect(),

View File

@ -473,7 +473,7 @@ fn lower_struct(
trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
ast: &InFile<ast::StructKind>,
) -> StructKind {
let ctx = LowerCtx::new(db, ast.file_id);
let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id);
match &ast.value {
ast::StructKind::Tuple(fl) => {

View File

@ -9,10 +9,10 @@ use la_arena::ArenaMap;
use syntax::{ast, AstPtr};
use crate::{
adt::{EnumData, StructData},
attr::{Attrs, AttrsWithOwner},
body::{scope::ExprScopes, Body, BodySourceMap},
data::{
adt::{EnumData, StructData},
ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData,
TraitAliasData, TraitData, TypeAliasData,
},

View File

@ -21,6 +21,8 @@
//!
//! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
//! a coincidence.
pub mod keys;
use std::{
hash::Hash,
marker::PhantomData,

View File

@ -12,7 +12,6 @@ use hir_expand::{
use intern::Interned;
use la_arena::{Arena, ArenaMap, Idx};
use once_cell::unsync::Lazy;
use std::ops::DerefMut;
use stdx::impl_from;
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
@ -20,8 +19,7 @@ use crate::{
body::{Expander, LowerCtx},
child_by_source::ChildBySource,
db::DefDatabase,
dyn_map::DynMap,
keys,
dyn_map::{keys, DynMap},
src::{HasChildSource, HasSource},
type_ref::{LifetimeRef, TypeBound, TypeRef},
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
@ -177,7 +175,7 @@ impl GenericParams {
// Don't create an `Expander` nor call `loc.source(db)` if not needed since this
// causes a reparse after the `ItemTree` has been created.
let mut expander = Lazy::new(|| Expander::new(db, loc.source(db).file_id, module));
for (_, param) in &func_data.params {
for param in &func_data.params {
generic_params.fill_implicit_impl_trait_args(db, &mut expander, param);
}
@ -329,7 +327,7 @@ impl GenericParams {
pub(crate) fn fill_implicit_impl_trait_args(
&mut self,
db: &dyn DefDatabase,
expander: &mut impl DerefMut<Target = Expander>,
expander: &mut Expander,
type_ref: &TypeRef,
) {
type_ref.walk(&mut |type_ref| {
@ -351,7 +349,7 @@ impl GenericParams {
let macro_call = mc.to_node(db.upcast());
match expander.enter_expand::<ast::Type>(db, macro_call) {
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
let ctx = LowerCtx::new(db, expander.current_file_id());
let ctx = expander.ctx(db);
let type_ref = TypeRef::from_ast(&ctx, expanded);
self.fill_implicit_impl_trait_args(db, expander, &type_ref);
expander.exit(db, mark);

View File

@ -12,12 +12,15 @@
//!
//! See also a neighboring `body` module.
pub mod type_ref;
use std::fmt;
use hir_expand::name::Name;
use intern::Interned;
use la_arena::{Idx, RawIdx};
use smallvec::SmallVec;
use syntax::ast;
use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
@ -28,10 +31,10 @@ use crate::{
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
pub type ExprId = Idx<Expr>;
pub type BindingId = Idx<Binding>;
pub type ExprId = Idx<Expr>;
/// FIXME: this is a hacky function which should be removed
pub(crate) fn dummy_expr_id() -> ExprId {
ExprId::from_raw(RawIdx::from(u32::MAX))
@ -102,6 +105,45 @@ impl Literal {
}
}
impl From<ast::LiteralKind> for Literal {
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
use ast::LiteralKind;
match ast_lit_kind {
// FIXME: these should have actual values filled in, but unsure on perf impact
LiteralKind::IntNumber(lit) => {
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
Literal::Float(
FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
builtin,
)
} 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) => {
let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
}
LiteralKind::ByteString(bs) => {
let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
Literal::ByteString(text)
}
LiteralKind::String(s) => {
let text = s.value().map(Box::from).unwrap_or_else(Default::default);
Literal::String(text)
}
LiteralKind::Byte(b) => {
Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
}
LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
LiteralKind::Bool(val) => Literal::Bool(val),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Expr {
/// This is produced if the syntax tree does not have a required expression piece.
@ -168,11 +210,11 @@ pub enum Expr {
arms: Box<[MatchArm]>,
},
Continue {
label: Option<Name>,
label: Option<LabelId>,
},
Break {
expr: Option<ExprId>,
label: Option<Name>,
label: Option<LabelId>,
},
Return {
expr: Option<ExprId>,
@ -233,6 +275,7 @@ pub enum Expr {
ret_type: Option<Interned<TypeRef>>,
body: ExprId,
closure_kind: ClosureKind,
capture_by: CaptureBy,
},
Tuple {
exprs: Box<[ExprId]>,
@ -250,6 +293,14 @@ pub enum ClosureKind {
Async,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CaptureBy {
/// `move |x| y + x`.
Value,
/// `move` keyword was not specified.
Ref,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Movability {
Static,
@ -442,6 +493,22 @@ pub struct Binding {
pub name: Name,
pub mode: BindingAnnotation,
pub definitions: SmallVec<[PatId; 1]>,
/// Id of the closure/generator that owns this binding. If it is owned by the
/// top level expression, this field would be `None`.
pub owner: Option<ExprId>,
}
impl Binding {
pub fn is_upvar(&self, relative_to: ExprId) -> bool {
match self.owner {
Some(x) => {
// We assign expression ids in a way that outer closures will recieve
// a lower id
x.into_raw() < relative_to.into_raw()
}
None => true,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]

View File

@ -13,7 +13,7 @@ use syntax::ast::{self, HasName};
use crate::{
body::LowerCtx,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
expr::Literal,
hir::Literal,
path::Path,
};

View File

@ -152,14 +152,6 @@ impl ItemTree {
&self.top_level
}
pub fn block_has_items(
db: &dyn DefDatabase,
file_id: HirFileId,
block: &ast::BlockExpr,
) -> bool {
lower::Ctx::new(db, file_id).block_has_items(block)
}
/// Returns the inner attributes of the source file.
pub fn top_level_attrs(&self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
Attrs::filter(
@ -614,7 +606,7 @@ pub struct Function {
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Param {
Normal(Option<Name>, Interned<TypeRef>),
Normal(Interned<TypeRef>),
Varargs,
}

View File

@ -29,7 +29,7 @@ impl<'a> Ctx<'a> {
db,
tree: ItemTree::default(),
source_ast_id_map: db.ast_id_map(file),
body_ctx: crate::body::LowerCtx::new(db, file),
body_ctx: crate::body::LowerCtx::with_file_id(db, file),
}
}
@ -101,34 +101,6 @@ impl<'a> Ctx<'a> {
self.tree
}
pub(super) fn block_has_items(mut self, block: &ast::BlockExpr) -> bool {
let statement_has_item = block
.statements()
.find_map(|stmt| match stmt {
ast::Stmt::Item(item) => self.lower_mod_item(&item),
// Macro calls can be both items and expressions. The syntax library always treats
// them as expressions here, so we undo that.
ast::Stmt::ExprStmt(es) => match es.expr()? {
ast::Expr::MacroExpr(expr) => self.lower_mod_item(&expr.macro_call()?.into()),
_ => None,
},
_ => None,
})
.is_some();
if statement_has_item {
return true;
}
if let Some(ast::Expr::MacroExpr(expr)) = block.tail_expr() {
if let Some(call) = expr.macro_call() {
if let Some(_) = self.lower_mod_item(&call.into()) {
return true;
}
}
}
false
}
fn data(&mut self) -> &mut ItemTreeData {
self.tree.data_mut()
}
@ -321,7 +293,7 @@ impl<'a> Ctx<'a> {
}
};
let ty = Interned::new(self_type);
let idx = self.data().params.alloc(Param::Normal(None, ty));
let idx = self.data().params.alloc(Param::Normal(ty));
self.add_attrs(
idx.into(),
RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()),
@ -334,19 +306,7 @@ impl<'a> Ctx<'a> {
None => {
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
let ty = Interned::new(type_ref);
let mut pat = param.pat();
// FIXME: This really shouldn't be here, in fact FunctionData/ItemTree's function shouldn't know about
// pattern names at all
let name = 'name: loop {
match pat {
Some(ast::Pat::RefPat(ref_pat)) => pat = ref_pat.pat(),
Some(ast::Pat::IdentPat(ident)) => {
break 'name ident.name().map(|it| it.as_name())
}
_ => break 'name None,
}
};
self.data().params.alloc(Param::Normal(name, ty))
self.data().params.alloc(Param::Normal(ty))
}
};
self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &param, self.hygiene()));

View File

@ -257,21 +257,15 @@ impl<'a> Printer<'a> {
w!(self, "(");
if !params.is_empty() {
self.indented(|this| {
for (i, param) in params.clone().enumerate() {
for param in params.clone() {
this.print_attrs_of(param);
match &this.tree[param] {
Param::Normal(name, ty) => {
match name {
Some(name) => w!(this, "{}: ", name),
None => w!(this, "_: "),
Param::Normal(ty) => {
if flags.contains(FnFlags::HAS_SELF_PARAM) {
w!(this, "self: ");
}
this.print_type_ref(ty);
w!(this, ",");
if flags.contains(FnFlags::HAS_SELF_PARAM) && i == 0 {
wln!(this, " // self");
} else {
wln!(this);
}
wln!(this, ",");
}
Param::Varargs => {
wln!(this, "...");

View File

@ -165,7 +165,7 @@ trait Tr: SuperTrait + 'lifetime {
fn method(&self);
}
"#,
expect![[r##"
expect![[r#"
pub static mut ST: () = _;
pub(self) const _: Anon = _;
@ -174,8 +174,8 @@ trait Tr: SuperTrait + 'lifetime {
#[inner_attr_in_fn]
pub(self) fn f(
#[attr]
arg: u8,
_: (),
u8,
(),
) -> () { ... }
pub(self) trait Tr<Self>
@ -186,10 +186,10 @@ trait Tr: SuperTrait + 'lifetime {
pub(self) type Assoc: AssocBound = Default;
pub(self) fn method(
_: &Self, // self
self: &Self,
) -> ();
}
"##]],
"#]],
);
}
@ -336,7 +336,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
T: 'b
{
pub(self) fn f<G>(
arg: impl Copy,
impl Copy,
) -> impl Copy
where
G: 'a { ... }

View File

@ -92,6 +92,7 @@ pub enum LayoutError {
SizeOverflow,
TargetLayoutNotAvailable,
HasPlaceholder,
HasErrorType,
NotImplemented,
Unknown,
}

View File

@ -18,24 +18,21 @@ pub mod db;
pub mod attr;
pub mod path;
pub mod type_ref;
pub mod builtin_type;
pub mod builtin_attr;
pub mod per_ns;
pub mod item_scope;
pub mod dyn_map;
pub mod keys;
pub mod item_tree;
pub mod adt;
pub mod data;
pub mod generics;
pub mod lang_item;
pub mod layout;
pub mod expr;
pub mod hir;
pub use self::hir::type_ref;
pub mod body;
pub mod resolver;
@ -82,8 +79,8 @@ use syntax::ast;
use ::tt::token_id as tt;
use crate::{
adt::VariantData,
builtin_type::BuiltinType,
data::adt::VariantData,
item_tree::{
Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
Static, Struct, Trait, TraitAlias, TypeAlias, Union,
@ -236,7 +233,7 @@ pub struct EnumVariantId {
pub local_id: LocalEnumVariantId,
}
pub type LocalEnumVariantId = Idx<adt::EnumVariantData>;
pub type LocalEnumVariantId = Idx<data::adt::EnumVariantData>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FieldId {
@ -244,7 +241,7 @@ pub struct FieldId {
pub local_id: LocalFieldId,
}
pub type LocalFieldId = Idx<adt::FieldData>;
pub type LocalFieldId = Idx<data::adt::FieldData>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ConstId(salsa::InternId);

View File

@ -16,7 +16,7 @@ struct Foo;
#[derive(Copy)]
struct Foo;
impl < > core::marker::Copy for Foo< > {}"#]],
impl < > core::marker::Copy for Foo< > where {}"#]],
);
}
@ -41,7 +41,7 @@ macro Copy {}
#[derive(Copy)]
struct Foo;
impl < > crate ::marker::Copy for Foo< > {}"#]],
impl < > crate ::marker::Copy for Foo< > where {}"#]],
);
}
@ -57,7 +57,7 @@ struct Foo<A, B>;
#[derive(Copy)]
struct Foo<A, B>;
impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
);
}
@ -74,7 +74,7 @@ struct Foo<A, B, 'a, 'b>;
#[derive(Copy)]
struct Foo<A, B, 'a, 'b>;
impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
);
}
@ -90,7 +90,7 @@ struct Foo<A, B>;
#[derive(Clone)]
struct Foo<A, B>;
impl <T0: core::clone::Clone, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Foo<A, B, > where {}"#]],
);
}
@ -106,6 +106,6 @@ struct Foo<const X: usize, T>(u32);
#[derive(Clone)]
struct Foo<const X: usize, T>(u32);
impl <const T0: usize, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
impl <const X: usize, T: core::clone::Clone, > core::clone::Clone for Foo<X, T, > where {}"#]],
);
}

View File

@ -4,7 +4,8 @@ use hir_expand::{attrs::Attr, MacroCallId};
use syntax::{ast, SmolStr};
use crate::{
attr_macro_as_call_id, builtin_attr,
attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
attr_macro_as_call_id,
db::DefDatabase,
item_scope::BuiltinShadowMode,
macro_id_to_def_id,
@ -76,19 +77,15 @@ impl DefMap {
let pred = |n: &_| *n == name;
let registered = self.registered_tools.iter().map(SmolStr::as_str);
let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred);
// FIXME: tool modules can be shadowed by actual modules
if is_tool {
return true;
}
if segments.len() == 1 {
let registered = self.registered_attrs.iter().map(SmolStr::as_str);
let is_inert = builtin_attr::INERT_ATTRIBUTES
.iter()
.map(|it| it.name)
.chain(registered)
.any(pred);
let mut registered = self.registered_attrs.iter().map(SmolStr::as_str);
let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred);
return is_inert;
}
}

View File

@ -331,11 +331,11 @@ impl DefMap {
Some(local_id) => {
let variant = EnumVariantId { parent: e, local_id };
match &*enum_data.variants[local_id].variant_data {
crate::adt::VariantData::Record(_) => {
crate::data::adt::VariantData::Record(_) => {
PerNs::types(variant.into(), Visibility::Public)
}
crate::adt::VariantData::Tuple(_)
| crate::adt::VariantData::Unit => {
crate::data::adt::VariantData::Tuple(_)
| crate::data::adt::VariantData::Unit => {
PerNs::both(variant.into(), variant.into(), Visibility::Public)
}
}

View File

@ -12,8 +12,8 @@ use crate::{
body::scope::{ExprScopes, ScopeId},
builtin_type::BuiltinType,
db::DefDatabase,
expr::{BindingId, ExprId, LabelId},
generics::{GenericParams, TypeOrConstParamData},
hir::{BindingId, ExprId, LabelId},
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
lang_item::LangItemTarget,
nameres::DefMap,

View File

@ -1,11 +1,12 @@
//! Builtin derives.
use base_db::{CrateOrigin, LangCrateOrigin};
use std::collections::HashSet;
use tracing::debug;
use crate::tt::{self, TokenId};
use syntax::{
ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName, HasTypeBounds, PathType},
match_ast,
};
@ -60,8 +61,11 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
struct BasicAdtInfo {
name: tt::Ident,
/// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
param_types: Vec<Option<tt::Subtree>>,
/// first field is the name, and
/// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
/// third fields is where bounds, if any
param_types: Vec<(tt::Subtree, Option<tt::Subtree>, Option<tt::Subtree>)>,
associated_types: Vec<tt::Subtree>,
}
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
@ -86,18 +90,28 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
},
}
};
let name = name.ok_or_else(|| {
debug!("parsed item has no name");
ExpandError::Other("missing name".into())
})?;
let name_token_id =
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
let mut param_type_set: HashSet<String> = HashSet::new();
let param_types = params
.into_iter()
.flat_map(|param_list| param_list.type_or_const_params())
.map(|param| {
if let ast::TypeOrConstParam::Const(param) = param {
let name = {
let this = param.name();
match this {
Some(x) => {
param_type_set.insert(x.to_string());
mbe::syntax_node_to_token_tree(x.syntax()).0
}
None => tt::Subtree::empty(),
}
};
let bounds = match &param {
ast::TypeOrConstParam::Type(x) => {
x.type_bound_list().map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
}
ast::TypeOrConstParam::Const(_) => None,
};
let ty = if let ast::TypeOrConstParam::Const(param) = param {
let ty = param
.ty()
.map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0)
@ -105,27 +119,97 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
Some(ty)
} else {
None
}
};
(name, ty, bounds)
})
.collect();
Ok(BasicAdtInfo { name: name_token, param_types })
let is_associated_type = |p: &PathType| {
if let Some(p) = p.path() {
if let Some(parent) = p.qualifier() {
if let Some(x) = parent.segment() {
if let Some(x) = x.path_type() {
if let Some(x) = x.path() {
if let Some(pname) = x.as_single_name_ref() {
if param_type_set.contains(&pname.to_string()) {
// <T as Trait>::Assoc
return true;
}
}
}
}
}
if let Some(pname) = parent.as_single_name_ref() {
if param_type_set.contains(&pname.to_string()) {
// T::Assoc
return true;
}
}
}
}
false
};
let associated_types = node
.descendants()
.filter_map(PathType::cast)
.filter(is_associated_type)
.map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
.collect::<Vec<_>>();
let name = name.ok_or_else(|| {
debug!("parsed item has no name");
ExpandError::Other("missing name".into())
})?;
let name_token_id =
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
Ok(BasicAdtInfo { name: name_token, param_types, associated_types })
}
/// Given that we are deriving a trait `DerivedTrait` for a type like:
///
/// ```ignore (only-for-syntax-highlight)
/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
/// a: A,
/// b: B::Item,
/// b1: <B as DeclaredTrait>::Item,
/// c1: <C as WhereTrait>::Item,
/// c2: Option<<C as WhereTrait>::Item>,
/// ...
/// }
/// ```
///
/// create an impl like:
///
/// ```ignore (only-for-syntax-highlight)
/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where
/// C: WhereTrait,
/// A: DerivedTrait + B1 + ... + BN,
/// B: DerivedTrait + B1 + ... + BN,
/// C: DerivedTrait + B1 + ... + BN,
/// B::Item: DerivedTrait + B1 + ... + BN,
/// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
/// ...
/// {
/// ...
/// }
/// ```
///
/// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and
/// therefore does not get bound by the derived trait.
fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
let info = match parse_adt(tt) {
Ok(info) => info,
Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e),
};
let mut where_block = vec![];
let (params, args): (Vec<_>, Vec<_>) = info
.param_types
.into_iter()
.enumerate()
.map(|(idx, param_ty)| {
let ident = tt::Leaf::Ident(tt::Ident {
span: tt::TokenId::unspecified(),
text: format!("T{idx}").into(),
});
.map(|(ident, param_ty, bound)| {
let ident_ = ident.clone();
if let Some(b) = bound {
let ident = ident.clone();
where_block.push(quote! { #ident : #b , });
}
if let Some(ty) = param_ty {
(quote! { const #ident : #ty , }, quote! { #ident_ , })
} else {
@ -134,9 +218,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
}
})
.unzip();
where_block.extend(info.associated_types.iter().map(|x| {
let x = x.clone();
let bound = trait_path.clone();
quote! { #x : #bound , }
}));
let name = info.name;
let expanded = quote! {
impl < ##params > #trait_path for #name < ##args > {}
impl < ##params > #trait_path for #name < ##args > where ##where_block {}
};
ExpandResult::ok(expanded)
}

View File

@ -55,7 +55,7 @@ pub type ExpandResult<T> = ValueResult<T, ExpandError>;
pub enum ExpandError {
UnresolvedProcMacro(CrateId),
Mbe(mbe::ExpandError),
RecursionOverflowPosioned,
RecursionOverflowPoisoned,
Other(Box<str>),
}
@ -70,7 +70,7 @@ impl fmt::Display for ExpandError {
match self {
ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"),
ExpandError::Mbe(it) => it.fmt(f),
ExpandError::RecursionOverflowPosioned => {
ExpandError::RecursionOverflowPoisoned => {
f.write_str("overflow expanding the original macro")
}
ExpandError::Other(it) => f.write_str(it),

View File

@ -120,8 +120,7 @@ impl 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())
Name::new_text(format!("<ra@gennew>{c}").into())
}
/// Returns the tuple index this name represents if it is a tuple field.

View File

@ -9,7 +9,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
use base_db::CrateId;
use hir_def::{
expr::Movability,
hir::Movability,
lang_item::{lang_attr, LangItem, LangItemTarget},
AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId,
};
@ -18,9 +18,10 @@ use hir_expand::name::name;
use crate::{
db::HirDatabase,
display::HirDisplay,
from_assoc_type_id, from_chalk_trait_id, make_binders, make_single_type_binders,
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_binders,
make_single_type_binders,
mapping::{from_chalk, ToChalk, TypeAliasAsValue},
method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
to_assoc_type_id, to_chalk_trait_id,
traits::ChalkContext,
utils::generics,
@ -106,6 +107,19 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
_ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
};
let trait_module = trait_.module(self.db.upcast());
let type_module = match self_ty_fp {
Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())),
Some(TyFingerprint::ForeignType(type_id)) => {
Some(from_foreign_def_id(type_id).module(self.db.upcast()))
}
Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())),
_ => None,
};
let mut def_blocks =
[trait_module.containing_block(), type_module.and_then(|it| it.containing_block())];
// Note: Since we're using impls_for_trait, only impls where the trait
// can be resolved should ever reach Chalk. impl_datum relies on that
// and will panic if the trait can't be resolved.
@ -120,6 +134,14 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
.and_then(|map| map.parent())
.and_then(|module| module.containing_block())
})
.inspect(|&block_id| {
// make sure we don't search the same block twice
def_blocks.iter_mut().for_each(|block| {
if *block == Some(block_id) {
*block = None;
}
});
})
.filter_map(|block_id| self.db.trait_impls_in_block(block_id));
let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
@ -127,18 +149,27 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
match fps {
[] => {
debug!("Unrestricted search for {:?} impls...", trait_);
impl_maps.into_iter().chain(block_impls).for_each(|impls| {
let mut f = |impls: Arc<TraitImpls>| {
result.extend(impls.for_trait(trait_).map(id_to_chalk));
});
};
impl_maps.into_iter().chain(block_impls).for_each(&mut f);
def_blocks
.into_iter()
.filter_map(|it| self.db.trait_impls_in_block(it?))
.for_each(f);
}
fps => {
impl_maps.into_iter().chain(block_impls).for_each(|impls| {
result.extend(
fps.iter().flat_map(|fp| {
let mut f =
|impls: Arc<TraitImpls>| {
result.extend(fps.iter().flat_map(|fp| {
impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
}),
);
});
}));
};
impl_maps.into_iter().chain(block_impls).for_each(&mut f);
def_blocks
.into_iter()
.filter_map(|it| self.db.trait_impls_in_block(it?))
.for_each(f);
}
}
@ -384,8 +415,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
let input_output = crate::make_type_and_const_binders(it, input_output);
let movability = match self.db.body(parent)[expr] {
hir_def::expr::Expr::Closure {
closure_kind: hir_def::expr::ClosureKind::Generator(movability),
hir_def::hir::Expr::Closure {
closure_kind: hir_def::hir::ClosureKind::Generator(movability),
..
} => movability,
_ => unreachable!("non generator expression interned as generator"),

View File

@ -12,8 +12,9 @@ use hir_def::{
use crate::{
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
CallableDefId, CallableSig, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
CallableDefId, CallableSig, ClosureId, DynTy, FnPointer, ImplTraitId, Interner, Lifetime,
ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags,
WhereClause,
};
pub trait TyExt {
@ -28,6 +29,7 @@ pub trait TyExt {
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
fn as_builtin(&self) -> Option<BuiltinType>;
fn as_tuple(&self) -> Option<&Substitution>;
fn as_closure(&self) -> Option<ClosureId>;
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
@ -128,6 +130,13 @@ impl TyExt for Ty {
}
}
fn as_closure(&self) -> Option<ClosureId> {
match self.kind(Interner) {
TyKind::Closure(id, _) => Some(*id),
_ => None,
}
}
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
match self.callable_def(db) {
Some(CallableDefId::FunctionId(func)) => Some(func),

View File

@ -3,7 +3,7 @@
use base_db::CrateId;
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
use hir_def::{
expr::Expr,
hir::Expr,
path::Path,
resolver::{Resolver, ValueNs},
type_ref::ConstRef,

View File

@ -1105,6 +1105,81 @@ fn try_block() {
);
}
#[test]
fn closures() {
check_number(
r#"
//- minicore: fn, copy
const GOAL: i32 = {
let y = 5;
let c = |x| x + y;
c(2)
};
"#,
7,
);
check_number(
r#"
//- minicore: fn, copy
const GOAL: i32 = {
let y = 5;
let c = |(a, b): &(i32, i32)| *a + *b + y;
c(&(2, 3))
};
"#,
10,
);
check_number(
r#"
//- minicore: fn, copy
const GOAL: i32 = {
let mut y = 5;
let c = |x| {
y = y + x;
};
c(2);
c(3);
y
};
"#,
10,
);
check_number(
r#"
//- minicore: fn, copy
struct X(i32);
impl X {
fn mult(&mut self, n: i32) {
self.0 = self.0 * n
}
}
const GOAL: i32 = {
let x = X(1);
let c = || {
x.mult(2);
|| {
x.mult(3);
|| {
|| {
x.mult(4);
|| {
x.mult(x.0);
|| {
x.0
}
}
}
}
}
};
let r = c()()()()()();
r + x.0
};
"#,
24 * 24 * 2,
);
}
#[test]
fn or_pattern() {
check_number(

View File

@ -6,7 +6,7 @@ use std::sync::Arc;
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{
db::DefDatabase,
expr::ExprId,
hir::ExprId,
layout::{Layout, LayoutError, TargetDataLayout},
AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId,
ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
@ -19,9 +19,9 @@ use crate::{
consteval::ConstEvalError,
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
mir::{BorrowckResult, MirBody, MirLowerError},
Binders, CallableDefId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner,
PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId,
ValueTyDefId,
Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult,
Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty,
TyDefId, ValueTyDefId,
};
use hir_expand::name::Name;
@ -38,8 +38,11 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::cycle(crate::mir::mir_body_recover)]
fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>;
#[salsa::invoke(crate::mir::mir_body_for_closure_query)]
fn mir_body_for_closure(&self, def: ClosureId) -> Result<Arc<MirBody>, MirLowerError>;
#[salsa::invoke(crate::mir::borrowck_query)]
fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<BorrowckResult>, MirLowerError>;
fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>;
#[salsa::invoke(crate::lower::ty_query)]
#[salsa::cycle(crate::lower::ty_recover)]

View File

@ -16,8 +16,8 @@ use std::fmt;
use base_db::CrateId;
use hir_def::{
adt::VariantData,
expr::{Pat, PatId},
data::adt::VariantData,
hir::{Pat, PatId},
src::HasSource,
AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, StaticId,
StructId,

View File

@ -27,7 +27,7 @@ use crate::{
pub(crate) use hir_def::{
body::Body,
expr::{Expr, ExprId, MatchArm, Pat, PatId},
hir::{Expr, ExprId, MatchArm, Pat, PatId},
LocalFieldId, VariantId,
};

View File

@ -1,6 +1,6 @@
//! Validation of matches.
//!
//! This module provides lowering from [hir_def::expr::Pat] to [self::Pat] and match
//! This module provides lowering from [hir_def::hir::Pat] to [self::Pat] and match
//! checking algorithm.
//!
//! It is modeled on the rustc module `rustc_mir_build::thir::pattern`.
@ -12,7 +12,7 @@ pub(crate) mod usefulness;
use chalk_ir::Mutability;
use hir_def::{
adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
body::Body, data::adt::VariantData, hir::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
};
use hir_expand::name::Name;
use stdx::{always, never};
@ -125,15 +125,15 @@ impl<'a> PatCtxt<'a> {
let variant = self.infer.variant_resolution_for_pat(pat);
let kind = match self.body[pat] {
hir_def::expr::Pat::Wild => PatKind::Wild,
hir_def::hir::Pat::Wild => PatKind::Wild,
hir_def::expr::Pat::Lit(expr) => self.lower_lit(expr),
hir_def::hir::Pat::Lit(expr) => self.lower_lit(expr),
hir_def::expr::Pat::Path(ref path) => {
hir_def::hir::Pat::Path(ref path) => {
return self.lower_path(pat, path);
}
hir_def::expr::Pat::Tuple { ref args, ellipsis } => {
hir_def::hir::Pat::Tuple { ref args, ellipsis } => {
let arity = match *ty.kind(Interner) {
TyKind::Tuple(arity, _) => arity,
_ => {
@ -146,7 +146,7 @@ impl<'a> PatCtxt<'a> {
PatKind::Leaf { subpatterns }
}
hir_def::expr::Pat::Bind { id, subpat, .. } => {
hir_def::hir::Pat::Bind { id, subpat, .. } => {
let bm = self.infer.pat_binding_modes[&pat];
let name = &self.body.bindings[id].name;
match (bm, ty.kind(Interner)) {
@ -161,13 +161,13 @@ impl<'a> PatCtxt<'a> {
PatKind::Binding { name: name.clone(), subpattern: self.lower_opt_pattern(subpat) }
}
hir_def::expr::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => {
hir_def::hir::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => {
let expected_len = variant.unwrap().variant_data(self.db.upcast()).fields().len();
let subpatterns = self.lower_tuple_subpats(args, expected_len, ellipsis);
self.lower_variant_or_leaf(pat, ty, subpatterns)
}
hir_def::expr::Pat::Record { ref args, .. } if variant.is_some() => {
hir_def::hir::Pat::Record { ref args, .. } if variant.is_some() => {
let variant_data = variant.unwrap().variant_data(self.db.upcast());
let subpatterns = args
.iter()
@ -187,12 +187,12 @@ impl<'a> PatCtxt<'a> {
}
}
}
hir_def::expr::Pat::TupleStruct { .. } | hir_def::expr::Pat::Record { .. } => {
hir_def::hir::Pat::TupleStruct { .. } | hir_def::hir::Pat::Record { .. } => {
self.errors.push(PatternError::UnresolvedVariant);
PatKind::Wild
}
hir_def::expr::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
hir_def::hir::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
_ => {
self.errors.push(PatternError::Unimplemented);
@ -279,8 +279,8 @@ impl<'a> PatCtxt<'a> {
}
}
fn lower_lit(&mut self, expr: hir_def::expr::ExprId) -> PatKind {
use hir_def::expr::{Expr, Literal::Bool};
fn lower_lit(&mut self, expr: hir_def::hir::ExprId) -> PatKind {
use hir_def::hir::{Expr, Literal::Bool};
match self.body[expr] {
Expr::Literal(Bool(value)) => PatKind::LiteralBool { value },

View File

@ -3,7 +3,7 @@
use hir_def::{
body::Body,
expr::{Expr, ExprId, UnaryOp},
hir::{Expr, ExprId, UnaryOp},
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
DefWithBodyId,
};

View File

@ -7,8 +7,8 @@ use std::fmt::{self, Debug};
use base_db::CrateId;
use chalk_ir::{BoundVar, TyKind};
use hir_def::{
adt::VariantData,
body,
data::adt::VariantData,
db::DefDatabase,
find_path,
generics::{TypeOrConstParamData, TypeParamProvenance},
@ -23,6 +23,7 @@ use hir_expand::{hygiene::Hygiene, name::Name};
use intern::{Internable, Interned};
use itertools::Itertools;
use smallvec::SmallVec;
use stdx::never;
use crate::{
db::HirDatabase,
@ -64,6 +65,7 @@ pub struct HirFormatter<'a> {
curr_size: usize,
pub(crate) max_size: Option<usize>,
omit_verbose_types: bool,
closure_style: ClosureStyle,
display_target: DisplayTarget,
}
@ -87,6 +89,7 @@ pub trait HirDisplay {
max_size: Option<usize>,
omit_verbose_types: bool,
display_target: DisplayTarget,
closure_style: ClosureStyle,
) -> HirDisplayWrapper<'a, Self>
where
Self: Sized,
@ -95,7 +98,14 @@ pub trait HirDisplay {
!matches!(display_target, DisplayTarget::SourceCode { .. }),
"HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead"
);
HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target }
HirDisplayWrapper {
db,
t: self,
max_size,
omit_verbose_types,
display_target,
closure_style,
}
}
/// Returns a `Display`able type that is human-readable.
@ -109,6 +119,7 @@ pub trait HirDisplay {
t: self,
max_size: None,
omit_verbose_types: false,
closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::Diagnostics,
}
}
@ -128,6 +139,7 @@ pub trait HirDisplay {
t: self,
max_size,
omit_verbose_types: true,
closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::Diagnostics,
}
}
@ -147,6 +159,7 @@ pub trait HirDisplay {
curr_size: 0,
max_size: None,
omit_verbose_types: false,
closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::SourceCode { module_id },
}) {
Ok(()) => {}
@ -166,6 +179,7 @@ pub trait HirDisplay {
t: self,
max_size: None,
omit_verbose_types: false,
closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::Test,
}
}
@ -253,7 +267,6 @@ impl DisplayTarget {
pub enum DisplaySourceCodeError {
PathNotFound,
UnknownType,
Closure,
Generator,
}
@ -274,9 +287,23 @@ pub struct HirDisplayWrapper<'a, T> {
t: &'a T,
max_size: Option<usize>,
omit_verbose_types: bool,
closure_style: ClosureStyle,
display_target: DisplayTarget,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ClosureStyle {
/// `impl FnX(i32, i32) -> i32`, where `FnX` is the most special trait between `Fn`, `FnMut`, `FnOnce` that the
/// closure implements. This is the default.
ImplFn,
/// `|i32, i32| -> i32`
RANotation,
/// `{closure#14825}`, useful for some diagnostics (like type mismatch) and internal usage.
ClosureWithId,
/// `…`, which is the `TYPE_HINT_TRUNCATION`
Hide,
}
impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
pub fn write_to<F: HirWrite>(&self, f: &mut F) -> Result<(), HirDisplayError> {
self.t.hir_fmt(&mut HirFormatter {
@ -287,8 +314,14 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
max_size: self.max_size,
omit_verbose_types: self.omit_verbose_types,
display_target: self.display_target,
closure_style: self.closure_style,
})
}
pub fn with_closure_style(mut self, c: ClosureStyle) -> Self {
self.closure_style = c;
self
}
}
impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
@ -919,26 +952,42 @@ impl HirDisplay for Ty {
}
}
}
TyKind::Closure(.., substs) => {
if f.display_target.is_source_code() {
return Err(HirDisplayError::DisplaySourceCodeError(
DisplaySourceCodeError::Closure,
));
TyKind::Closure(id, substs) => {
if f.display_target.is_source_code() && f.closure_style != ClosureStyle::ImplFn {
never!("Only `impl Fn` is valid for displaying closures in source code");
}
match f.closure_style {
ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),
ClosureStyle::ClosureWithId => {
return write!(f, "{{closure#{:?}}}", id.0.as_u32())
}
_ => (),
}
let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db);
if let Some(sig) = sig {
let (def, _) = db.lookup_intern_closure((*id).into());
let infer = db.infer(def);
let (_, kind) = infer.closure_info(id);
match f.closure_style {
ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?,
ClosureStyle::RANotation => write!(f, "|")?,
_ => unreachable!(),
}
if sig.params().is_empty() {
write!(f, "||")?;
} else if f.should_truncate() {
write!(f, "|{TYPE_HINT_TRUNCATION}|")?;
write!(f, "{TYPE_HINT_TRUNCATION}")?;
} else {
write!(f, "|")?;
f.write_joined(sig.params(), ", ")?;
write!(f, "|")?;
};
write!(f, " -> ")?;
sig.ret().hir_fmt(f)?;
match f.closure_style {
ClosureStyle::ImplFn => write!(f, ")")?,
ClosureStyle::RANotation => write!(f, "|")?,
_ => unreachable!(),
}
if f.closure_style == ClosureStyle::RANotation || !sig.ret().is_unit() {
write!(f, " -> ")?;
sig.ret().hir_fmt(f)?;
}
} else {
write!(f, "{{closure}}")?;
}

View File

@ -18,11 +18,12 @@ use std::{convert::identity, ops::Index};
use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
use either::Either;
use hir_def::hir::LabelId;
use hir_def::{
body::Body,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
data::{ConstData, StaticData},
expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
lang_item::{LangItem, LangItemTarget},
layout::Integer,
path::{ModPath, Path},
@ -32,15 +33,15 @@ use hir_def::{
TraitId, TypeAliasId, VariantId,
};
use hir_expand::name::{name, Name};
use la_arena::ArenaMap;
use la_arena::{ArenaMap, Entry};
use rustc_hash::{FxHashMap, FxHashSet};
use stdx::{always, never};
use crate::{
db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
static_lifetime, to_assoc_type_id, AliasEq, AliasTy, DomainGoal, GenericArg, Goal, ImplTraitId,
InEnvironment, Interner, ProjectionTy, RpitId, Substitution, TraitRef, Ty, TyBuilder, TyExt,
TyKind,
static_lifetime, to_assoc_type_id, traits::FnTrait, AliasEq, AliasTy, ClosureId, DomainGoal,
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
TraitRef, Ty, TyBuilder, TyExt, TyKind,
};
// This lint has a false positive here. See the link below for details.
@ -51,6 +52,8 @@ pub use coerce::could_coerce;
#[allow(unreachable_pub)]
pub use unify::could_unify;
pub(crate) use self::closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
pub(crate) mod unify;
mod path;
mod expr;
@ -102,6 +105,8 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
ctx.infer_mut_body();
ctx.infer_closures();
Arc::new(ctx.resolve_all())
}
@ -188,7 +193,7 @@ pub enum InferenceDiagnostic {
/// Contains the type the field resolves to
field_with_same_name: Option<Ty>,
},
// FIXME: Make this proper
// FIXME: This should be emitted in body lowering
BreakOutsideOfLoop {
expr: ExprId,
is_break: bool,
@ -311,6 +316,13 @@ pub enum AutoBorrow {
RawPtr(Mutability),
}
impl AutoBorrow {
fn mutability(self) -> Mutability {
let (AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) = self;
m
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PointerCast {
/// Go from a fn-item type to a fn-pointer type.
@ -372,6 +384,9 @@ pub struct InferenceResult {
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
pub pat_binding_modes: FxHashMap<PatId, BindingMode>,
pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>,
pub(crate) closure_info: FxHashMap<ClosureId, (Vec<CapturedItem>, FnTrait)>,
// FIXME: remove this field
pub mutated_bindings_in_closure: FxHashSet<BindingId>,
}
impl InferenceResult {
@ -408,6 +423,9 @@ impl InferenceResult {
_ => None,
})
}
pub(crate) fn closure_info(&self, closure: &ClosureId) -> &(Vec<CapturedItem>, FnTrait) {
self.closure_info.get(closure).unwrap()
}
}
impl Index<ExprId> for InferenceResult {
@ -459,6 +477,14 @@ pub(crate) struct InferenceContext<'a> {
resume_yield_tys: Option<(Ty, Ty)>,
diverges: Diverges,
breakables: Vec<BreakableContext>,
// fields related to closure capture
current_captures: Vec<CapturedItemWithoutTy>,
current_closure: Option<ClosureId>,
/// Stores the list of closure ids that need to be analyzed before this closure. See the
/// comment on `InferenceContext::sort_closures`
closure_dependecies: FxHashMap<ClosureId, Vec<ClosureId>>,
deferred_closures: FxHashMap<ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>,
}
#[derive(Clone, Debug)]
@ -468,7 +494,7 @@ struct BreakableContext {
/// The coercion target of the context.
coerce: Option<CoerceMany>,
/// The optional label of the context.
label: Option<name::Name>,
label: Option<LabelId>,
kind: BreakableKind,
}
@ -483,21 +509,21 @@ enum BreakableKind {
fn find_breakable<'c>(
ctxs: &'c mut [BreakableContext],
label: Option<&name::Name>,
label: Option<LabelId>,
) -> Option<&'c mut BreakableContext> {
let mut ctxs = ctxs
.iter_mut()
.rev()
.take_while(|it| matches!(it.kind, BreakableKind::Block | BreakableKind::Loop));
match label {
Some(_) => ctxs.find(|ctx| ctx.label.as_ref() == label),
Some(_) => ctxs.find(|ctx| ctx.label == label),
None => ctxs.find(|ctx| matches!(ctx.kind, BreakableKind::Loop)),
}
}
fn find_continuable<'c>(
ctxs: &'c mut [BreakableContext],
label: Option<&name::Name>,
label: Option<LabelId>,
) -> Option<&'c mut BreakableContext> {
match label {
Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)),
@ -526,6 +552,10 @@ impl<'a> InferenceContext<'a> {
resolver,
diverges: Diverges::Maybe,
breakables: Vec::new(),
current_captures: vec![],
current_closure: None,
deferred_closures: FxHashMap::default(),
closure_dependecies: FxHashMap::default(),
}
}
@ -617,7 +647,7 @@ impl<'a> InferenceContext<'a> {
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
.with_impl_trait_mode(ImplTraitLoweringMode::Param);
let mut param_tys =
data.params.iter().map(|(_, type_ref)| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
data.params.iter().map(|type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
// Check if function contains a va_list, if it does then we append it to the parameter types
// that are collected from the function data
if data.is_varargs() {
@ -646,36 +676,16 @@ impl<'a> InferenceContext<'a> {
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
// RPIT opaque types use substitution of their parent function.
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
fold_tys(
return_ty,
|ty, _| {
let opaque_ty_id = match ty.kind(Interner) {
TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
_ => return ty,
};
let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
_ => unreachable!(),
};
let bounds = (*rpits).map_ref(|rpits| {
rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter())
});
let var = self.table.new_type_var();
let var_subst = Substitution::from1(Interner, var.clone());
for bound in bounds {
let predicate =
bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
let (var_predicate, binders) = predicate
.substitute(Interner, &var_subst)
.into_value_and_skipped_binders();
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
self.push_obligation(var_predicate.cast(Interner));
}
self.result.type_of_rpit.insert(idx, var.clone());
var
},
DebruijnIndex::INNERMOST,
)
let result =
self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders);
let rpits = rpits.skip_binders();
for (id, _) in rpits.impl_traits.iter() {
if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
never!("Missed RPIT in `insert_inference_vars_for_rpit`");
e.insert(TyKind::Error.intern(Interner));
}
}
result
} else {
return_ty
};
@ -684,6 +694,50 @@ impl<'a> InferenceContext<'a> {
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
}
fn insert_inference_vars_for_rpit<T>(
&mut self,
t: T,
rpits: Arc<chalk_ir::Binders<crate::ReturnTypeImplTraits>>,
fn_placeholders: Substitution,
) -> T
where
T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
{
fold_tys(
t,
|ty, _| {
let opaque_ty_id = match ty.kind(Interner) {
TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
_ => return ty,
};
let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
_ => unreachable!(),
};
let bounds = (*rpits)
.map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter()));
let var = self.table.new_type_var();
let var_subst = Substitution::from1(Interner, var.clone());
for bound in bounds {
let predicate =
bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
let (var_predicate, binders) =
predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
let var_predicate = self.insert_inference_vars_for_rpit(
var_predicate,
rpits.clone(),
fn_placeholders.clone(),
);
self.push_obligation(var_predicate.cast(Interner));
}
self.result.type_of_rpit.insert(idx, var.clone());
var
},
DebruijnIndex::INNERMOST,
)
}
fn infer_body(&mut self) {
match self.return_coercion {
Some(_) => self.infer_return(self.body.body_expr),

View File

@ -1,12 +1,29 @@
//! Inference of closure parameter types based on the closure's expected type.
use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, WhereClause};
use hir_def::{expr::ExprId, HasModule};
use std::{cmp, collections::HashMap, convert::Infallible, mem};
use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, Mutability, TyKind, WhereClause};
use hir_def::{
hir::{
Array, BinaryOp, BindingAnnotation, BindingId, CaptureBy, Expr, ExprId, Pat, PatId,
Statement, UnaryOp,
},
lang_item::LangItem,
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
FieldId, HasModule, VariantId,
};
use hir_expand::name;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use stdx::never;
use crate::{
to_chalk_trait_id, utils, ChalkTraitId, DynTy, FnPointer, FnSig, Interner, Substitution, Ty,
TyExt, TyKind,
mir::{BorrowKind, ProjectionElem},
static_lifetime, to_chalk_trait_id,
traits::FnTrait,
utils::{self, pattern_matching_dereference_count},
Adjust, Adjustment, Canonical, CanonicalVarKinds, ChalkTraitId, ClosureId, DynTy, FnPointer,
FnSig, InEnvironment, Interner, Substitution, Ty, TyBuilder, TyExt,
};
use super::{Expectation, InferenceContext};
@ -86,3 +103,700 @@ impl InferenceContext<'_> {
None
}
}
// The below functions handle capture and closure kind (Fn, FnMut, ..)
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct HirPlace {
pub(crate) local: BindingId,
pub(crate) projections: Vec<ProjectionElem<Infallible, Ty>>,
}
impl HirPlace {
fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty {
let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone());
for p in &self.projections {
ty = p.projected_ty(ty, ctx.db, |_, _| {
unreachable!("Closure field only happens in MIR");
});
}
ty.clone()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum CaptureKind {
ByRef(BorrowKind),
ByValue,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct CapturedItem {
pub(crate) place: HirPlace,
pub(crate) kind: CaptureKind,
pub(crate) ty: Ty,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct CapturedItemWithoutTy {
pub(crate) place: HirPlace,
pub(crate) kind: CaptureKind,
}
impl CapturedItemWithoutTy {
fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem {
let ty = self.place.ty(ctx).clone();
let ty = match &self.kind {
CaptureKind::ByValue => ty,
CaptureKind::ByRef(bk) => {
let m = match bk {
BorrowKind::Mut { .. } => Mutability::Mut,
_ => Mutability::Not,
};
TyKind::Ref(m, static_lifetime(), ty).intern(Interner)
}
};
CapturedItem { place: self.place, kind: self.kind, ty }
}
}
impl InferenceContext<'_> {
fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
let r = self.place_of_expr_without_adjust(tgt_expr)?;
let default = vec![];
let adjustments = self.result.expr_adjustments.get(&tgt_expr).unwrap_or(&default);
apply_adjusts_to_place(r, adjustments)
}
fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
match &self.body[tgt_expr] {
Expr::Path(p) => {
let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
if let Some(r) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) {
if let ResolveValueResult::ValueNs(v) = r {
if let ValueNs::LocalBinding(b) = v {
return Some(HirPlace { local: b, projections: vec![] });
}
}
}
}
Expr::Field { expr, name } => {
let mut place = self.place_of_expr(*expr)?;
if let TyKind::Tuple(..) = self.expr_ty(*expr).kind(Interner) {
let index = name.as_tuple_index()?;
place.projections.push(ProjectionElem::TupleOrClosureField(index))
} else {
let field = self.result.field_resolution(tgt_expr)?;
place.projections.push(ProjectionElem::Field(field));
}
return Some(place);
}
_ => (),
}
None
}
fn push_capture(&mut self, capture: CapturedItemWithoutTy) {
self.current_captures.push(capture);
}
fn ref_expr(&mut self, expr: ExprId) {
if let Some(place) = self.place_of_expr(expr) {
self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared));
}
self.walk_expr(expr);
}
fn add_capture(&mut self, place: HirPlace, kind: CaptureKind) {
if self.is_upvar(&place) {
self.push_capture(CapturedItemWithoutTy { place, kind });
}
}
fn mutate_expr(&mut self, expr: ExprId) {
if let Some(place) = self.place_of_expr(expr) {
self.add_capture(
place,
CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }),
);
}
self.walk_expr(expr);
}
fn consume_expr(&mut self, expr: ExprId) {
if let Some(place) = self.place_of_expr(expr) {
self.consume_place(place);
}
self.walk_expr(expr);
}
fn consume_place(&mut self, place: HirPlace) {
if self.is_upvar(&place) {
let ty = place.ty(self).clone();
let kind = if self.is_ty_copy(ty) {
CaptureKind::ByRef(BorrowKind::Shared)
} else {
CaptureKind::ByValue
};
self.push_capture(CapturedItemWithoutTy { place, kind });
}
}
fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) {
if let Some((last, rest)) = adjustment.split_last() {
match last.kind {
Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => {
self.walk_expr_with_adjust(tgt_expr, rest)
}
Adjust::Deref(Some(m)) => match m.0 {
Some(m) => {
self.ref_capture_with_adjusts(m, tgt_expr, rest);
}
None => unreachable!(),
},
Adjust::Borrow(b) => {
self.ref_capture_with_adjusts(b.mutability(), tgt_expr, rest);
}
}
} else {
self.walk_expr_without_adjust(tgt_expr);
}
}
fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) {
let capture_kind = match m {
Mutability::Mut => {
CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false })
}
Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared),
};
if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) {
if let Some(place) = apply_adjusts_to_place(place, rest) {
if self.is_upvar(&place) {
self.push_capture(CapturedItemWithoutTy { place, kind: capture_kind });
}
}
}
self.walk_expr_with_adjust(tgt_expr, rest);
}
fn walk_expr(&mut self, tgt_expr: ExprId) {
if let Some(x) = self.result.expr_adjustments.get_mut(&tgt_expr) {
// FIXME: this take is completely unneeded, and just is here to make borrow checker
// happy. Remove it if you can.
let x_taken = mem::take(x);
self.walk_expr_with_adjust(tgt_expr, &x_taken);
*self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken;
} else {
self.walk_expr_without_adjust(tgt_expr);
}
}
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
match &self.body[tgt_expr] {
Expr::If { condition, then_branch, else_branch } => {
self.consume_expr(*condition);
self.consume_expr(*then_branch);
if let &Some(expr) = else_branch {
self.consume_expr(expr);
}
}
Expr::Async { statements, tail, .. }
| Expr::Const { statements, tail, .. }
| Expr::Unsafe { statements, tail, .. }
| Expr::Block { statements, tail, .. } => {
for s in statements.iter() {
match s {
Statement::Let { pat, type_ref: _, initializer, else_branch } => {
if let Some(else_branch) = else_branch {
self.consume_expr(*else_branch);
if let Some(initializer) = initializer {
self.consume_expr(*initializer);
}
return;
}
if let Some(initializer) = initializer {
self.walk_expr(*initializer);
if let Some(place) = self.place_of_expr(*initializer) {
let ty = self.expr_ty(*initializer);
self.consume_with_pat(
place,
ty,
BindingAnnotation::Unannotated,
*pat,
);
}
}
}
Statement::Expr { expr, has_semi: _ } => {
self.consume_expr(*expr);
}
}
}
if let Some(tail) = tail {
self.consume_expr(*tail);
}
}
Expr::While { condition, body, label: _ }
| Expr::For { iterable: condition, pat: _, body, label: _ } => {
self.consume_expr(*condition);
self.consume_expr(*body);
}
Expr::Call { callee, args, is_assignee_expr: _ } => {
self.consume_expr(*callee);
self.consume_exprs(args.iter().copied());
}
Expr::MethodCall { receiver, args, .. } => {
self.consume_expr(*receiver);
self.consume_exprs(args.iter().copied());
}
Expr::Match { expr, arms } => {
self.consume_expr(*expr);
for arm in arms.iter() {
self.consume_expr(arm.expr);
}
}
Expr::Break { expr, label: _ }
| Expr::Return { expr }
| Expr::Yield { expr }
| Expr::Yeet { expr } => {
if let &Some(expr) = expr {
self.consume_expr(expr);
}
}
Expr::RecordLit { fields, spread, .. } => {
if let &Some(expr) = spread {
self.consume_expr(expr);
}
self.consume_exprs(fields.iter().map(|x| x.expr));
}
Expr::Field { expr, name: _ } => self.select_from_expr(*expr),
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
if let Some((f, _)) = self.result.method_resolution(tgt_expr) {
let mutability = 'b: {
if let Some(deref_trait) =
self.resolve_lang_item(LangItem::DerefMut).and_then(|x| x.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
};
if mutability {
self.mutate_expr(*expr);
} else {
self.ref_expr(*expr);
}
} else {
self.select_from_expr(*expr);
}
}
Expr::UnaryOp { expr, op: _ }
| Expr::Array(Array::Repeat { initializer: expr, repeat: _ })
| Expr::Await { expr }
| Expr::Loop { body: expr, label: _ }
| Expr::Let { pat: _, expr }
| Expr::Box { expr }
| Expr::Cast { expr, type_ref: _ } => {
self.consume_expr(*expr);
}
Expr::Ref { expr, rawness: _, mutability } => match mutability {
hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr),
hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr),
},
Expr::BinaryOp { lhs, rhs, op } => {
let Some(op) = op else {
return;
};
if matches!(op, BinaryOp::Assignment { .. }) {
self.mutate_expr(*lhs);
self.consume_expr(*rhs);
return;
}
self.consume_expr(*lhs);
self.consume_expr(*rhs);
}
Expr::Range { lhs, rhs, range_type: _ } => {
if let &Some(expr) = lhs {
self.consume_expr(expr);
}
if let &Some(expr) = rhs {
self.consume_expr(expr);
}
}
Expr::Index { base, index } => {
self.select_from_expr(*base);
self.consume_expr(*index);
}
Expr::Closure { .. } => {
let ty = self.expr_ty(tgt_expr);
let TyKind::Closure(id, _) = ty.kind(Interner) else {
never!("closure type is always closure");
return;
};
let (captures, _) =
self.result.closure_info.get(id).expect(
"We sort closures, so we should always have data for inner closures",
);
let mut cc = mem::take(&mut self.current_captures);
cc.extend(
captures
.iter()
.filter(|x| self.is_upvar(&x.place))
.map(|x| CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind }),
);
self.current_captures = cc;
}
Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ })
| Expr::Tuple { exprs, is_assignee_expr: _ } => {
self.consume_exprs(exprs.iter().copied())
}
Expr::Missing
| Expr::Continue { .. }
| Expr::Path(_)
| Expr::Literal(_)
| Expr::Underscore => (),
}
}
fn expr_ty(&mut self, expr: ExprId) -> Ty {
self.result[expr].clone()
}
fn is_upvar(&self, place: &HirPlace) -> bool {
let b = &self.body[place.local];
if let Some(c) = self.current_closure {
let (_, root) = self.db.lookup_intern_closure(c.into());
return b.is_upvar(root);
}
false
}
fn is_ty_copy(&self, ty: Ty) -> bool {
if let TyKind::Closure(id, _) = ty.kind(Interner) {
// FIXME: We handle closure as a special case, since chalk consider every closure as copy. We
// should probably let chalk know which closures are copy, but I don't know how doing it
// without creating query cycles.
return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true);
}
let crate_id = self.owner.module(self.db.upcast()).krate();
let Some(copy_trait) = self.db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else {
return false;
};
let trait_ref = TyBuilder::trait_ref(self.db, copy_trait).push(ty).build();
let env = self.db.trait_environment_for_body(self.owner);
let goal = Canonical {
value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
binders: CanonicalVarKinds::empty(Interner),
};
self.db.trait_solve(crate_id, None, goal).is_some()
}
fn select_from_expr(&mut self, expr: ExprId) {
self.walk_expr(expr);
}
fn adjust_for_move_closure(&mut self) {
for capture in &mut self.current_captures {
if let Some(first_deref) =
capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref)
{
capture.place.projections.truncate(first_deref);
}
capture.kind = CaptureKind::ByValue;
}
}
fn minimize_captures(&mut self) {
self.current_captures.sort_by_key(|x| x.place.projections.len());
let mut hash_map = HashMap::<HirPlace, usize>::new();
let result = mem::take(&mut self.current_captures);
for item in result {
let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] };
let mut it = item.place.projections.iter();
let prev_index = loop {
if let Some(k) = hash_map.get(&lookup_place) {
break Some(*k);
}
match it.next() {
Some(x) => lookup_place.projections.push(x.clone()),
None => break None,
}
};
match prev_index {
Some(p) => {
self.current_captures[p].kind =
cmp::max(item.kind, self.current_captures[p].kind);
}
None => {
hash_map.insert(item.place.clone(), self.current_captures.len());
self.current_captures.push(item);
}
}
}
}
fn consume_with_pat(
&mut self,
mut place: HirPlace,
mut ty: Ty,
mut bm: BindingAnnotation,
pat: PatId,
) {
match &self.body[pat] {
Pat::Missing | Pat::Wild => (),
Pat::Tuple { args, ellipsis } => {
pattern_matching_dereference(&mut ty, &mut bm, &mut place);
let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
let subst = match ty.kind(Interner) {
TyKind::Tuple(_, s) => s,
_ => return,
};
let fields = subst.iter(Interner).map(|x| x.assert_ty_ref(Interner)).enumerate();
let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
for (arg, (i, ty)) in it {
let mut p = place.clone();
p.projections.push(ProjectionElem::TupleOrClosureField(i));
self.consume_with_pat(p, ty.clone(), bm, *arg);
}
}
Pat::Or(pats) => {
for pat in pats.iter() {
self.consume_with_pat(place.clone(), ty.clone(), bm, *pat);
}
}
Pat::Record { args, .. } => {
pattern_matching_dereference(&mut ty, &mut bm, &mut place);
let subst = match ty.kind(Interner) {
TyKind::Adt(_, s) => s,
_ => return,
};
let Some(variant) = self.result.variant_resolution_for_pat(pat) else {
return;
};
match variant {
VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
self.consume_place(place)
}
VariantId::StructId(s) => {
let vd = &*self.db.struct_data(s).variant_data;
let field_types = self.db.field_types(variant);
for field_pat in args.iter() {
let arg = field_pat.pat;
let Some(local_id) = vd.field(&field_pat.name) else {
continue;
};
let mut p = place.clone();
p.projections.push(ProjectionElem::Field(FieldId {
parent: variant.into(),
local_id,
}));
self.consume_with_pat(
p,
field_types[local_id].clone().substitute(Interner, subst),
bm,
arg,
);
}
}
}
}
Pat::Range { .. }
| Pat::Slice { .. }
| Pat::ConstBlock(_)
| Pat::Path(_)
| Pat::Lit(_) => self.consume_place(place),
Pat::Bind { id, subpat: _ } => {
let mode = self.body.bindings[*id].mode;
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
bm = mode;
}
let capture_kind = match bm {
BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
self.consume_place(place);
return;
}
BindingAnnotation::Ref => BorrowKind::Shared,
BindingAnnotation::RefMut => BorrowKind::Mut { allow_two_phase_borrow: false },
};
self.add_capture(place, CaptureKind::ByRef(capture_kind));
}
Pat::TupleStruct { path: _, args, ellipsis } => {
pattern_matching_dereference(&mut ty, &mut bm, &mut place);
let subst = match ty.kind(Interner) {
TyKind::Adt(_, s) => s,
_ => return,
};
let Some(variant) = self.result.variant_resolution_for_pat(pat) else {
return;
};
match variant {
VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
self.consume_place(place)
}
VariantId::StructId(s) => {
let vd = &*self.db.struct_data(s).variant_data;
let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
let fields = vd.fields().iter();
let it =
al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
let field_types = self.db.field_types(variant);
for (arg, (i, _)) in it {
let mut p = place.clone();
p.projections.push(ProjectionElem::Field(FieldId {
parent: variant.into(),
local_id: i,
}));
self.consume_with_pat(
p,
field_types[i].clone().substitute(Interner, subst),
bm,
*arg,
);
}
}
}
}
Pat::Ref { pat, mutability: _ } => {
if let Some((inner, _, _)) = ty.as_reference() {
ty = inner.clone();
place.projections.push(ProjectionElem::Deref);
self.consume_with_pat(place, ty, bm, *pat)
}
}
Pat::Box { .. } => (), // not supported
}
}
fn consume_exprs(&mut self, exprs: impl Iterator<Item = ExprId>) {
for expr in exprs {
self.consume_expr(expr);
}
}
fn closure_kind(&self) -> FnTrait {
let mut r = FnTrait::Fn;
for x in &self.current_captures {
r = cmp::min(
r,
match &x.kind {
CaptureKind::ByRef(BorrowKind::Unique | BorrowKind::Mut { .. }) => {
FnTrait::FnMut
}
CaptureKind::ByRef(BorrowKind::Shallow | BorrowKind::Shared) => FnTrait::Fn,
CaptureKind::ByValue => FnTrait::FnOnce,
},
)
}
r
}
fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait {
let (_, root) = self.db.lookup_intern_closure(closure.into());
self.current_closure = Some(closure);
let Expr::Closure { body, capture_by, .. } = &self.body[root] else {
unreachable!("Closure expression id is always closure");
};
self.consume_expr(*body);
for item in &self.current_captures {
if matches!(item.kind, CaptureKind::ByRef(BorrowKind::Mut { .. })) {
// FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in
// MIR. I didn't do that due duplicate diagnostics.
self.result.mutated_bindings_in_closure.insert(item.place.local);
}
}
// closure_kind should be done before adjust_for_move_closure
let closure_kind = self.closure_kind();
match capture_by {
CaptureBy::Value => self.adjust_for_move_closure(),
CaptureBy::Ref => (),
}
self.minimize_captures();
let result = mem::take(&mut self.current_captures);
let captures = result.into_iter().map(|x| x.with_ty(self)).collect::<Vec<_>>();
self.result.closure_info.insert(closure, (captures, closure_kind));
closure_kind
}
pub(crate) fn infer_closures(&mut self) {
let deferred_closures = self.sort_closures();
for (closure, exprs) in deferred_closures.into_iter().rev() {
self.current_captures = vec![];
let kind = self.analyze_closure(closure);
for (derefed_callee, callee_ty, params, expr) in exprs {
if let &Expr::Call { callee, .. } = &self.body[expr] {
let mut adjustments =
self.result.expr_adjustments.remove(&callee).unwrap_or_default();
self.write_fn_trait_method_resolution(
kind,
&derefed_callee,
&mut adjustments,
&callee_ty,
&params,
expr,
);
self.result.expr_adjustments.insert(callee, adjustments);
}
}
}
}
/// We want to analyze some closures before others, to have a correct analysis:
/// * We should analyze nested closures before the parent, since the parent should capture some of
/// the things that its children captures.
/// * If a closure calls another closure, we need to analyze the callee, to find out how we should
/// capture it (e.g. by move for FnOnce)
///
/// These dependencies are collected in the main inference. We do a topological sort in this function. It
/// will consume the `deferred_closures` field and return its content in a sorted vector.
fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>)> {
let mut deferred_closures = mem::take(&mut self.deferred_closures);
let mut dependents_count: FxHashMap<ClosureId, usize> =
deferred_closures.keys().map(|x| (*x, 0)).collect();
for (_, deps) in &self.closure_dependecies {
for dep in deps {
*dependents_count.entry(*dep).or_default() += 1;
}
}
let mut queue: Vec<_> =
deferred_closures.keys().copied().filter(|x| dependents_count[x] == 0).collect();
let mut result = vec![];
while let Some(x) = queue.pop() {
if let Some(d) = deferred_closures.remove(&x) {
result.push((x, d));
}
for dep in self.closure_dependecies.get(&x).into_iter().flat_map(|x| x.iter()) {
let cnt = dependents_count.get_mut(dep).unwrap();
*cnt -= 1;
if *cnt == 0 {
queue.push(*dep);
}
}
}
result
}
}
fn apply_adjusts_to_place(mut r: HirPlace, adjustments: &[Adjustment]) -> Option<HirPlace> {
for adj in adjustments {
match &adj.kind {
Adjust::Deref(None) => {
r.projections.push(ProjectionElem::Deref);
}
_ => return None,
}
}
Some(r)
}
fn pattern_matching_dereference(
cond_ty: &mut Ty,
binding_mode: &mut BindingAnnotation,
cond_place: &mut HirPlace,
) {
let cnt = pattern_matching_dereference_count(cond_ty, binding_mode);
cond_place.projections.extend((0..cnt).map(|_| ProjectionElem::Deref));
}

View File

@ -7,9 +7,9 @@
use std::{iter, sync::Arc};
use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyVariableKind};
use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyKind, TyVariableKind};
use hir_def::{
expr::ExprId,
hir::ExprId,
lang_item::{LangItem, LangItemTarget},
};
use stdx::always;
@ -22,7 +22,7 @@ use crate::{
TypeError, TypeMismatch,
},
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner,
Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
};
use super::unify::InferenceTable;
@ -111,6 +111,8 @@ impl CoerceMany {
// pointers to have a chance at getting a match. See
// https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
let sig = match (self.merged_ty().kind(Interner), expr_ty.kind(Interner)) {
(TyKind::FnDef(x, _), TyKind::FnDef(y, _)) if x == y => None,
(TyKind::Closure(x, _), TyKind::Closure(y, _)) if x == y => None,
(TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => {
// FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
// we should be coercing the closure to a fn pointer of the safety of the FnDef

View File

@ -10,10 +10,10 @@ use chalk_ir::{
cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyKind, TyVariableKind,
};
use hir_def::{
expr::{
generics::TypeOrConstParamData,
hir::{
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
},
generics::TypeOrConstParamData,
lang_item::LangItem,
path::{GenericArg, GenericArgs},
BlockId, ConstParamId, FieldId, ItemContainerId, Lookup,
@ -86,10 +86,10 @@ impl<'a> InferenceContext<'a> {
}
}
pub(super) fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
let ty = self.infer_expr_inner(expr, expected);
// While we don't allow *arbitrary* coercions here, we *do* allow
// coercions from ! to `expected`.
// coercions from `!` to `expected`.
if ty.is_never() {
if let Some(adjustments) = self.result.expr_adjustments.get(&expr) {
return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments {
@ -99,13 +99,22 @@ impl<'a> InferenceContext<'a> {
};
}
let adj_ty = self.table.new_type_var();
self.write_expr_adj(
expr,
vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty.clone() }],
);
adj_ty
if let Some(target) = expected.only_has_type(&mut self.table) {
self.coerce(Some(expr), &ty, &target)
.expect("never-to-any coercion should always succeed")
} else {
ty
}
} else {
if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
let could_unify = self.unify(&ty, &expected_ty);
if !could_unify {
self.result.type_mismatches.insert(
expr.into(),
TypeMismatch { expected: expected_ty, actual: ty.clone() },
);
}
}
ty
}
}
@ -212,7 +221,7 @@ impl<'a> InferenceContext<'a> {
self.diverges = Diverges::Maybe;
TyBuilder::unit()
}
Expr::Closure { body, args, ret_type, arg_types, closure_kind } => {
Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => {
assert_eq!(args.len(), arg_types.len());
let mut sig_tys = Vec::with_capacity(arg_types.len() + 1);
@ -247,7 +256,7 @@ impl<'a> InferenceContext<'a> {
})
.intern(Interner);
let (ty, resume_yield_tys) = match closure_kind {
let (id, ty, resume_yield_tys) = match closure_kind {
ClosureKind::Generator(_) => {
// FIXME: report error when there are more than 1 parameter.
let resume_ty = match sig_tys.first() {
@ -267,7 +276,7 @@ impl<'a> InferenceContext<'a> {
let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into();
let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner);
(generator_ty, Some((resume_ty, yield_ty)))
(None, generator_ty, Some((resume_ty, yield_ty)))
}
ClosureKind::Closure | ClosureKind::Async => {
let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
@ -276,8 +285,11 @@ impl<'a> InferenceContext<'a> {
Substitution::from1(Interner, sig_ty.clone()),
)
.intern(Interner);
(closure_ty, None)
self.deferred_closures.entry(closure_id).or_default();
if let Some(c) = self.current_closure {
self.closure_dependecies.entry(c).or_default().push(closure_id);
}
(Some(closure_id), closure_ty, None)
}
};
@ -293,6 +305,7 @@ impl<'a> InferenceContext<'a> {
// FIXME: lift these out into a struct
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let prev_closure = mem::replace(&mut self.current_closure, id);
let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
let prev_ret_coercion =
mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty)));
@ -306,6 +319,7 @@ impl<'a> InferenceContext<'a> {
self.diverges = prev_diverges;
self.return_ty = prev_ret_ty;
self.return_coercion = prev_ret_coercion;
self.current_closure = prev_closure;
self.resume_yield_tys = prev_resume_yield_tys;
ty
@ -331,43 +345,28 @@ impl<'a> InferenceContext<'a> {
let (param_tys, ret_ty) = match res {
Some((func, params, ret_ty)) => {
let mut adjustments = auto_deref_adjust_steps(&derefs);
if let TyKind::Closure(c, _) =
self.table.resolve_completely(callee_ty.clone()).kind(Interner)
{
if let Some(par) = self.current_closure {
self.closure_dependecies.entry(par).or_default().push(*c);
}
self.deferred_closures.entry(*c).or_default().push((
derefed_callee.clone(),
callee_ty.clone(),
params.clone(),
tgt_expr,
));
}
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.table.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)
}
self.write_fn_trait_method_resolution(
fn_x,
&derefed_callee,
&mut adjustments,
&callee_ty,
&params,
tgt_expr,
);
}
self.write_expr_adj(*callee, adjustments);
(params, ret_ty)
@ -459,8 +458,8 @@ impl<'a> InferenceContext<'a> {
self.resolver.reset_to_guard(g);
ty
}
Expr::Continue { label } => {
if let None = find_continuable(&mut self.breakables, label.as_ref()) {
&Expr::Continue { label } => {
if let None = find_continuable(&mut self.breakables, label) {
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
expr: tgt_expr,
is_break: false,
@ -469,9 +468,9 @@ impl<'a> InferenceContext<'a> {
};
self.result.standard_types.never.clone()
}
Expr::Break { expr, label } => {
let val_ty = if let Some(expr) = *expr {
let opt_coerce_to = match find_breakable(&mut self.breakables, label.as_ref()) {
&Expr::Break { expr, label } => {
let val_ty = if let Some(expr) = expr {
let opt_coerce_to = match find_breakable(&mut self.breakables, label) {
Some(ctxt) => match &ctxt.coerce {
Some(coerce) => coerce.expected_ty(),
None => {
@ -490,13 +489,13 @@ impl<'a> InferenceContext<'a> {
TyBuilder::unit()
};
match find_breakable(&mut self.breakables, label.as_ref()) {
match find_breakable(&mut self.breakables, label) {
Some(ctxt) => match ctxt.coerce.take() {
Some(mut coerce) => {
coerce.coerce(self, *expr, &val_ty);
coerce.coerce(self, expr, &val_ty);
// Avoiding borrowck
let ctxt = find_breakable(&mut self.breakables, label.as_ref())
let ctxt = find_breakable(&mut self.breakables, label)
.expect("breakable stack changed during coercion");
ctxt.may_break = true;
ctxt.coerce = Some(coerce);
@ -897,6 +896,41 @@ impl<'a> InferenceContext<'a> {
TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)).intern(Interner)
}
pub(crate) fn write_fn_trait_method_resolution(
&mut self,
fn_x: FnTrait,
derefed_callee: &Ty,
adjustments: &mut Vec<Adjustment>,
callee_ty: &Ty,
params: &Vec<Ty>,
tgt_expr: ExprId,
) {
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 Some(trait_) = fn_x.get_id(self.db, self.table.trait_env.krate) else {
return;
};
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());
}
}
fn infer_expr_array(
&mut self,
array: &Array,
@ -1900,7 +1934,6 @@ impl<'a> InferenceContext<'a> {
cb: impl FnOnce(&mut Self) -> T,
) -> (Option<Ty>, T) {
self.breakables.push({
let label = label.map(|label| self.body[label].name.clone());
BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label }
});
let res = cb(self);

View File

@ -3,7 +3,7 @@
use chalk_ir::Mutability;
use hir_def::{
expr::{Array, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
lang_item::LangItem,
};
use hir_expand::name;
@ -80,6 +80,9 @@ impl<'a> InferenceContext<'a> {
self.infer_mut_expr(*expr, m);
for arm in arms.iter() {
self.infer_mut_expr(arm.expr, Mutability::Not);
if let Some(g) = arm.guard {
self.infer_mut_expr(g, Mutability::Not);
}
}
}
Expr::Yield { expr }
@ -158,14 +161,19 @@ impl<'a> InferenceContext<'a> {
let mutability = lower_to_chalk_mutability(*mutability);
self.infer_mut_expr(*expr, mutability);
}
Expr::BinaryOp { lhs, rhs, op: Some(BinaryOp::Assignment { .. }) } => {
self.infer_mut_expr(*lhs, Mutability::Mut);
self.infer_mut_expr(*rhs, Mutability::Not);
}
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::Closure { body, .. } => {
self.infer_mut_expr(*body, Mutability::Not);
}
Expr::Tuple { exprs, is_assignee_expr: _ }
| Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) => {
self.infer_mut_not_expr_iter(exprs.iter().copied());

View File

@ -5,7 +5,7 @@ use std::iter::repeat_with;
use chalk_ir::Mutability;
use hir_def::{
body::Body,
expr::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
path::Path,
};
use hir_expand::name::Name;

View File

@ -6,8 +6,8 @@ use chalk_ir::{
DebruijnIndex,
};
use hir_def::{
adt::VariantData, attr::Attrs, visibility::Visibility, AdtId, EnumVariantId, HasModule, Lookup,
ModuleId, VariantId,
attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule,
Lookup, ModuleId, VariantId,
};
use rustc_hash::FxHashSet;

View File

@ -1,6 +1,6 @@
//! Functions to detect special lang items
use hir_def::{adt::StructFlags, lang_item::LangItem, AdtId};
use hir_def::{data::adt::StructFlags, lang_item::LangItem, AdtId};
use hir_expand::name::Name;
use crate::db::HirDatabase;

View File

@ -229,11 +229,24 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
}
}
}
TyKind::Closure(_, _) | TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
TyKind::Closure(c, _) => {
let (def, _) = db.lookup_intern_closure((*c).into());
let infer = db.infer(def);
let (captures, _) = infer.closure_info(c);
let fields = captures
.iter()
.map(|x| layout_of_ty(db, &x.ty, krate))
.collect::<Result<Vec<_>, _>>()?;
let fields = fields.iter().collect::<Vec<_>>();
let fields = fields.iter().collect::<Vec<_>>();
cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized)
.ok_or(LayoutError::Unknown)?
}
TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
return Err(LayoutError::NotImplemented)
}
TyKind::Error => return Err(LayoutError::HasErrorType),
TyKind::AssociatedType(_, _)
| TyKind::Error
| TyKind::Alias(_)
| TyKind::Placeholder(_)
| TyKind::BoundVar(_)

View File

@ -3,7 +3,7 @@
use std::ops::Bound;
use hir_def::{
adt::VariantData,
data::adt::VariantData,
layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId,
};

View File

@ -11,6 +11,8 @@ use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution};
use super::layout_of_ty;
mod closure;
fn current_machine_data_layout() -> String {
project_model::target_data_layout::get(None, None, &HashMap::default()).unwrap()
}
@ -81,8 +83,8 @@ fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64)
#[track_caller]
fn check_size_and_align_expr(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
let l = eval_expr(ra_fixture, minicore).unwrap();
assert_eq!(l.size.bytes(), size);
assert_eq!(l.align.abi.bytes(), align);
assert_eq!(l.size.bytes(), size, "size mismatch");
assert_eq!(l.align.abi.bytes(), align, "align mismatch");
}
#[track_caller]
@ -118,13 +120,31 @@ macro_rules! size_and_align {
};
}
#[macro_export]
macro_rules! size_and_align_expr {
(minicore: $($x:tt),*; stmts: [$($s:tt)*] $($t:tt)*) => {
{
#[allow(dead_code)]
#[allow(unused_must_use)]
#[allow(path_statements)]
{
$($s)*
let val = { $($t)* };
$crate::layout::tests::check_size_and_align_expr(
&format!("{{ {} let val = {{ {} }}; val }}", stringify!($($s)*), stringify!($($t)*)),
&format!("//- minicore: {}\n", stringify!($($x),*)),
::std::mem::size_of_val(&val) as u64,
::std::mem::align_of_val(&val) as u64,
);
}
}
};
($($t:tt)*) => {
{
#[allow(dead_code)]
{
let val = { $($t)* };
check_size_and_align_expr(
$crate::layout::tests::check_size_and_align_expr(
stringify!($($t)*),
"",
::std::mem::size_of_val(&val) as u64,
@ -212,6 +232,45 @@ fn return_position_impl_trait() {
fn foo() -> (impl T, impl T, impl T) { (2i64, 5i32, 7i32) }
foo()
}
size_and_align_expr! {
minicore: iterators;
stmts: []
trait Tr {}
impl Tr for i32 {}
fn foo() -> impl Iterator<Item = impl Tr> {
[1, 2, 3].into_iter()
}
let mut iter = foo();
let item = iter.next();
(iter, item)
}
size_and_align_expr! {
minicore: future;
stmts: []
use core::{future::Future, task::{Poll, Context}, pin::pin};
use std::{task::Wake, sync::Arc};
trait Tr {}
impl Tr for i32 {}
async fn f() -> impl Tr {
2
}
fn unwrap_fut<T>(inp: impl Future<Output = T>) -> Poll<T> {
// In a normal test we could use `loop {}` or `panic!()` here,
// but rustc actually runs this code.
let pinned = pin!(inp);
struct EmptyWaker;
impl Wake for EmptyWaker {
fn wake(self: Arc<Self>) {
}
}
let waker = Arc::new(EmptyWaker).into();
let mut context = Context::from_waker(&waker);
let x = pinned.poll(&mut context);
x
}
let x = unwrap_fut(f());
x
}
size_and_align_expr! {
struct Foo<T>(T, T, (T, T));
trait T {}

View File

@ -0,0 +1,175 @@
use crate::size_and_align_expr;
#[test]
fn zero_capture_simple() {
size_and_align_expr! {
|x: i32| x + 2
}
}
#[test]
fn move_simple() {
size_and_align_expr! {
minicore: copy;
stmts: []
let y: i32 = 5;
move |x: i32| {
x + y
}
}
}
#[test]
fn ref_simple() {
size_and_align_expr! {
minicore: copy;
stmts: [
let y: i32 = 5;
]
|x: i32| {
x + y
}
}
size_and_align_expr! {
minicore: copy;
stmts: [
let mut y: i32 = 5;
]
|x: i32| {
y = y + x;
y
}
}
size_and_align_expr! {
minicore: copy;
stmts: [
struct X(i32, i64);
let x: X = X(2, 6);
]
|| {
x
}
}
}
#[test]
fn ref_then_mut_then_move() {
size_and_align_expr! {
minicore: copy;
stmts: [
struct X(i32, i64);
let mut x: X = X(2, 6);
]
|| {
&x;
&mut x;
x;
}
}
}
#[test]
fn nested_closures() {
size_and_align_expr! {
|| {
|| {
|| {
let x = 2;
move || {
move || {
x
}
}
}
}
}
}
}
#[test]
fn capture_specific_fields() {
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
y.0 + x + (y.2 .0 as i64)
}
}
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
let _ = &y;
y.0 + x + (y.2 .0 as i64)
}
}
size_and_align_expr! {
minicore: copy;
stmts: [
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
]
let y = &y;
move |x: i64| {
y.0 + x + (y.2 .0 as i64)
}
}
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
let X(a, _, (b, _)) = y;
a + x + (b as i64)
}
}
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y = &&X(2, 5, (7, 3));
move |x: i64| {
let X(a, _, (b, _)) = y;
*a + x + (*b as i64)
}
}
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
match y {
X(a, _, (b, _)) => a + x + (b as i64),
}
}
}
size_and_align_expr! {
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
let X(a @ 2, _, (b, _)) = y else { return 5 };
a + x + (b as i64)
}
}
}
#[test]
fn ellipsis_pattern() {
size_and_align_expr! {
struct X(i8, u16, i32, u64, i128, u8);
let y: X = X(1, 2, 3, 4, 5, 6);
move |_: i64| {
let X(_a, .., _b, _c) = y;
}
}
size_and_align_expr! {
struct X { a: i32, b: u8, c: i128}
let y: X = X { a: 1, b: 2, c: 3 };
move |_: i64| {
let X { a, b, .. } = y;
_ = (a, b);
}
}
size_and_align_expr! {
let y: (&&&(i8, u16, i32, u64, i128, u8), u16, i32, u64, i128, u8) = (&&&(1, 2, 3, 4, 5, 6), 2, 3, 4, 5, 6);
move |_: i64| {
let ((_a, .., _b, _c), .., _e, _f) = y;
}
}
}

View File

@ -44,7 +44,7 @@ use chalk_ir::{
NoSolution, TyData,
};
use either::Either;
use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
use hir_def::{hir::ExprId, type_ref::Rawness, TypeOrConstParamId};
use hir_expand::name;
use la_arena::{Arena, Idx};
use mir::MirEvalError;

View File

@ -18,9 +18,9 @@ use chalk_ir::{
use either::Either;
use hir_def::{
adt::StructKind,
body::{Expander, LowerCtx},
body::Expander,
builtin_type::BuiltinType,
data::adt::StructKind,
generics::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
},
@ -380,7 +380,7 @@ impl<'a> TyLoweringContext<'a> {
let macro_call = macro_call.to_node(self.db.upcast());
match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
let ctx = LowerCtx::new(self.db.upcast(), expander.current_file_id());
let ctx = expander.ctx(self.db.upcast());
let type_ref = TypeRef::from_ast(&ctx, expanded);
drop(expander);
@ -988,7 +988,7 @@ impl<'a> TyLoweringContext<'a> {
// ignore `T: Drop` or `T: Destruct` bounds.
// - `T: ~const Drop` has a special meaning in Rust 1.61 that we don't implement.
// (So ideally, we'd only ignore `~const Drop` here)
// - `Destruct` impls are built-in in 1.62 (current nightlies as of 08-04-2022), so until
// - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until
// the builtin impls are supported by Chalk, we ignore them here.
if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) {
if matches!(lang, LangItem::Drop | LangItem::Destruct) {
@ -1082,23 +1082,23 @@ impl<'a> TyLoweringContext<'a> {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution,
};
let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity(
let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
);
if let Some(type_ref) = &binding.type_ref {
let ty = self.lower_ty(type_ref);
let alias_eq =
AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
preds.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
}
for bound in binding.bounds.iter() {
preds.extend(self.lower_type_bound(
predicates.extend(self.lower_type_bound(
bound,
TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
false,
));
}
preds
predicates
})
}
@ -1165,7 +1165,7 @@ impl<'a> TyLoweringContext<'a> {
return None;
}
// As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
// As multiple occurrences of the same auto traits *are* permitted, we deduplicate the
// bounds. We shouldn't have repeated elements besides auto traits at this point.
bounds.dedup();
@ -1634,7 +1634,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
let ctx_params = TyLoweringContext::new(db, &resolver)
.with_impl_trait_mode(ImplTraitLoweringMode::Variable)
.with_type_param_mode(ParamLoweringMode::Variable);
let params = data.params.iter().map(|(_, tr)| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
let params = data.params.iter().map(|tr| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
let ctx_ret = TyLoweringContext::new(db, &resolver)
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);

View File

@ -7,9 +7,11 @@ use std::{ops::ControlFlow, sync::Arc};
use base_db::{CrateId, Edition};
use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause};
use hir_def::{
adt::StructFlags, data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId,
ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId,
TraitId,
data::{adt::StructFlags, ImplData},
item_scope::ItemScope,
nameres::DefMap,
AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup,
ModuleDefId, ModuleId, TraitId,
};
use hir_expand::name::Name;
use rustc_hash::{FxHashMap, FxHashSet};
@ -952,7 +954,14 @@ fn iterate_method_candidates_with_autoref(
)
};
iterate_method_candidates_by_receiver(receiver_ty, first_adjustment.clone())?;
let mut maybe_reborrowed = first_adjustment.clone();
if let Some((_, _, m)) = receiver_ty.value.as_reference() {
// Prefer reborrow of references to move
maybe_reborrowed.autoref = Some(m);
maybe_reborrowed.autoderefs += 1;
}
iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?;
let refed = Canonical {
value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone())

View File

@ -3,11 +3,12 @@
use std::{fmt::Display, iter};
use crate::{
infer::PointerCast, Const, ConstScalar, InferenceResult, Interner, MemoryMap, Substitution, Ty,
db::HirDatabase, infer::PointerCast, ClosureId, Const, ConstScalar, InferenceResult, Interner,
MemoryMap, Substitution, Ty, TyKind,
};
use chalk_ir::Mutability;
use hir_def::{
expr::{BindingId, Expr, ExprId, Ordering, PatId},
hir::{BindingId, Expr, ExprId, Ordering, PatId},
DefWithBodyId, FieldId, UnionId, VariantId,
};
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
@ -19,9 +20,11 @@ mod pretty;
pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError};
pub use lower::{lower_to_mir, mir_body_query, mir_body_recover, MirLowerError};
pub use lower::{
lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
};
use smallvec::{smallvec, SmallVec};
use stdx::impl_from;
use stdx::{impl_from, never};
use super::consteval::{intern_const_scalar, try_const_usize};
@ -89,11 +92,12 @@ impl Operand {
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ProjectionElem<V, T> {
Deref,
Field(FieldId),
TupleField(usize),
// FIXME: get rid of this, and use FieldId for tuples and closures
TupleOrClosureField(usize),
Index(V),
ConstantIndex { offset: u64, min_length: u64, from_end: bool },
Subslice { from: u64, to: u64, from_end: bool },
@ -101,6 +105,63 @@ pub enum ProjectionElem<V, T> {
OpaqueCast(T),
}
impl<V, T> ProjectionElem<V, T> {
pub fn projected_ty(
&self,
base: Ty,
db: &dyn HirDatabase,
closure_field: impl FnOnce(ClosureId, usize) -> Ty,
) -> Ty {
match self {
ProjectionElem::Deref => match &base.data(Interner).kind {
TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
_ => {
never!("Overloaded deref is not a projection");
return TyKind::Error.intern(Interner);
}
},
ProjectionElem::Field(f) => match &base.data(Interner).kind {
TyKind::Adt(_, subst) => {
db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
}
_ => {
never!("Only adt has field");
return TyKind::Error.intern(Interner);
}
},
ProjectionElem::TupleOrClosureField(f) => match &base.data(Interner).kind {
TyKind::Tuple(_, subst) => subst
.as_slice(Interner)
.get(*f)
.map(|x| x.assert_ty_ref(Interner))
.cloned()
.unwrap_or_else(|| {
never!("Out of bound tuple field");
TyKind::Error.intern(Interner)
}),
TyKind::Closure(id, _) => closure_field(*id, *f),
_ => {
never!("Only tuple or closure has tuple or closure field");
return TyKind::Error.intern(Interner);
}
},
ProjectionElem::Index(_) => match &base.data(Interner).kind {
TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(),
_ => {
never!("Overloaded index is not a projection");
return TyKind::Error.intern(Interner);
}
},
ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::OpaqueCast(_) => {
never!("We don't emit these yet");
return TyKind::Error.intern(Interner);
}
}
}
}
type PlaceElem = ProjectionElem<LocalId, Ty>;
#[derive(Debug, Clone, PartialEq, Eq)]
@ -123,7 +184,7 @@ pub enum AggregateKind {
Tuple(Ty),
Adt(VariantId, Substitution),
Union(UnionId, FieldId),
//Closure(LocalDefId, SubstsRef),
Closure(Ty),
//Generator(LocalDefId, SubstsRef, Movability),
}
@ -418,7 +479,7 @@ pub enum Terminator {
},
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
pub enum BorrowKind {
/// Data must be immutable and is aliasable.
Shared,
@ -588,32 +649,32 @@ impl Display for BinOp {
}
}
impl From<hir_def::expr::ArithOp> for BinOp {
fn from(value: hir_def::expr::ArithOp) -> Self {
impl From<hir_def::hir::ArithOp> for BinOp {
fn from(value: hir_def::hir::ArithOp) -> Self {
match value {
hir_def::expr::ArithOp::Add => BinOp::Add,
hir_def::expr::ArithOp::Mul => BinOp::Mul,
hir_def::expr::ArithOp::Sub => BinOp::Sub,
hir_def::expr::ArithOp::Div => BinOp::Div,
hir_def::expr::ArithOp::Rem => BinOp::Rem,
hir_def::expr::ArithOp::Shl => BinOp::Shl,
hir_def::expr::ArithOp::Shr => BinOp::Shr,
hir_def::expr::ArithOp::BitXor => BinOp::BitXor,
hir_def::expr::ArithOp::BitOr => BinOp::BitOr,
hir_def::expr::ArithOp::BitAnd => BinOp::BitAnd,
hir_def::hir::ArithOp::Add => BinOp::Add,
hir_def::hir::ArithOp::Mul => BinOp::Mul,
hir_def::hir::ArithOp::Sub => BinOp::Sub,
hir_def::hir::ArithOp::Div => BinOp::Div,
hir_def::hir::ArithOp::Rem => BinOp::Rem,
hir_def::hir::ArithOp::Shl => BinOp::Shl,
hir_def::hir::ArithOp::Shr => BinOp::Shr,
hir_def::hir::ArithOp::BitXor => BinOp::BitXor,
hir_def::hir::ArithOp::BitOr => BinOp::BitOr,
hir_def::hir::ArithOp::BitAnd => BinOp::BitAnd,
}
}
}
impl From<hir_def::expr::CmpOp> for BinOp {
fn from(value: hir_def::expr::CmpOp) -> Self {
impl From<hir_def::hir::CmpOp> for BinOp {
fn from(value: hir_def::hir::CmpOp) -> Self {
match value {
hir_def::expr::CmpOp::Eq { negated: false } => BinOp::Eq,
hir_def::expr::CmpOp::Eq { negated: true } => BinOp::Ne,
hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
hir_def::hir::CmpOp::Eq { negated: false } => BinOp::Eq,
hir_def::hir::CmpOp::Eq { negated: true } => BinOp::Ne,
hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
}
}
}
@ -847,6 +908,87 @@ pub struct MirBody {
pub arg_count: usize,
pub binding_locals: ArenaMap<BindingId, LocalId>,
pub param_locals: Vec<LocalId>,
/// This field stores the closures directly owned by this body. It is used
/// in traversing every mir body.
pub closures: Vec<ClosureId>,
}
impl MirBody {
fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) {
fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) {
match op {
Operand::Copy(p) | Operand::Move(p) => {
f(p);
}
Operand::Constant(_) => (),
}
}
for (_, block) in self.basic_blocks.iter_mut() {
for statement in &mut block.statements {
match &mut statement.kind {
StatementKind::Assign(p, r) => {
f(p);
match r {
Rvalue::ShallowInitBox(o, _)
| Rvalue::UnaryOp(_, o)
| Rvalue::Cast(_, o, _)
| Rvalue::Use(o) => for_operand(o, &mut f),
Rvalue::CopyForDeref(p)
| Rvalue::Discriminant(p)
| Rvalue::Len(p)
| Rvalue::Ref(_, p) => f(p),
Rvalue::CheckedBinaryOp(_, o1, o2) => {
for_operand(o1, &mut f);
for_operand(o2, &mut f);
}
Rvalue::Aggregate(_, ops) => {
for op in ops {
for_operand(op, &mut f);
}
}
}
}
StatementKind::Deinit(p) => f(p),
StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Nop => (),
}
}
match &mut block.terminator {
Some(x) => match x {
Terminator::SwitchInt { discr, .. } => for_operand(discr, &mut f),
Terminator::FalseEdge { .. }
| Terminator::FalseUnwind { .. }
| Terminator::Goto { .. }
| Terminator::Resume
| Terminator::GeneratorDrop
| Terminator::Abort
| Terminator::Return
| Terminator::Unreachable => (),
Terminator::Drop { place, .. } => {
f(place);
}
Terminator::DropAndReplace { place, value, .. } => {
f(place);
for_operand(value, &mut f);
}
Terminator::Call { func, args, destination, .. } => {
for_operand(func, &mut f);
args.iter_mut().for_each(|x| for_operand(x, &mut f));
f(destination);
}
Terminator::Assert { cond, .. } => {
for_operand(cond, &mut f);
}
Terminator::Yield { value, resume_arg, .. } => {
for_operand(value, &mut f);
f(resume_arg);
}
},
None => (),
}
}
}
}
fn const_as_usize(c: &Const) -> usize {

View File

@ -3,13 +3,13 @@
// Currently it is an ad-hoc implementation, only useful for mutability analysis. Feel free to remove all of these
// if needed for implementing a proper borrow checker.
use std::sync::Arc;
use std::{iter, sync::Arc};
use hir_def::DefWithBodyId;
use la_arena::ArenaMap;
use stdx::never;
use crate::db::HirDatabase;
use crate::{db::HirDatabase, ClosureId};
use super::{
BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
@ -29,14 +29,48 @@ pub struct BorrowckResult {
pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
}
fn all_mir_bodies(
db: &dyn HirDatabase,
def: DefWithBodyId,
) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
fn for_closure(
db: &dyn HirDatabase,
c: ClosureId,
) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
match db.mir_body_for_closure(c) {
Ok(body) => {
let closures = body.closures.clone();
Box::new(
iter::once(Ok(body))
.chain(closures.into_iter().flat_map(|x| for_closure(db, x))),
)
}
Err(e) => Box::new(iter::once(Err(e))),
}
}
match db.mir_body(def) {
Ok(body) => {
let closures = body.closures.clone();
Box::new(
iter::once(Ok(body)).chain(closures.into_iter().flat_map(|x| for_closure(db, x))),
)
}
Err(e) => Box::new(iter::once(Err(e))),
}
}
pub fn borrowck_query(
db: &dyn HirDatabase,
def: DefWithBodyId,
) -> Result<Arc<BorrowckResult>, MirLowerError> {
) -> Result<Arc<[BorrowckResult]>, MirLowerError> {
let _p = profile::span("borrowck_query");
let body = db.mir_body(def)?;
let r = BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body };
Ok(Arc::new(r))
let r = all_mir_bodies(db, def)
.map(|body| {
let body = body?;
Ok(BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body })
})
.collect::<Result<Vec<_>, MirLowerError>>()?;
Ok(r.into())
}
fn is_place_direct(lvalue: &Place) -> bool {
@ -60,7 +94,7 @@ fn place_case(lvalue: &Place) -> ProjectionCase {
ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Field(_)
| ProjectionElem::TupleField(_)
| ProjectionElem::TupleOrClosureField(_)
| ProjectionElem::Index(_) => {
is_part_of = true;
}

View File

@ -25,8 +25,8 @@ use crate::{
mapping::from_chalk,
method_resolution::{is_dyn_method, lookup_impl_method},
traits::FnTrait,
CallableDefId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, Substitution,
TraitEnvironment, Ty, TyBuilder, TyExt,
CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
};
use super::{
@ -92,6 +92,7 @@ pub struct Evaluator<'a> {
enum Address {
Stack(usize),
Heap(usize),
Invalid(usize),
}
use Address::*;
@ -169,8 +170,10 @@ impl Address {
fn from_usize(x: usize) -> Self {
if x > usize::MAX / 2 {
Stack(x - usize::MAX / 2)
} else if x > usize::MAX / 4 {
Heap(x - usize::MAX / 4)
} else {
Heap(x)
Invalid(x)
}
}
@ -181,7 +184,8 @@ impl Address {
fn to_usize(&self) -> usize {
let as_num = match self {
Stack(x) => *x + usize::MAX / 2,
Heap(x) => *x,
Heap(x) => *x + usize::MAX / 4,
Invalid(x) => *x,
};
as_num
}
@ -190,6 +194,7 @@ impl Address {
match self {
Stack(x) => Stack(f(*x)),
Heap(x) => Heap(f(*x)),
Invalid(x) => Invalid(f(*x)),
}
}
@ -209,6 +214,7 @@ pub enum MirEvalError {
UndefinedBehavior(&'static str),
Panic(String),
MirLowerError(FunctionId, MirLowerError),
MirLowerErrorForClosure(ClosureId, MirLowerError),
TypeIsUnsized(Ty, &'static str),
NotSupported(String),
InvalidConst(Const),
@ -238,6 +244,9 @@ impl std::fmt::Debug for MirEvalError {
Self::MirLowerError(arg0, arg1) => {
f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
}
Self::MirLowerErrorForClosure(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) => {
@ -355,16 +364,15 @@ impl Evaluator<'_> {
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 {
let prev_ty = ty.clone();
ty = proj.projected_ty(ty, self.db, |c, f| {
let (def, _) = self.db.lookup_intern_closure(c.into());
let infer = self.db.infer(def);
let (captures, _) = infer.closure_info(&c);
captures.get(f).expect("broken closure field").ty.clone()
});
match proj {
ProjectionElem::Deref => {
ty = match &ty.data(Interner).kind {
TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
_ => {
return Err(MirEvalError::TypeError(
"Overloaded deref in MIR is disallowed",
))
}
};
metadata = if self.size_of(&ty, locals)?.is_none() {
Some(Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() })
} else {
@ -377,78 +385,41 @@ impl Evaluator<'_> {
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) => {
ty = inner.clone();
let ty_size = self.size_of_sized(
&ty,
locals,
"slice inner type should be sized",
)?;
let value = self.read_memory(addr, self.ptr_size() * 2)?;
addr = Address::from_bytes(&value[0..8])?.offset(ty_size * offset);
}
x => not_supported!("MIR index for ref type {x:?}"),
},
TyKind::Array(inner, _) | TyKind::Slice(inner) => {
ty = inner.clone();
let ty_size = self.size_of_sized(
&ty,
locals,
"array inner type should be sized",
)?;
addr = addr.offset(ty_size * offset);
}
x => not_supported!("MIR index for type {x:?}"),
}
let ty_size =
self.size_of_sized(&ty, locals, "array inner type should be sized")?;
addr = addr.offset(ty_size * offset);
}
&ProjectionElem::TupleOrClosureField(f) => {
let layout = self.layout(&prev_ty)?;
let offset = layout.fields.offset(f).bytes_usize();
addr = addr.offset(offset);
metadata = None; // tuple field is always sized
}
ProjectionElem::Field(f) => {
let layout = self.layout(&prev_ty)?;
let variant_layout = match &layout.variants {
Variants::Single { .. } => &layout,
Variants::Multiple { variants, .. } => {
&variants[match f.parent {
hir_def::VariantId::EnumVariantId(x) => {
RustcEnumVariantIdx(x.local_id)
}
_ => {
return Err(MirEvalError::TypeError(
"Multivariant layout only happens for enums",
))
}
}]
}
};
let offset = variant_layout
.fields
.offset(u32::from(f.local_id.into_raw()) as usize)
.bytes_usize();
addr = addr.offset(offset);
// FIXME: support structs with unsized fields
metadata = None;
}
&ProjectionElem::TupleField(f) => match &ty.data(Interner).kind {
TyKind::Tuple(_, subst) => {
let layout = self.layout(&ty)?;
ty = subst
.as_slice(Interner)
.get(f)
.ok_or(MirEvalError::TypeError("not enough tuple fields"))?
.assert_ty_ref(Interner)
.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")),
},
ProjectionElem::Field(f) => match &ty.data(Interner).kind {
TyKind::Adt(adt, subst) => {
let layout = self.layout_adt(adt.0, subst.clone())?;
let variant_layout = match &layout.variants {
Variants::Single { .. } => &layout,
Variants::Multiple { variants, .. } => {
&variants[match f.parent {
hir_def::VariantId::EnumVariantId(x) => {
RustcEnumVariantIdx(x.local_id)
}
_ => {
return Err(MirEvalError::TypeError(
"Multivariant layout only happens for enums",
))
}
}]
}
};
ty = self.db.field_types(f.parent)[f.local_id]
.clone()
.substitute(Interner, subst);
let offset = variant_layout
.fields
.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")),
},
ProjectionElem::ConstantIndex { .. } => {
not_supported!("constant index")
}
@ -845,6 +816,15 @@ impl Evaluator<'_> {
values.iter().copied(),
)?)
}
AggregateKind::Closure(ty) => {
let layout = self.layout(&ty)?;
Owned(self.make_by_layout(
layout.size.bytes_usize(),
&layout,
None,
values.iter().copied(),
)?)
}
}
}
Rvalue::Cast(kind, operand, target_ty) => match kind {
@ -1065,6 +1045,9 @@ impl Evaluator<'_> {
let (mem, pos) = match addr {
Stack(x) => (&self.stack, x),
Heap(x) => (&self.heap, x),
Invalid(_) => {
return Err(MirEvalError::UndefinedBehavior("read invalid memory address"))
}
};
mem.get(pos..pos + size).ok_or(MirEvalError::UndefinedBehavior("out of bound memory read"))
}
@ -1073,6 +1056,9 @@ impl Evaluator<'_> {
let (mem, pos) = match addr {
Stack(x) => (&mut self.stack, x),
Heap(x) => (&mut self.heap, x),
Invalid(_) => {
return Err(MirEvalError::UndefinedBehavior("write invalid memory address"))
}
};
mem.get_mut(pos..pos + r.len())
.ok_or(MirEvalError::UndefinedBehavior("out of bound memory write"))?
@ -1394,6 +1380,25 @@ impl Evaluator<'_> {
Ok(())
}
fn exec_closure(
&mut self,
closure: ClosureId,
closure_data: Interval,
generic_args: &Substitution,
destination: Interval,
args: &[IntervalAndTy],
) -> Result<()> {
let mir_body = self
.db
.mir_body_for_closure(closure)
.map_err(|x| MirEvalError::MirLowerErrorForClosure(closure, x))?;
let arg_bytes = iter::once(Ok(closure_data.get(self)?.to_owned()))
.chain(args.iter().map(|x| Ok(x.get(&self)?.to_owned())))
.collect::<Result<Vec<_>>>()?;
let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter(), generic_args.clone())?;
destination.write_from_bytes(self, &bytes)
}
fn exec_fn_def(
&mut self,
def: FnDefId,
@ -1546,6 +1551,9 @@ impl Evaluator<'_> {
TyKind::Function(_) => {
self.exec_fn_pointer(func_data, destination, &args[1..], locals)?;
}
TyKind::Closure(closure, subst) => {
self.exec_closure(*closure, func_data, subst, destination, &args[1..])?;
}
x => not_supported!("Call FnTrait methods with type {x:?}"),
}
Ok(())

View File

@ -4,9 +4,9 @@ use std::{iter, mem, sync::Arc};
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
use hir_def::{
adt::{StructKind, VariantData},
body::Body,
expr::{
data::adt::{StructKind, VariantData},
hir::{
Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
RecordFieldPat, RecordLitField,
},
@ -21,9 +21,16 @@ use la_arena::ArenaMap;
use rustc_hash::FxHashMap;
use crate::{
consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime,
utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
consteval::ConstEvalError,
db::HirDatabase,
display::HirDisplay,
infer::{CaptureKind, CapturedItem, TypeMismatch},
inhabitedness::is_ty_uninhabited_from,
layout::layout_of_ty,
mapping::ToChalk,
static_lifetime,
utils::generics,
Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
};
use super::*;
@ -47,7 +54,7 @@ struct MirLowerCtx<'a> {
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>,
labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>,
discr_temp: Option<Place>,
db: &'a dyn HirDatabase,
body: &'a Body,
@ -74,10 +81,12 @@ pub enum MirLowerError {
BreakWithoutLoop,
Loop,
/// Something that should never happen and is definitely a bug, but we don't want to panic if it happened
ImplementationError(&'static str),
ImplementationError(String),
LangItemNotFound(LangItem),
MutatingRvalue,
UnresolvedLabel,
UnresolvedUpvar(Place),
UnaccessableLocal,
}
macro_rules! not_supported {
@ -88,8 +97,8 @@ macro_rules! not_supported {
macro_rules! implementation_error {
($x: expr) => {{
::stdx::never!("MIR lower implementation bug: {}", $x);
return Err(MirLowerError::ImplementationError($x));
::stdx::never!("MIR lower implementation bug: {}", format!($x));
return Err(MirLowerError::ImplementationError(format!($x)));
}};
}
@ -116,7 +125,44 @@ impl MirLowerError {
type Result<T> = std::result::Result<T, MirLowerError>;
impl MirLowerCtx<'_> {
impl<'ctx> MirLowerCtx<'ctx> {
fn new(
db: &'ctx dyn HirDatabase,
owner: DefWithBodyId,
body: &'ctx Body,
infer: &'ctx InferenceResult,
) -> Self {
let mut basic_blocks = Arena::new();
let start_block = basic_blocks.alloc(BasicBlock {
statements: vec![],
terminator: None,
is_cleanup: false,
});
let locals = Arena::new();
let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
let mir = MirBody {
basic_blocks,
locals,
start_block,
binding_locals,
param_locals: vec![],
owner,
arg_count: body.params.len(),
closures: vec![],
};
let ctx = MirLowerCtx {
result: mir,
db,
infer,
body,
owner,
current_loop_blocks: None,
labeled_loop_blocks: Default::default(),
discr_temp: None,
};
ctx
}
fn temp(&mut self, ty: Ty) -> Result<LocalId> {
if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
implementation_error!("unsized temporaries");
@ -268,7 +314,7 @@ impl MirLowerCtx<'_> {
self.push_assignment(
current,
place,
Operand::Copy(self.result.binding_locals[pat_id].into()).into(),
Operand::Copy(self.binding_local(pat_id)?.into()).into(),
expr_id.into(),
);
Ok(Some(current))
@ -579,19 +625,19 @@ impl MirLowerCtx<'_> {
Ok(None)
}
},
Expr::Break { expr, label } => {
&Expr::Break { expr, label } => {
if let Some(expr) = expr {
let loop_data = match label {
Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
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 {
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"),
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);
@ -713,20 +759,20 @@ impl MirLowerCtx<'_> {
Ok(Some(current))
}
Expr::Box { .. } => not_supported!("box expression"),
Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::expr::UnaryOp::Deref, .. } => {
Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => {
let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
return Ok(None);
};
self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
Ok(Some(current))
}
Expr::UnaryOp { expr, op: op @ (hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg) } => {
Expr::UnaryOp { expr, op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg) } => {
let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
return Ok(None);
};
let operation = match op {
hir_def::expr::UnaryOp::Not => UnOp::Not,
hir_def::expr::UnaryOp::Neg => UnOp::Neg,
hir_def::hir::UnaryOp::Not => UnOp::Not,
hir_def::hir::UnaryOp::Neg => UnOp::Neg,
_ => unreachable!(),
};
self.push_assignment(
@ -739,7 +785,7 @@ impl MirLowerCtx<'_> {
},
Expr::BinaryOp { lhs, rhs, op } => {
let op = op.ok_or(MirLowerError::IncompleteExpr)?;
if let hir_def::expr::BinaryOp::Assignment { op } = op {
if let hir_def::hir::BinaryOp::Assignment { op } = op {
if op.is_some() {
not_supported!("assignment with arith op (like +=)");
}
@ -765,13 +811,13 @@ impl MirLowerCtx<'_> {
place,
Rvalue::CheckedBinaryOp(
match op {
hir_def::expr::BinaryOp::LogicOp(op) => match op {
hir_def::expr::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
hir_def::expr::LogicOp::Or => BinOp::BitOr,
hir_def::hir::BinaryOp::LogicOp(op) => match op {
hir_def::hir::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
hir_def::hir::LogicOp::Or => BinOp::BitOr,
},
hir_def::expr::BinaryOp::ArithOp(op) => BinOp::from(op),
hir_def::expr::BinaryOp::CmpOp(op) => BinOp::from(op),
hir_def::expr::BinaryOp::Assignment { .. } => unreachable!(), // handled above
hir_def::hir::BinaryOp::ArithOp(op) => BinOp::from(op),
hir_def::hir::BinaryOp::CmpOp(op) => BinOp::from(op),
hir_def::hir::BinaryOp::Assignment { .. } => unreachable!(), // handled above
},
lhs_op,
rhs_op,
@ -823,7 +869,51 @@ impl MirLowerCtx<'_> {
);
Ok(Some(current))
},
Expr::Closure { .. } => not_supported!("closure"),
Expr::Closure { .. } => {
let ty = self.expr_ty(expr_id);
let TyKind::Closure(id, _) = ty.kind(Interner) else {
not_supported!("closure with non closure type");
};
self.result.closures.push(*id);
let (captures, _) = self.infer.closure_info(id);
let mut operands = vec![];
for capture in captures.iter() {
let p = Place {
local: self.binding_local(capture.place.local)?,
projection: capture.place.projections.clone().into_iter().map(|x| {
match x {
ProjectionElem::Deref => ProjectionElem::Deref,
ProjectionElem::Field(x) => ProjectionElem::Field(x),
ProjectionElem::TupleOrClosureField(x) => ProjectionElem::TupleOrClosureField(x),
ProjectionElem::ConstantIndex { offset, min_length, from_end } => ProjectionElem::ConstantIndex { offset, min_length, from_end },
ProjectionElem::Subslice { from, to, from_end } => ProjectionElem::Subslice { from, to, from_end },
ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x),
ProjectionElem::Index(x) => match x { },
}
}).collect(),
};
match &capture.kind {
CaptureKind::ByRef(bk) => {
let tmp: Place = self.temp(capture.ty.clone())?.into();
self.push_assignment(
current,
tmp.clone(),
Rvalue::Ref(bk.clone(), p),
expr_id.into(),
);
operands.push(Operand::Move(tmp));
},
CaptureKind::ByValue => operands.push(Operand::Move(p)),
}
}
self.push_assignment(
current,
place,
Rvalue::Aggregate(AggregateKind::Closure(ty), operands),
expr_id.into(),
);
Ok(Some(current))
},
Expr::Tuple { exprs, is_assignee_expr: _ } => {
let Some(values) = exprs
.iter()
@ -893,7 +983,7 @@ impl MirLowerCtx<'_> {
let index = name
.as_tuple_index()
.ok_or(MirLowerError::TypeError("named field on tuple"))?;
place.projection.push(ProjectionElem::TupleField(index))
place.projection.push(ProjectionElem::TupleOrClosureField(index))
} else {
let field =
self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?;
@ -910,7 +1000,7 @@ impl MirLowerCtx<'_> {
.size
.bytes_usize();
let bytes = match l {
hir_def::expr::Literal::String(b) => {
hir_def::hir::Literal::String(b) => {
let b = b.as_bytes();
let mut data = vec![];
data.extend(0usize.to_le_bytes());
@ -919,7 +1009,7 @@ impl MirLowerCtx<'_> {
mm.insert(0, b.to_vec());
return Ok(Operand::from_concrete_const(data, mm, ty));
}
hir_def::expr::Literal::ByteString(b) => {
hir_def::hir::Literal::ByteString(b) => {
let mut data = vec![];
data.extend(0usize.to_le_bytes());
data.extend(b.len().to_le_bytes());
@ -927,11 +1017,11 @@ impl MirLowerCtx<'_> {
mm.insert(0, b.to_vec());
return Ok(Operand::from_concrete_const(data, mm, ty));
}
hir_def::expr::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
hir_def::expr::Literal::Bool(b) => vec![*b as u8],
hir_def::expr::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
hir_def::expr::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
hir_def::expr::Literal::Float(f, _) => match size {
hir_def::hir::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
hir_def::hir::Literal::Bool(b) => vec![*b as u8],
hir_def::hir::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
hir_def::hir::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
hir_def::hir::Literal::Float(f, _) => match size {
8 => f.into_f64().to_le_bytes().into(),
4 => f.into_f32().to_le_bytes().into(),
_ => {
@ -1119,19 +1209,18 @@ impl MirLowerCtx<'_> {
// 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(),
)
self.labeled_loop_blocks
.insert(label, 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"))?;
let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
)?;
if let Some(prev) = prev_label {
self.labeled_loop_blocks.insert(self.body.labels[label.unwrap()].name.clone(), prev);
self.labeled_loop_blocks.insert(label.unwrap(), prev);
}
Ok(my.end)
}
@ -1161,7 +1250,9 @@ impl MirLowerCtx<'_> {
let r = match self
.current_loop_blocks
.as_mut()
.ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
.ok_or(MirLowerError::ImplementationError(
"Current loop access out of loop".to_string(),
))?
.end
{
Some(x) => x,
@ -1169,7 +1260,9 @@ impl MirLowerCtx<'_> {
let s = self.new_basic_block();
self.current_loop_blocks
.as_mut()
.ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
.ok_or(MirLowerError::ImplementationError(
"Current loop access out of loop".to_string(),
))?
.end = Some(s);
s
}
@ -1183,7 +1276,7 @@ impl MirLowerCtx<'_> {
/// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in
/// the appropriated places.
fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) {
fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> {
// Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break
// and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in
// the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
@ -1208,9 +1301,10 @@ impl MirLowerCtx<'_> {
.copied()
.map(MirSpan::PatId)
.unwrap_or(MirSpan::Unknown);
let l = self.result.binding_locals[b];
let l = self.binding_local(b)?;
self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
Ok(())
}
fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
@ -1220,14 +1314,14 @@ impl MirLowerCtx<'_> {
fn lower_block_to_place(
&mut self,
statements: &[hir_def::expr::Statement],
statements: &[hir_def::hir::Statement],
mut current: BasicBlockId,
tail: Option<ExprId>,
place: Place,
) -> Result<Option<Idx<BasicBlock>>> {
for statement in statements.iter() {
match statement {
hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
if let Some(expr_id) = initializer {
let else_block;
let Some((init_place, c)) =
@ -1258,12 +1352,18 @@ impl MirLowerCtx<'_> {
}
}
} else {
let mut err = None;
self.body.walk_bindings_in_pat(*pat, |b| {
self.push_storage_live(b, current);
if let Err(e) = self.push_storage_live(b, current) {
err = Some(e);
}
});
if let Some(e) = err {
return Err(e);
}
}
}
hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
hir_def::hir::Statement::Expr { expr, has_semi: _ } => {
let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None);
};
@ -1276,6 +1376,67 @@ impl MirLowerCtx<'_> {
None => Ok(Some(current)),
}
}
fn lower_params_and_bindings(
&mut self,
params: impl Iterator<Item = (PatId, Ty)> + Clone,
pick_binding: impl Fn(BindingId) -> bool,
) -> Result<BasicBlockId> {
let base_param_count = self.result.param_locals.len();
self.result.param_locals.extend(params.clone().map(|(x, ty)| {
let local_id = self.result.locals.alloc(Local { ty });
if let Pat::Bind { id, subpat: None } = self.body[x] {
if matches!(
self.body.bindings[id].mode,
BindingAnnotation::Unannotated | BindingAnnotation::Mutable
) {
self.result.binding_locals.insert(id, local_id);
}
}
local_id
}));
// and then rest of bindings
for (id, _) in self.body.bindings.iter() {
if !pick_binding(id) {
continue;
}
if !self.result.binding_locals.contains_idx(id) {
self.result
.binding_locals
.insert(id, self.result.locals.alloc(Local { ty: self.infer[id].clone() }));
}
}
let mut current = self.result.start_block;
for ((param, _), local) in
params.zip(self.result.param_locals.clone().into_iter().skip(base_param_count))
{
if let Pat::Bind { id, .. } = self.body[param] {
if local == self.binding_local(id)? {
continue;
}
}
let r = self.pattern_match(
current,
None,
local.into(),
self.result.locals[local].ty.clone(),
param,
BindingAnnotation::Unannotated,
)?;
if let Some(b) = r.1 {
self.set_terminator(b, Terminator::Unreachable);
}
current = r.0;
}
Ok(current)
}
fn binding_local(&self, b: BindingId) -> Result<LocalId> {
match self.result.binding_locals.get(b) {
Some(x) => Ok(*x),
None => Err(MirLowerError::UnaccessableLocal),
}
}
}
fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
@ -1299,6 +1460,87 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
})
}
pub fn mir_body_for_closure_query(
db: &dyn HirDatabase,
closure: ClosureId,
) -> Result<Arc<MirBody>> {
let (owner, expr) = db.lookup_intern_closure(closure.into());
let body = db.body(owner);
let infer = db.infer(owner);
let Expr::Closure { args, body: root, .. } = &body[expr] else {
implementation_error!("closure expression is not closure");
};
let TyKind::Closure(_, substs) = &infer[expr].kind(Interner) else {
implementation_error!("closure expression is not closure");
};
let (captures, _) = infer.closure_info(&closure);
let mut ctx = MirLowerCtx::new(db, owner, &body, &infer);
ctx.result.arg_count = args.len() + 1;
// 0 is return local
ctx.result.locals.alloc(Local { ty: infer[*root].clone() });
ctx.result.locals.alloc(Local { ty: infer[expr].clone() });
let Some(sig) = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db) else {
implementation_error!("closure has not callable sig");
};
let current = ctx.lower_params_and_bindings(
args.iter().zip(sig.params().iter()).map(|(x, y)| (*x, y.clone())),
|_| true,
)?;
if let Some(b) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
ctx.set_terminator(b, Terminator::Return);
}
let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
for (i, capture) in captures.iter().enumerate() {
let local = ctx.binding_local(capture.place.local)?;
upvar_map.entry(local).or_default().push((capture, i));
}
let mut err = None;
let closure_local = ctx.result.locals.iter().nth(1).unwrap().0;
ctx.result.walk_places(|p| {
if let Some(x) = upvar_map.get(&p.local) {
let r = x.iter().find(|x| {
if p.projection.len() < x.0.place.projections.len() {
return false;
}
for (x, y) in p.projection.iter().zip(x.0.place.projections.iter()) {
match (x, y) {
(ProjectionElem::Deref, ProjectionElem::Deref) => (),
(ProjectionElem::Field(x), ProjectionElem::Field(y)) if x == y => (),
(
ProjectionElem::TupleOrClosureField(x),
ProjectionElem::TupleOrClosureField(y),
) if x == y => (),
_ => return false,
}
}
true
});
match r {
Some(x) => {
p.local = closure_local;
let prev_projs =
mem::replace(&mut p.projection, vec![PlaceElem::TupleOrClosureField(x.1)]);
if x.0.kind != CaptureKind::ByValue {
p.projection.push(ProjectionElem::Deref);
}
p.projection.extend(prev_projs.into_iter().skip(x.0.place.projections.len()));
}
None => err = Some(p.clone()),
}
}
});
ctx.result.binding_locals = ctx
.result
.binding_locals
.into_iter()
.filter(|x| ctx.body[x.0].owner == Some(expr))
.collect();
if let Some(err) = err {
return Err(MirLowerError::UnresolvedUpvar(err));
}
Ok(Arc::new(ctx.result))
}
pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
let _p = profile::span("mir_body_query").detail(|| match def {
DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(),
@ -1336,86 +1578,29 @@ pub fn lower_to_mir(
if let Some((_, x)) = infer.type_mismatches().next() {
return Err(MirLowerError::TypeMismatch(x.clone()));
}
let mut basic_blocks = Arena::new();
let start_block =
basic_blocks.alloc(BasicBlock { statements: vec![], terminator: None, is_cleanup: false });
let mut locals = Arena::new();
let mut ctx = MirLowerCtx::new(db, owner, body, infer);
// 0 is return local
locals.alloc(Local { ty: infer[root_expr].clone() });
let mut binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
ctx.result.locals.alloc(Local { ty: infer[root_expr].clone() });
let binding_picker = |b: BindingId| {
if root_expr == body.body_expr {
body[b].owner.is_none()
} else {
body[b].owner == Some(root_expr)
}
};
// 1 to param_len is for params
let param_locals: Vec<LocalId> = if let DefWithBodyId::FunctionId(fid) = owner {
let current = if let DefWithBodyId::FunctionId(fid) = owner {
let substs = TyBuilder::placeholder_subst(db, fid);
let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs);
body.params
.iter()
.zip(callable_sig.params().iter())
.map(|(&x, ty)| {
let local_id = locals.alloc(Local { ty: ty.clone() });
if let Pat::Bind { id, subpat: None } = body[x] {
if matches!(
body.bindings[id].mode,
BindingAnnotation::Unannotated | BindingAnnotation::Mutable
) {
binding_locals.insert(id, local_id);
}
}
local_id
})
.collect()
ctx.lower_params_and_bindings(
body.params.iter().zip(callable_sig.params().iter()).map(|(x, y)| (*x, y.clone())),
binding_picker,
)?
} else {
if !body.params.is_empty() {
return Err(MirLowerError::TypeError("Unexpected parameter for non function body"));
}
vec![]
ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
};
// and then rest of bindings
for (id, _) in body.bindings.iter() {
if !binding_locals.contains_idx(id) {
binding_locals.insert(id, locals.alloc(Local { ty: infer[id].clone() }));
}
}
let mir = MirBody {
basic_blocks,
locals,
start_block,
binding_locals,
param_locals,
owner,
arg_count: body.params.len(),
};
let mut ctx = MirLowerCtx {
result: mir,
db,
infer,
body,
owner,
current_loop_blocks: None,
labeled_loop_blocks: Default::default(),
discr_temp: None,
};
let mut current = start_block;
for (&param, local) in body.params.iter().zip(ctx.result.param_locals.clone().into_iter()) {
if let Pat::Bind { id, .. } = body[param] {
if local == ctx.result.binding_locals[id] {
continue;
}
}
let r = ctx.pattern_match(
current,
None,
local.into(),
ctx.result.locals[local].ty.clone(),
param,
BindingAnnotation::Unannotated,
)?;
if let Some(b) = r.1 {
ctx.set_terminator(b, Terminator::Unreachable);
}
current = r.0;
}
if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
ctx.result.basic_blocks[b].terminator = Some(Terminator::Return);
ctx.set_terminator(b, Terminator::Return);
}
Ok(ctx.result)
}

View File

@ -141,7 +141,7 @@ impl MirLowerCtx<'_> {
}
}
Expr::UnaryOp { expr, op } => match op {
hir_def::expr::UnaryOp::Deref => {
hir_def::hir::UnaryOp::Deref => {
if !matches!(
self.expr_ty(*expr).kind(Interner),
TyKind::Ref(..) | TyKind::Raw(..)

View File

@ -1,5 +1,7 @@
//! MIR lowering for patterns
use crate::utils::pattern_matching_dereference_count;
use super::*;
macro_rules! not_supported {
@ -52,7 +54,7 @@ impl MirLowerCtx<'_> {
args,
*ellipsis,
subst.iter(Interner).enumerate().map(|(i, x)| {
(PlaceElem::TupleField(i), x.assert_ty_ref(Interner).clone())
(PlaceElem::TupleOrClosureField(i), x.assert_ty_ref(Interner).clone())
}),
&cond_place,
binding_mode,
@ -142,7 +144,7 @@ impl MirLowerCtx<'_> {
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
binding_mode = mode;
}
self.push_storage_live(*id, current);
self.push_storage_live(*id, current)?;
self.push_assignment(
current,
target_place.into(),
@ -387,13 +389,6 @@ fn pattern_matching_dereference(
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);
}
let cnt = pattern_matching_dereference_count(cond_ty, binding_mode);
cond_place.projection.extend((0..cnt).map(|_| ProjectionElem::Deref));
}

View File

@ -1,8 +1,11 @@
//! A pretty-printer for MIR.
use std::fmt::{Debug, Display, Write};
use std::{
fmt::{Debug, Display, Write},
mem,
};
use hir_def::{body::Body, expr::BindingId};
use hir_def::{body::Body, hir::BindingId};
use hir_expand::name::Name;
use la_arena::ArenaMap;
@ -20,7 +23,7 @@ impl MirBody {
pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
let hir_body = db.body(self.owner);
let mut ctx = MirPrettyCtx::new(self, &hir_body, db);
ctx.for_body();
ctx.for_body(ctx.body.owner);
ctx.result
}
@ -42,7 +45,7 @@ struct MirPrettyCtx<'a> {
hir_body: &'a Body,
db: &'a dyn HirDatabase,
result: String,
ident: String,
indent: String,
local_to_binding: ArenaMap<LocalId, BindingId>,
}
@ -88,22 +91,43 @@ impl Display for LocalName {
}
impl<'a> MirPrettyCtx<'a> {
fn for_body(&mut self) {
wln!(self, "// {:?}", self.body.owner);
fn for_body(&mut self, name: impl Debug) {
wln!(self, "// {:?}", name);
self.with_block(|this| {
this.locals();
wln!(this);
this.blocks();
});
for &closure in &self.body.closures {
let body = match self.db.mir_body_for_closure(closure) {
Ok(x) => x,
Err(e) => {
wln!(self, "// error in {closure:?}: {e:?}");
continue;
}
};
let result = mem::take(&mut self.result);
let indent = mem::take(&mut self.indent);
let mut ctx = MirPrettyCtx {
body: &body,
local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(),
result,
indent,
..*self
};
ctx.for_body(closure);
self.result = ctx.result;
self.indent = ctx.indent;
}
}
fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) {
self.ident += " ";
self.indent += " ";
wln!(self, "{{");
f(self);
for _ in 0..4 {
self.result.pop();
self.ident.pop();
self.indent.pop();
}
wln!(self, "}}");
}
@ -114,7 +138,7 @@ impl<'a> MirPrettyCtx<'a> {
body,
db,
result: String::new(),
ident: String::new(),
indent: String::new(),
local_to_binding,
hir_body,
}
@ -122,7 +146,7 @@ impl<'a> MirPrettyCtx<'a> {
fn write_line(&mut self) {
self.result.push('\n');
self.result += &self.ident;
self.result += &self.indent;
}
fn write(&mut self, line: &str) {
@ -247,7 +271,7 @@ impl<'a> MirPrettyCtx<'a> {
}
}
}
ProjectionElem::TupleField(x) => {
ProjectionElem::TupleOrClosureField(x) => {
f(this, local, head);
w!(this, ".{}", x);
}
@ -302,6 +326,11 @@ impl<'a> MirPrettyCtx<'a> {
self.operand_list(x);
w!(self, ")");
}
Rvalue::Aggregate(AggregateKind::Closure(_), x) => {
w!(self, "Closure(");
self.operand_list(x);
w!(self, ")");
}
Rvalue::Aggregate(AggregateKind::Union(_, _), x) => {
w!(self, "Union(");
self.operand_list(x);

View File

@ -17,7 +17,7 @@ use expect_test::Expect;
use hir_def::{
body::{Body, BodySourceMap, SyntheticSyntax},
db::{DefDatabase, InternDatabase},
expr::{ExprId, PatId},
hir::{ExprId, PatId},
item_scope::ItemScope,
nameres::DefMap,
src::HasSource,
@ -198,8 +198,8 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
for (expr_or_pat, mismatch) in inference_result.type_mismatches() {
let Some(node) = (match expr_or_pat {
hir_def::expr::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
hir_def::expr::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
}) else { continue; };
let range = node.as_ref().original_file_range(&db);
let actual = format!(

View File

@ -575,7 +575,7 @@ fn two_closures_lub() {
fn foo(c: i32) {
let add = |a: i32, b: i32| a + b;
let sub = |a, b| a - b;
//^^^^^^^^^^^^ |i32, i32| -> i32
//^^^^^^^^^^^^ impl Fn(i32, i32) -> i32
if c > 42 { add } else { sub };
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fn(i32, i32) -> i32
}
@ -875,6 +875,16 @@ fn test() {
fn adjust_index() {
check_no_mismatches(
r"
//- minicore: index, slice, coerce_unsized
fn test() {
let x = [1, 2, 3];
x[2] = 6;
// ^ adjustments: Borrow(Ref(Mut))
}
",
);
check_no_mismatches(
r"
//- minicore: index
struct Struct;
impl core::ops::Index<usize> for Struct {
@ -902,3 +912,32 @@ fn test() {
}",
);
}
#[test]
fn regression_14443_dyn_coercion_block_impls() {
check_no_mismatches(
r#"
//- minicore: coerce_unsized
trait T {}
fn dyn_t(d: &dyn T) {}
fn main() {
struct A;
impl T for A {}
let a = A;
let b = {
struct B;
impl T for B {}
B
};
dyn_t(&a);
dyn_t(&b);
}
"#,
)
}

View File

@ -198,7 +198,7 @@ fn expr_macro_def_expanded_in_various_places() {
100..119 'for _ ...!() {}': ()
104..105 '_': {unknown}
117..119 '{}': ()
124..134 '|| spam!()': || -> isize
124..134 '|| spam!()': impl Fn() -> isize
140..156 'while ...!() {}': ()
154..156 '{}': ()
161..174 'break spam!()': !
@ -279,7 +279,7 @@ fn expr_macro_rules_expanded_in_various_places() {
114..133 'for _ ...!() {}': ()
118..119 '_': {unknown}
131..133 '{}': ()
138..148 '|| spam!()': || -> isize
138..148 '|| spam!()': impl Fn() -> isize
154..170 'while ...!() {}': ()
168..170 '{}': ()
175..188 'break spam!()': !

View File

@ -388,6 +388,24 @@ mod bar_test {
);
}
#[test]
fn infer_trait_method_multiple_mutable_reference() {
check_types(
r#"
trait Trait {
fn method(&mut self) -> i32 { 5 }
}
struct S;
impl Trait for &mut &mut S {}
fn test() {
let s = &mut &mut &mut S;
s.method();
//^^^^^^^^^^ i32
}
"#,
);
}
#[test]
fn infer_trait_method_generic_1() {
// the trait implementation is intentionally incomplete -- it shouldn't matter
@ -1722,7 +1740,7 @@ fn test() {
Foo.foo();
//^^^ adjustments: Borrow(Ref(Not))
(&Foo).foo();
// ^^^^ adjustments: ,
// ^^^^ adjustments: Deref(None), Borrow(Ref(Not))
}
"#,
);

View File

@ -70,8 +70,8 @@ fn infer_pattern() {
228..233 '&true': &bool
229..233 'true': bool
234..236 '{}': ()
246..252 'lambda': |u64, u64, i32| -> i32
255..287 '|a: u6...b; c }': |u64, u64, i32| -> i32
246..252 'lambda': impl Fn(u64, u64, i32) -> i32
255..287 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32
256..257 'a': u64
264..265 'b': u64
267..268 'c': i32
@ -476,7 +476,7 @@ fn infer_adt_pattern() {
183..184 'x': usize
190..191 'x': usize
201..205 'E::B': E
209..212 'foo': {unknown}
209..212 'foo': bool
216..217 '1': usize
227..231 'E::B': E
235..237 '10': usize
@ -677,25 +677,25 @@ fn test() {
51..58 'loop {}': !
56..58 '{}': ()
72..171 '{ ... x); }': ()
78..81 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32
78..81 'foo': fn foo<&(i32, &str), i32, impl Fn(&(i32, &str)) -> i32>(&(i32, &str), impl Fn(&(i32, &str)) -> i32) -> i32
78..105 'foo(&(...y)| x)': i32
82..91 '&(1, "a")': &(i32, &str)
83..91 '(1, "a")': (i32, &str)
84..85 '1': i32
87..90 '"a"': &str
93..104 '|&(x, y)| x': |&(i32, &str)| -> i32
93..104 '|&(x, y)| x': impl Fn(&(i32, &str)) -> i32
94..101 '&(x, y)': &(i32, &str)
95..101 '(x, y)': (i32, &str)
96..97 'x': i32
99..100 'y': &str
103..104 'x': i32
142..145 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32
142..145 'foo': fn foo<&(i32, &str), &i32, impl Fn(&(i32, &str)) -> &i32>(&(i32, &str), impl Fn(&(i32, &str)) -> &i32) -> &i32
142..168 'foo(&(...y)| x)': &i32
146..155 '&(1, "a")': &(i32, &str)
147..155 '(1, "a")': (i32, &str)
148..149 '1': i32
151..154 '"a"': &str
157..167 '|(x, y)| x': |&(i32, &str)| -> &i32
157..167 '|(x, y)| x': impl Fn(&(i32, &str)) -> &i32
158..164 '(x, y)': (i32, &str)
159..160 'x': &i32
162..163 'y': &&str

View File

@ -270,7 +270,7 @@ fn infer_std_crash_5() {
61..320 '{ ... }': ()
75..79 'name': &{unknown}
82..166 'if doe... }': &{unknown}
85..98 'doesnt_matter': {unknown}
85..98 'doesnt_matter': bool
99..128 '{ ... }': &{unknown}
113..118 'first': &{unknown}
134..166 '{ ... }': &{unknown}
@ -279,7 +279,7 @@ fn infer_std_crash_5() {
181..188 'content': &{unknown}
191..313 'if ICE... }': &{unknown}
194..231 'ICE_RE..._VALUE': {unknown}
194..247 'ICE_RE...&name)': {unknown}
194..247 'ICE_RE...&name)': bool
241..246 '&name': &&{unknown}
242..246 'name': &{unknown}
248..276 '{ ... }': &{unknown}
@ -805,19 +805,19 @@ fn issue_4966() {
225..229 'iter': T
244..246 '{}': Vec<A>
258..402 '{ ...r(); }': ()
268..273 'inner': Map<|&f64| -> f64>
276..300 'Map { ... 0.0 }': Map<|&f64| -> f64>
285..298 '|_: &f64| 0.0': |&f64| -> f64
268..273 'inner': Map<impl Fn(&f64) -> f64>
276..300 'Map { ... 0.0 }': Map<impl Fn(&f64) -> f64>
285..298 '|_: &f64| 0.0': impl Fn(&f64) -> f64
286..287 '_': &f64
295..298 '0.0': f64
311..317 'repeat': Repeat<Map<|&f64| -> f64>>
320..345 'Repeat...nner }': Repeat<Map<|&f64| -> f64>>
338..343 'inner': Map<|&f64| -> f64>
356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
362..371 'from_iter': fn from_iter<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>, Repeat<Map<|&f64| -> f64>>>(Repeat<Map<|&f64| -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
372..378 'repeat': Repeat<Map<|&f64| -> f64>>
386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
311..317 'repeat': Repeat<Map<impl Fn(&f64) -> f64>>
320..345 'Repeat...nner }': Repeat<Map<impl Fn(&f64) -> f64>>
338..343 'inner': Map<impl Fn(&f64) -> f64>
356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
362..371 'from_iter': fn from_iter<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>, Repeat<Map<impl Fn(&f64) -> f64>>>(Repeat<Map<impl Fn(&f64) -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
372..378 'repeat': Repeat<Map<impl Fn(&f64) -> f64>>
386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
386..399 'vec.foo_bar()': {unknown}
"#]],
);
@ -852,7 +852,7 @@ fn main() {
123..126 'S()': S<i32>
132..133 's': S<i32>
132..144 's.g(|_x| {})': ()
136..143 '|_x| {}': |&i32| -> ()
136..143 '|_x| {}': impl Fn(&i32)
137..139 '_x': &i32
141..143 '{}': ()
150..151 's': S<i32>
@ -1067,6 +1067,23 @@ fn parse_arule() {
)
}
#[test]
fn nested_closure() {
check_types(
r#"
//- minicore: fn, option
fn map<T, U>(o: Option<T>, f: impl FnOnce(T) -> U) -> Option<U> { loop {} }
fn test() {
let o = Some(Some(2));
map(o, |s| map(s, |x| x));
// ^ i32
}
"#,
);
}
#[test]
fn call_expected_type_closure() {
check_types(
@ -1759,13 +1776,14 @@ const C: usize = 2 + 2;
#[test]
fn regression_14456() {
check_no_mismatches(
check_types(
r#"
//- minicore: future
async fn x() {}
fn f() {
let fut = x();
let t = [0u8; 2 + 2];
let t = [0u8; { let a = 2 + 2; a }];
//^ [u8; 4]
}
"#,
);
@ -1802,3 +1820,21 @@ where
"#,
);
}
#[test]
fn match_ergonomics_with_binding_modes_interaction() {
check_types(
r"
enum E { A }
fn foo() {
match &E::A {
b @ (x @ E::A | x) => {
b;
//^ &E
x;
//^ &E
}
}
}",
);
}

View File

@ -1906,8 +1906,8 @@ fn closure_return() {
"#,
expect![[r#"
16..58 '{ ...; }; }': u32
26..27 'x': || -> usize
30..55 '|| -> ...n 1; }': || -> usize
26..27 'x': impl Fn() -> usize
30..55 '|| -> ...n 1; }': impl Fn() -> usize
42..55 '{ return 1; }': usize
44..52 'return 1': !
51..52 '1': usize
@ -1925,8 +1925,8 @@ fn closure_return_unit() {
"#,
expect![[r#"
16..47 '{ ...; }; }': u32
26..27 'x': || -> ()
30..44 '|| { return; }': || -> ()
26..27 'x': impl Fn()
30..44 '|| { return; }': impl Fn()
33..44 '{ return; }': ()
35..41 'return': !
"#]],
@ -1943,8 +1943,8 @@ fn closure_return_inferred() {
"#,
expect![[r#"
16..46 '{ ..." }; }': u32
26..27 'x': || -> &str
30..43 '|| { "test" }': || -> &str
26..27 'x': impl Fn() -> &str
30..43 '|| { "test" }': impl Fn() -> &str
33..43 '{ "test" }': &str
35..41 '"test"': &str
"#]],
@ -2050,7 +2050,7 @@ fn fn_pointer_return() {
47..120 '{ ...hod; }': ()
57..63 'vtable': Vtable
66..90 'Vtable...| {} }': Vtable
83..88 '|| {}': || -> ()
83..88 '|| {}': impl Fn()
86..88 '{}': ()
100..101 'm': fn()
104..110 'vtable': Vtable
@ -2142,9 +2142,9 @@ fn main() {
149..151 'Ok': Ok<(), ()>(()) -> Result<(), ()>
149..155 'Ok(())': Result<(), ()>
152..154 '()': ()
167..171 'test': fn test<(), (), || -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(|| -> impl Future<Output = Result<(), ()>>)
167..171 'test': fn test<(), (), impl Fn() -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(impl Fn() -> impl Future<Output = Result<(), ()>>)
167..228 'test(|... })': ()
172..227 '|| asy... }': || -> impl Future<Output = Result<(), ()>>
172..227 '|| asy... }': impl Fn() -> impl Future<Output = Result<(), ()>>
175..227 'async ... }': impl Future<Output = Result<(), ()>>
191..205 'return Err(())': !
198..201 'Err': Err<(), ()>(()) -> Result<(), ()>
@ -2270,8 +2270,8 @@ fn infer_labelled_break_with_val() {
"#,
expect![[r#"
9..335 '{ ... }; }': ()
19..21 '_x': || -> bool
24..332 '|| 'ou... }': || -> bool
19..21 '_x': impl Fn() -> bool
24..332 '|| 'ou... }': impl Fn() -> bool
27..332 ''outer... }': bool
40..332 '{ ... }': ()
54..59 'inner': i8
@ -2695,6 +2695,179 @@ impl B for Astruct {}
)
}
#[test]
fn capture_kinds_simple() {
check_types(
r#"
struct S;
impl S {
fn read(&self) -> &S { self }
fn write(&mut self) -> &mut S { self }
fn consume(self) -> S { self }
}
fn f() {
let x = S;
let c1 = || x.read();
//^^ impl Fn() -> &S
let c2 = || x.write();
//^^ impl FnMut() -> &mut S
let c3 = || x.consume();
//^^ impl FnOnce() -> S
let c3 = || x.consume().consume().consume();
//^^ impl FnOnce() -> S
let c3 = || x.consume().write().read();
//^^ impl FnOnce() -> &S
let x = &mut x;
let c1 = || x.write();
//^^ impl FnMut() -> &mut S
let x = S;
let c1 = || { let ref t = x; t };
//^^ impl Fn() -> &S
let c2 = || { let ref mut t = x; t };
//^^ impl FnMut() -> &mut S
let c3 = || { let t = x; t };
//^^ impl FnOnce() -> S
}
"#,
)
}
#[test]
fn capture_kinds_closure() {
check_types(
r#"
//- minicore: copy, fn
fn f() {
let mut x = 2;
x = 5;
let mut c1 = || { x = 3; x };
//^^^^^^ impl FnMut() -> i32
let mut c2 = || { c1() };
//^^^^^^ impl FnMut() -> i32
let mut c1 = || { x };
//^^^^^^ impl Fn() -> i32
let mut c2 = || { c1() };
//^^^^^^ impl Fn() -> i32
struct X;
let x = X;
let mut c1 = || { x };
//^^^^^^ impl FnOnce() -> X
let mut c2 = || { c1() };
//^^^^^^ impl FnOnce() -> X
}
"#,
);
}
#[test]
fn capture_kinds_overloaded_deref() {
check_types(
r#"
//- minicore: fn, deref_mut
use core::ops::{Deref, DerefMut};
struct Foo;
impl Deref for Foo {
type Target = (i32, u8);
fn deref(&self) -> &(i32, u8) {
&(5, 2)
}
}
impl DerefMut for Foo {
fn deref_mut(&mut self) -> &mut (i32, u8) {
&mut (5, 2)
}
}
fn test() {
let mut x = Foo;
let c1 = || *x;
//^^ impl Fn() -> (i32, u8)
let c2 = || { *x = (2, 5); };
//^^ impl FnMut()
let c3 = || { x.1 };
//^^ impl Fn() -> u8
let c4 = || { x.1 = 6; };
//^^ impl FnMut()
}
"#,
);
}
#[test]
fn capture_kinds_with_copy_types() {
check_types(
r#"
//- minicore: copy, clone, derive
#[derive(Clone, Copy)]
struct Copy;
struct NotCopy;
#[derive(Clone, Copy)]
struct Generic<T>(T);
trait Tr {
type Assoc;
}
impl Tr for Copy {
type Assoc = NotCopy;
}
#[derive(Clone, Copy)]
struct AssocGeneric<T: Tr>(T::Assoc);
fn f() {
let a = Copy;
let b = NotCopy;
let c = Generic(Copy);
let d = Generic(NotCopy);
let e: AssocGeneric<Copy> = AssocGeneric(NotCopy);
let c1 = || a;
//^^ impl Fn() -> Copy
let c2 = || b;
//^^ impl FnOnce() -> NotCopy
let c3 = || c;
//^^ impl Fn() -> Generic<Copy>
let c3 = || d;
//^^ impl FnOnce() -> Generic<NotCopy>
let c3 = || e;
//^^ impl FnOnce() -> AssocGeneric<Copy>
}
"#,
)
}
#[test]
fn derive_macro_should_work_for_associated_type() {
check_types(
r#"
//- minicore: copy, clone, derive
#[derive(Clone)]
struct X;
#[derive(Clone)]
struct Y;
trait Tr {
type Assoc;
}
impl Tr for X {
type Assoc = Y;
}
#[derive(Clone)]
struct AssocGeneric<T: Tr>(T::Assoc);
fn f() {
let e: AssocGeneric<X> = AssocGeneric(Y);
let e_clone = e.clone();
//^^^^^^^ AssocGeneric<X>
}
"#,
)
}
#[test]
fn cfgd_out_assoc_items() {
check_types(
@ -3291,6 +3464,19 @@ fn f<T>(t: Ark<T>) {
);
}
#[test]
fn const_dependent_on_local() {
check_types(
r#"
fn main() {
let s = 5;
let t = [2; s];
//^ [i32; _]
}
"#,
);
}
#[test]
fn issue_14275() {
// FIXME: evaluate const generic

View File

@ -90,7 +90,7 @@ fn infer_async_closure() {
async fn test() {
let f = async move |x: i32| x + 42;
f;
// ^ |i32| -> impl Future<Output = i32>
// ^ impl Fn(i32) -> impl Future<Output = i32>
let a = f(4);
a;
// ^ impl Future<Output = i32>
@ -99,7 +99,7 @@ async fn test() {
// ^ i32
let f = async move || 42;
f;
// ^ || -> impl Future<Output = i32>
// ^ impl Fn() -> impl Future<Output = i32>
let a = f();
a;
// ^ impl Future<Output = i32>
@ -116,7 +116,7 @@ async fn test() {
};
let _: Option<u64> = c().await;
c;
// ^ || -> impl Future<Output = Option<u64>>
// ^ impl Fn() -> impl Future<Output = Option<u64>>
}
"#,
);
@ -550,7 +550,7 @@ fn test() -> u64 {
53..54 'a': S
57..58 'S': S(fn(u32) -> u64) -> S
57..74 'S(|i| ...s u64)': S
59..73 '|i| 2*i as u64': |u32| -> u64
59..73 '|i| 2*i as u64': impl Fn(u32) -> u64
60..61 'i': u32
63..64 '2': u64
63..73 '2*i as u64': u64
@ -1333,9 +1333,9 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) {
}
"#,
expect![[r#"
134..165 '{ ...(C)) }': (|&str, T| -> (), Bar<u8>)
140..163 '(|inpu...ar(C))': (|&str, T| -> (), Bar<u8>)
141..154 '|input, t| {}': |&str, T| -> ()
134..165 '{ ...(C)) }': (impl Fn(&str, T), Bar<u8>)
140..163 '(|inpu...ar(C))': (impl Fn(&str, T), Bar<u8>)
141..154 '|input, t| {}': impl Fn(&str, T)
142..147 'input': &str
149..150 't': T
152..154 '{}': ()
@ -1506,8 +1506,8 @@ fn main() {
71..105 '{ ...()); }': ()
77..78 'f': fn f(&dyn Fn(S))
77..102 'f(&|nu...foo())': ()
79..101 '&|numb....foo()': &|S| -> ()
80..101 '|numbe....foo()': |S| -> ()
79..101 '&|numb....foo()': &impl Fn(S)
80..101 '|numbe....foo()': impl Fn(S)
81..87 'number': S
89..95 'number': S
89..101 'number.foo()': ()
@ -1912,13 +1912,13 @@ fn test() {
131..132 'f': F
151..153 '{}': Lazy<T, F>
251..497 '{ ...o(); }': ()
261..266 'lazy1': Lazy<Foo, || -> Foo>
283..292 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
283..300 'Lazy::...| Foo)': Lazy<Foo, || -> Foo>
293..299 '|| Foo': || -> Foo
261..266 'lazy1': Lazy<Foo, impl Fn() -> Foo>
283..292 'Lazy::new': fn new<Foo, impl Fn() -> Foo>(impl Fn() -> Foo) -> Lazy<Foo, impl Fn() -> Foo>
283..300 'Lazy::...| Foo)': Lazy<Foo, impl Fn() -> Foo>
293..299 '|| Foo': impl Fn() -> Foo
296..299 'Foo': Foo
310..312 'r1': usize
315..320 'lazy1': Lazy<Foo, || -> Foo>
315..320 'lazy1': Lazy<Foo, impl Fn() -> Foo>
315..326 'lazy1.foo()': usize
368..383 'make_foo_fn_ptr': fn() -> Foo
399..410 'make_foo_fn': fn make_foo_fn() -> Foo
@ -1963,20 +1963,20 @@ fn test() {
163..167 '1u32': u32
174..175 'x': Option<u32>
174..190 'x.map(...v + 1)': Option<u32>
180..189 '|v| v + 1': |u32| -> u32
180..189 '|v| v + 1': impl Fn(u32) -> u32
181..182 'v': u32
184..185 'v': u32
184..189 'v + 1': u32
188..189 '1': u32
196..197 'x': Option<u32>
196..212 'x.map(... 1u64)': Option<u64>
202..211 '|_v| 1u64': |u32| -> u64
202..211 '|_v| 1u64': impl Fn(u32) -> u64
203..205 '_v': u32
207..211 '1u64': u64
222..223 'y': Option<i64>
239..240 'x': Option<u32>
239..252 'x.map(|_v| 1)': Option<i64>
245..251 '|_v| 1': |u32| -> i64
245..251 '|_v| 1': impl Fn(u32) -> i64
246..248 '_v': u32
250..251 '1': i64
"#]],
@ -2005,11 +2005,11 @@ fn test<F: FnOnce(u32) -> u64>(f: F) {
//^^^^ u64
let g = |v| v + 1;
//^^^^^ u64
//^^^^^^^^^ |u64| -> u64
//^^^^^^^^^ impl Fn(u64) -> u64
g(1u64);
//^^^^^^^ u64
let h = |v| 1u128 + v;
//^^^^^^^^^^^^^ |u128| -> u128
//^^^^^^^^^^^^^ impl Fn(u128) -> u128
}"#,
);
}
@ -2062,17 +2062,17 @@ fn test() {
312..314 '{}': ()
330..489 '{ ... S); }': ()
340..342 'x1': u64
345..349 'foo1': fn foo1<S, u64, |S| -> u64>(S, |S| -> u64) -> u64
345..349 'foo1': fn foo1<S, u64, impl Fn(S) -> u64>(S, impl Fn(S) -> u64) -> u64
345..368 'foo1(S...hod())': u64
350..351 'S': S
353..367 '|s| s.method()': |S| -> u64
353..367 '|s| s.method()': impl Fn(S) -> u64
354..355 's': S
357..358 's': S
357..367 's.method()': u64
378..380 'x2': u64
383..387 'foo2': fn foo2<S, u64, |S| -> u64>(|S| -> u64, S) -> u64
383..387 'foo2': fn foo2<S, u64, impl Fn(S) -> u64>(impl Fn(S) -> u64, S) -> u64
383..406 'foo2(|...(), S)': u64
388..402 '|s| s.method()': |S| -> u64
388..402 '|s| s.method()': impl Fn(S) -> u64
389..390 's': S
392..393 's': S
392..402 's.method()': u64
@ -2081,14 +2081,14 @@ fn test() {
421..422 'S': S
421..446 'S.foo1...hod())': u64
428..429 'S': S
431..445 '|s| s.method()': |S| -> u64
431..445 '|s| s.method()': impl Fn(S) -> u64
432..433 's': S
435..436 's': S
435..445 's.method()': u64
456..458 'x4': u64
461..462 'S': S
461..486 'S.foo2...(), S)': u64
468..482 '|s| s.method()': |S| -> u64
468..482 '|s| s.method()': impl Fn(S) -> u64
469..470 's': S
472..473 's': S
472..482 's.method()': u64
@ -2562,9 +2562,9 @@ fn main() {
72..74 '_v': F
117..120 '{ }': ()
132..163 '{ ... }); }': ()
138..148 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ())
138..148 'f::<(), _>': fn f<(), impl Fn(&())>(impl Fn(&()))
138..160 'f::<()... z; })': ()
149..159 '|z| { z; }': |&()| -> ()
149..159 '|z| { z; }': impl Fn(&())
150..151 'z': &()
153..159 '{ z; }': ()
155..156 'z': &()
@ -2721,9 +2721,9 @@ fn main() {
983..998 'Vec::<i32>::new': fn new<i32>() -> Vec<i32>
983..1000 'Vec::<...:new()': Vec<i32>
983..1012 'Vec::<...iter()': IntoIter<i32>
983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>>
983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, impl Fn(i32) -> Option<u32>>
983..1101 'Vec::<... y; })': ()
1029..1074 '|x| if...None }': |i32| -> Option<u32>
1029..1074 '|x| if...None }': impl Fn(i32) -> Option<u32>
1030..1031 'x': i32
1033..1074 'if x >...None }': Option<u32>
1036..1037 'x': i32
@ -2736,7 +2736,7 @@ fn main() {
1049..1057 'x as u32': u32
1066..1074 '{ None }': Option<u32>
1068..1072 'None': Option<u32>
1090..1100 '|y| { y; }': |u32| -> ()
1090..1100 '|y| { y; }': impl Fn(u32)
1091..1092 'y': u32
1094..1100 '{ y; }': ()
1096..1097 'y': u32
@ -2979,13 +2979,13 @@ fn foo() {
52..126 '{ ...)(s) }': ()
62..63 's': Option<i32>
66..78 'Option::None': Option<i32>
88..89 'f': |Option<i32>| -> ()
92..111 '|x: Op...2>| {}': |Option<i32>| -> ()
88..89 'f': impl Fn(Option<i32>)
92..111 '|x: Op...2>| {}': impl Fn(Option<i32>)
93..94 'x': Option<i32>
109..111 '{}': ()
117..124 '(&f)(s)': ()
118..120 '&f': &|Option<i32>| -> ()
119..120 'f': |Option<i32>| -> ()
118..120 '&f': &impl Fn(Option<i32>)
119..120 'f': impl Fn(Option<i32>)
122..123 's': Option<i32>
"#]],
);
@ -3072,15 +3072,15 @@ fn foo() {
228..229 's': Option<i32>
232..236 'None': Option<i32>
246..247 'f': Box<dyn FnOnce(&Option<i32>)>
281..294 'box (|ps| {})': Box<|&Option<i32>| -> ()>
286..293 '|ps| {}': |&Option<i32>| -> ()
281..294 'box (|ps| {})': Box<impl Fn(&Option<i32>)>
286..293 '|ps| {}': impl Fn(&Option<i32>)
287..289 'ps': &Option<i32>
291..293 '{}': ()
300..301 'f': Box<dyn FnOnce(&Option<i32>)>
300..305 'f(&s)': ()
302..304 '&s': &Option<i32>
303..304 's': Option<i32>
281..294: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|&Option<i32>| -> ()>
281..294: expected Box<dyn FnOnce(&Option<i32>)>, got Box<impl Fn(&Option<i32>)>
"#]],
);
}
@ -3811,6 +3811,35 @@ fn f() {
);
}
#[test]
fn regression_14443_trait_solve() {
check_no_mismatches(
r#"
trait T {
fn f(&self) {}
}
fn main() {
struct A;
impl T for A {}
let a = A;
let b = {
struct B;
impl T for B {}
B
};
a.f();
b.f();
}
"#,
)
}
#[test]
fn associated_type_sized_bounds() {
check_infer(
@ -4286,3 +4315,63 @@ impl Trait for () {
"#,
);
}
#[test]
fn derive_macro_bounds() {
check_types(
r#"
//- minicore: clone, derive
#[derive(Clone)]
struct Copy;
struct NotCopy;
#[derive(Clone)]
struct Generic<T>(T);
trait Tr {
type Assoc;
}
impl Tr for Copy {
type Assoc = NotCopy;
}
#[derive(Clone)]
struct AssocGeneric<T: Tr>(T::Assoc);
#[derive(Clone)]
struct AssocGeneric2<T: Tr>(<T as Tr>::Assoc);
#[derive(Clone)]
struct AssocGeneric3<T: Tr>(Generic<T::Assoc>);
#[derive(Clone)]
struct Vec<T>();
#[derive(Clone)]
struct R1(Vec<R2>);
#[derive(Clone)]
struct R2(R1);
fn f() {
let x = (&Copy).clone();
//^ Copy
let x = (&NotCopy).clone();
//^ &NotCopy
let x = (&Generic(Copy)).clone();
//^ Generic<Copy>
let x = (&Generic(NotCopy)).clone();
//^ &Generic<NotCopy>
let x: &AssocGeneric<Copy> = &AssocGeneric(NotCopy);
let x = x.clone();
//^ &AssocGeneric<Copy>
let x: &AssocGeneric2<Copy> = &AssocGeneric2(NotCopy);
let x = x.clone();
//^ &AssocGeneric2<Copy>
let x: &AssocGeneric3<Copy> = &AssocGeneric3(Generic(NotCopy));
let x = x.clone();
//^ &AssocGeneric3<Copy>
let x = (&R1(Vec())).clone();
//^ R1
let x = (&R2(R1(Vec()))).clone();
//^ R2
}
"#,
);
}

View File

@ -4,7 +4,7 @@ use std::{env::var, sync::Arc};
use chalk_ir::GoalData;
use chalk_recursive::Cache;
use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver};
use chalk_solve::{logging_db::LoggingRustIrDatabase, rust_ir, Solver};
use base_db::CrateId;
use hir_def::{
@ -177,8 +177,10 @@ fn is_chalk_print() -> bool {
std::env::var("CHALK_PRINT").is_ok()
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum FnTrait {
// Warning: Order is important. If something implements `x` it should also implement
// `y` if `y <= x`.
FnOnce,
FnMut,
Fn,
@ -193,6 +195,14 @@ impl FnTrait {
}
}
pub const fn to_chalk_ir(self) -> rust_ir::ClosureKind {
match self {
FnTrait::FnOnce => rust_ir::ClosureKind::FnOnce,
FnTrait::FnMut => rust_ir::ClosureKind::FnMut,
FnTrait::Fn => rust_ir::ClosureKind::Fn,
}
}
pub fn method_name(self) -> Name {
match self {
FnTrait::FnOnce => name!(call_once),

View File

@ -4,7 +4,7 @@
use std::iter;
use base_db::CrateId;
use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex};
use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex, Mutability};
use either::Either;
use hir_def::{
db::DefDatabase,
@ -12,6 +12,7 @@ use hir_def::{
GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
WherePredicateTypeTarget,
},
hir::BindingAnnotation,
lang_item::LangItem,
resolver::{HasResolver, TypeNs},
type_ref::{TraitBoundModifier, TypeRef},
@ -24,7 +25,8 @@ use rustc_hash::FxHashSet;
use smallvec::{smallvec, SmallVec};
use crate::{
db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, WhereClause,
db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyExt,
WhereClause,
};
pub(crate) fn fn_traits(
@ -352,3 +354,20 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
_ => false,
}
}
pub(crate) fn pattern_matching_dereference_count(
cond_ty: &mut Ty,
binding_mode: &mut BindingAnnotation,
) -> usize {
let mut r = 0;
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();
r += 1;
}
r
}

View File

@ -50,7 +50,9 @@ diagnostics![
PrivateField,
ReplaceFilterMapNextWithFindMap,
TypeMismatch,
UndeclaredLabel,
UnimplementedBuiltinMacro,
UnreachableLabel,
UnresolvedExternCrate,
UnresolvedField,
UnresolvedImport,
@ -61,6 +63,13 @@ diagnostics![
UnusedMut,
];
#[derive(Debug)]
pub struct BreakOutsideOfLoop {
pub expr: InFile<AstPtr<ast::Expr>>,
pub is_break: bool,
pub bad_value_break: bool,
}
#[derive(Debug)]
pub struct UnresolvedModule {
pub decl: InFile<AstPtr<ast::Module>>,
@ -84,6 +93,17 @@ pub struct UnresolvedMacroCall {
pub path: ModPath,
pub is_bang: bool,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct UnreachableLabel {
pub node: InFile<AstPtr<ast::Lifetime>>,
pub name: Name,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct UndeclaredLabel {
pub node: InFile<AstPtr<ast::Lifetime>>,
pub name: Name,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct InactiveCode {
@ -166,13 +186,6 @@ pub struct PrivateField {
pub field: Field,
}
#[derive(Debug)]
pub struct BreakOutsideOfLoop {
pub expr: InFile<AstPtr<ast::Expr>>,
pub is_break: bool,
pub bad_value_break: bool,
}
#[derive(Debug)]
pub struct MissingUnsafe {
pub expr: InFile<AstPtr<ast::Expr>>,

View File

@ -1,6 +1,6 @@
//! HirDisplay implementations for various hir types.
use hir_def::{
adt::VariantData,
data::adt::VariantData,
generics::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
},
@ -8,6 +8,7 @@ use hir_def::{
type_ref::{TypeBound, TypeRef},
AdtId, GenericDefId,
};
use hir_expand::name;
use hir_ty::{
display::{
write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
@ -76,22 +77,22 @@ impl HirDisplay for Function {
};
let mut first = true;
for (name, type_ref) in &data.params {
// FIXME: Use resolved `param.ty` once we no longer discard lifetimes
for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)) {
let local = param.as_local(db).map(|it| it.name(db));
if !first {
f.write_str(", ")?;
} else {
first = false;
if data.has_self_param() {
if local == Some(name!(self)) {
write_self_param(type_ref, f)?;
continue;
}
}
match name {
match local {
Some(name) => write!(f, "{name}: ")?,
None => f.write_str("_: ")?,
}
// FIXME: Use resolved `param.ty` or raw `type_ref`?
// The former will ignore lifetime arguments currently.
type_ref.hir_fmt(f)?;
}

View File

@ -4,7 +4,7 @@
//! are splitting the hir.
use hir_def::{
expr::{BindingId, LabelId},
hir::{BindingId, LabelId},
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
ModuleDefId, VariantId,
};

View File

@ -39,10 +39,10 @@ use arrayvec::ArrayVec;
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
use either::Either;
use hir_def::{
adt::VariantData,
body::{BodyDiagnostic, SyntheticSyntax},
expr::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
data::adt::VariantData,
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
item_tree::ItemTreeNode,
lang_item::{LangItem, LangItemTarget},
layout::{Layout, LayoutError, ReprOptions},
@ -88,9 +88,10 @@ pub use crate::{
AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
IncorrectCase, InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount,
MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem,
PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UndeclaredLabel,
UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
UnresolvedProcMacro, UnusedMut,
},
has_source::HasSource,
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@ -108,9 +109,8 @@ pub use crate::{
pub use {
cfg::{CfgAtom, CfgExpr, CfgOptions},
hir_def::{
adt::StructKind,
attr::{Attrs, AttrsWithOwner, Documentation},
builtin_attr::AttributeTemplate,
attr::{builtin::AttributeTemplate, Attrs, AttrsWithOwner, Documentation},
data::adt::StructKind,
find_path::PrefixKind,
import_map,
nameres::ModuleSource,
@ -129,7 +129,7 @@ pub use {
ExpandResult, HirFileId, InFile, MacroFile, Origin,
},
hir_ty::{
display::{HirDisplay, HirDisplayError, HirWrite},
display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
mir::MirEvalError,
PointerCast, Safety,
},
@ -1393,6 +1393,12 @@ impl DefWithBody {
}
.into(),
),
BodyDiagnostic::UnreachableLabel { node, name } => {
acc.push(UnreachableLabel { node: node.clone(), name: name.clone() }.into())
}
BodyDiagnostic::UndeclaredLabel { node, name } => {
acc.push(UndeclaredLabel { node: node.clone(), name: name.clone() }.into())
}
}
}
@ -1405,14 +1411,6 @@ impl DefWithBody {
let field = source_map.field_syntax(expr);
acc.push(NoSuchField { field }.into())
}
&hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
expr,
is_break,
bad_value_break,
} => {
let expr = expr_syntax(expr);
acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
}
&hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
acc.push(
MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found }
@ -1484,6 +1482,14 @@ impl DefWithBody {
.into(),
)
}
&hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
expr,
is_break,
bad_value_break,
} => {
let expr = expr_syntax(expr);
acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
}
}
}
for (pat_or_expr, mismatch) in infer.type_mismatches() {
@ -1524,35 +1530,44 @@ impl DefWithBody {
let hir_body = db.body(self.into());
if let Ok(borrowck_result) = db.borrowck(self.into()) {
let mir_body = &borrowck_result.mir_body;
let mol = &borrowck_result.mutability_of_locals;
for (binding_id, _) in hir_body.bindings.iter() {
let need_mut = &mol[mir_body.binding_locals[binding_id]];
let local = Local { parent: self.into(), binding_id };
match (need_mut, local.is_mut(db)) {
(mir::MutabilityReason::Mut { .. }, true)
| (mir::MutabilityReason::Not, false) => (),
(mir::MutabilityReason::Mut { spans }, false) => {
for span in spans {
let span: InFile<SyntaxNodePtr> = match span {
mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
Ok(s) => s.map(|x| x.into()),
Err(_) => continue,
},
mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
Ok(s) => s.map(|x| match x {
Either::Left(e) => e.into(),
Either::Right(e) => e.into(),
}),
Err(_) => continue,
},
mir::MirSpan::Unknown => continue,
};
acc.push(NeedMut { local, span }.into());
if let Ok(borrowck_results) = db.borrowck(self.into()) {
for borrowck_result in borrowck_results.iter() {
let mir_body = &borrowck_result.mir_body;
let mol = &borrowck_result.mutability_of_locals;
for (binding_id, _) in hir_body.bindings.iter() {
let Some(&local) = mir_body.binding_locals.get(binding_id) else {
continue;
};
let need_mut = &mol[local];
let local = Local { parent: self.into(), binding_id };
match (need_mut, local.is_mut(db)) {
(mir::MutabilityReason::Mut { .. }, true)
| (mir::MutabilityReason::Not, false) => (),
(mir::MutabilityReason::Mut { spans }, false) => {
for span in spans {
let span: InFile<SyntaxNodePtr> = match span {
mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
Ok(s) => s.map(|x| x.into()),
Err(_) => continue,
},
mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
Ok(s) => s.map(|x| match x {
Either::Left(e) => e.into(),
Either::Right(e) => e.into(),
}),
Err(_) => continue,
},
mir::MirSpan::Unknown => continue,
};
acc.push(NeedMut { local, span }.into());
}
}
(mir::MutabilityReason::Not, true) => {
if !infer.mutated_bindings_in_closure.contains(&binding_id) {
acc.push(UnusedMut { local }.into())
}
}
}
(mir::MutabilityReason::Not, true) => acc.push(UnusedMut { local }.into()),
}
}
}
@ -1838,7 +1853,7 @@ impl Param {
}
pub fn name(&self, db: &dyn HirDatabase) -> Option<Name> {
db.function_data(self.func.id).params[self.idx].0.clone()
Some(self.as_local(db)?.name(db))
}
pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
@ -1879,7 +1894,7 @@ impl SelfParam {
func_data
.params
.first()
.map(|(_, param)| match &**param {
.map(|param| match &**param {
TypeRef::Reference(.., mutability) => match mutability {
hir_def::type_ref::Mutability::Shared => Access::Shared,
hir_def::type_ref::Mutability::Mut => Access::Exclusive,
@ -2690,9 +2705,7 @@ impl BuiltinAttr {
}
fn builtin(name: &str) -> Option<Self> {
hir_def::builtin_attr::INERT_ATTRIBUTES
.iter()
.position(|tool| tool.name == name)
hir_def::attr::builtin::find_builtin_attr_idx(name)
.map(|idx| BuiltinAttr { krate: None, idx: idx as u32 })
}
@ -2700,14 +2713,14 @@ impl BuiltinAttr {
// FIXME: Return a `Name` here
match self.krate {
Some(krate) => db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(),
None => SmolStr::new(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].name),
None => SmolStr::new(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].name),
}
}
pub fn template(&self, _: &dyn HirDatabase) -> Option<AttributeTemplate> {
match self.krate {
Some(_) => None,
None => Some(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].template),
None => Some(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].template),
}
}
}
@ -2730,7 +2743,7 @@ impl ToolModule {
}
fn builtin(name: &str) -> Option<Self> {
hir_def::builtin_attr::TOOL_MODULES
hir_def::attr::builtin::TOOL_MODULES
.iter()
.position(|&tool| tool == name)
.map(|idx| ToolModule { krate: None, idx: idx as u32 })
@ -2740,7 +2753,7 @@ impl ToolModule {
// FIXME: Return a `Name` here
match self.krate {
Some(krate) => db.crate_def_map(krate).registered_tools()[self.idx as usize].clone(),
None => SmolStr::new(hir_def::builtin_attr::TOOL_MODULES[self.idx as usize]),
None => SmolStr::new(hir_def::attr::builtin::TOOL_MODULES[self.idx as usize]),
}
}
}
@ -3379,7 +3392,12 @@ impl Type {
}
pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
let mut the_ty = &self.ty;
let callee = match self.ty.kind(Interner) {
TyKind::Ref(_, _, ty) if ty.as_closure().is_some() => {
the_ty = ty;
Callee::Closure(ty.as_closure().unwrap())
}
TyKind::Closure(id, _) => Callee::Closure(*id),
TyKind::Function(_) => Callee::FnPtr,
TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?),
@ -3394,7 +3412,7 @@ impl Type {
}
};
let sig = self.ty.callable_sig(db)?;
let sig = the_ty.callable_sig(db)?;
Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false })
}

View File

@ -8,7 +8,7 @@ use base_db::{FileId, FileRange};
use either::Either;
use hir_def::{
body,
expr::Expr,
hir::Expr,
macro_id_to_def_id,
resolver::{self, HasResolver, Resolver, TypeNs},
type_ref::Mutability,
@ -1065,7 +1065,7 @@ impl<'db> SemanticsImpl<'db> {
fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
let analyze = self.analyze(ty.syntax())?;
let ctx = body::LowerCtx::new(self.db.upcast(), analyze.file_id);
let ctx = body::LowerCtx::with_file_id(self.db.upcast(), analyze.file_id);
let ty = hir_ty::TyLoweringContext::new(self.db, &analyze.resolver)
.lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone()));
Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
@ -1672,7 +1672,7 @@ impl<'a> SemanticsScope<'a> {
/// Resolve a path as-if it was written at the given scope. This is
/// necessary a heuristic, as it doesn't take hygiene into account.
pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id);
let ctx = body::LowerCtx::with_file_id(self.db.upcast(), self.file_id);
let path = Path::from_src(path.clone(), &ctx)?;
resolve_hir_path(self.db, &self.resolver, &path)
}

View File

@ -14,7 +14,7 @@
//! expression, an item definition.
//!
//! Knowing only the syntax gives us relatively little info. For example,
//! looking at the syntax of the function we can realise that it is a part of an
//! looking at the syntax of the function we can realize that it is a part of an
//! `impl` block, but we won't be able to tell what trait function the current
//! function overrides, and whether it does that correctly. For that, we need to
//! go from [`ast::Fn`] to [`crate::Function`], and that's exactly what this
@ -88,9 +88,11 @@
use base_db::FileId;
use hir_def::{
child_by_source::ChildBySource,
dyn_map::DynMap,
expr::{BindingId, LabelId},
keys::{self, Key},
dyn_map::{
keys::{self, Key},
DynMap,
},
hir::{BindingId, LabelId},
AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId,
GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId,
TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,

View File

@ -17,7 +17,7 @@ use hir_def::{
scope::{ExprScopes, ScopeId},
Body, BodySourceMap,
},
expr::{ExprId, Pat, PatId},
hir::{ExprId, Pat, PatId},
lang_item::LangItem,
macro_id_to_def_id,
path::{ModPath, Path, PathKind},
@ -463,7 +463,7 @@ impl SourceAnalyzer {
db: &dyn HirDatabase,
macro_call: InFile<&ast::MacroCall>,
) -> Option<Macro> {
let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id);
let ctx = body::LowerCtx::with_file_id(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())
}

View File

@ -0,0 +1,209 @@
use ide_db::assists::{AssistId, AssistKind};
use syntax::ast::{self, HasGenericParams, HasName};
use syntax::{AstNode, SyntaxKind};
use crate::assist_context::{AssistContext, Assists};
// Assist: convert_nested_function_to_closure
//
// Converts a function that is defined within the body of another function into a closure.
//
// ```
// fn main() {
// fn fo$0o(label: &str, number: u64) {
// println!("{}: {}", label, number);
// }
//
// foo("Bar", 100);
// }
// ```
// ->
// ```
// fn main() {
// let foo = |label: &str, number: u64| {
// println!("{}: {}", label, number);
// };
//
// foo("Bar", 100);
// }
// ```
pub(crate) fn convert_nested_function_to_closure(
acc: &mut Assists,
ctx: &AssistContext<'_>,
) -> Option<()> {
let name = ctx.find_node_at_offset::<ast::Name>()?;
let function = name.syntax().parent().and_then(ast::Fn::cast)?;
if !is_nested_function(&function) || is_generic(&function) || has_modifiers(&function) {
return None;
}
let target = function.syntax().text_range();
let body = function.body()?;
let name = function.name()?;
let param_list = function.param_list()?;
acc.add(
AssistId("convert_nested_function_to_closure", AssistKind::RefactorRewrite),
"Convert nested function to closure",
target,
|edit| {
let params = &param_list.syntax().text().to_string();
let params = params.strip_prefix("(").unwrap_or(params);
let params = params.strip_suffix(")").unwrap_or(params);
let mut body = body.to_string();
if !has_semicolon(&function) {
body.push(';');
}
edit.replace(target, format!("let {name} = |{params}| {body}"));
},
)
}
/// Returns whether the given function is nested within the body of another function.
fn is_nested_function(function: &ast::Fn) -> bool {
function.syntax().ancestors().skip(1).find_map(ast::Item::cast).map_or(false, |it| {
matches!(it, ast::Item::Fn(_) | ast::Item::Static(_) | ast::Item::Const(_))
})
}
/// Returns whether the given nested function has generic parameters.
fn is_generic(function: &ast::Fn) -> bool {
function.generic_param_list().is_some()
}
/// Returns whether the given nested function has any modifiers:
///
/// - `async`,
/// - `const` or
/// - `unsafe`
fn has_modifiers(function: &ast::Fn) -> bool {
function.async_token().is_some()
|| function.const_token().is_some()
|| function.unsafe_token().is_some()
}
/// Returns whether the given nested function has a trailing semicolon.
fn has_semicolon(function: &ast::Fn) -> bool {
function
.syntax()
.next_sibling_or_token()
.map(|t| t.kind() == SyntaxKind::SEMICOLON)
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
use super::convert_nested_function_to_closure;
#[test]
fn convert_nested_function_to_closure_works() {
check_assist(
convert_nested_function_to_closure,
r#"
fn main() {
fn $0foo(a: u64, b: u64) -> u64 {
2 * (a + b)
}
_ = foo(3, 4);
}
"#,
r#"
fn main() {
let foo = |a: u64, b: u64| {
2 * (a + b)
};
_ = foo(3, 4);
}
"#,
);
}
#[test]
fn convert_nested_function_to_closure_works_with_existing_semicolon() {
check_assist(
convert_nested_function_to_closure,
r#"
fn main() {
fn foo$0(a: u64, b: u64) -> u64 {
2 * (a + b)
};
_ = foo(3, 4);
}
"#,
r#"
fn main() {
let foo = |a: u64, b: u64| {
2 * (a + b)
};
_ = foo(3, 4);
}
"#,
);
}
#[test]
fn convert_nested_function_to_closure_is_not_suggested_on_top_level_function() {
check_assist_not_applicable(
convert_nested_function_to_closure,
r#"
fn ma$0in() {}
"#,
);
}
#[test]
fn convert_nested_function_to_closure_is_not_suggested_when_cursor_off_name() {
check_assist_not_applicable(
convert_nested_function_to_closure,
r#"
fn main() {
fn foo(a: u64, $0b: u64) -> u64 {
2 * (a + b)
}
_ = foo(3, 4);
}
"#,
);
}
#[test]
fn convert_nested_function_to_closure_is_not_suggested_if_function_has_generic_params() {
check_assist_not_applicable(
convert_nested_function_to_closure,
r#"
fn main() {
fn fo$0o<S: Into<String>>(s: S) -> String {
s.into()
}
_ = foo("hello");
}
"#,
);
}
#[test]
fn convert_nested_function_to_closure_is_not_suggested_if_function_has_modifier() {
check_assist_not_applicable(
convert_nested_function_to_closure,
r#"
fn main() {
const fn fo$0o(s: String) -> String {
s
}
_ = foo("hello");
}
"#,
);
}
}

View File

@ -70,6 +70,11 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
}
let node = ctx.covering_element();
if matches!(node.kind(), T!['{'] | T!['}'] | T!['('] | T![')'] | T!['['] | T![']']) {
cov_mark::hit!(extract_function_in_braces_is_not_applicable);
return None;
}
if node.kind() == COMMENT {
cov_mark::hit!(extract_function_in_comment_is_not_applicable);
return None;
@ -5800,4 +5805,40 @@ fn $0fun_name() -> ControlFlow<()> {
"#,
);
}
#[test]
fn in_left_curly_is_not_applicable() {
cov_mark::check!(extract_function_in_braces_is_not_applicable);
check_assist_not_applicable(extract_function, r"fn foo() { $0}$0");
}
#[test]
fn in_right_curly_is_not_applicable() {
cov_mark::check!(extract_function_in_braces_is_not_applicable);
check_assist_not_applicable(extract_function, r"fn foo() $0{$0 }");
}
#[test]
fn in_left_paren_is_not_applicable() {
cov_mark::check!(extract_function_in_braces_is_not_applicable);
check_assist_not_applicable(extract_function, r"fn foo( $0)$0 { }");
}
#[test]
fn in_right_paren_is_not_applicable() {
cov_mark::check!(extract_function_in_braces_is_not_applicable);
check_assist_not_applicable(extract_function, r"fn foo $0($0 ) { }");
}
#[test]
fn in_left_brack_is_not_applicable() {
cov_mark::check!(extract_function_in_braces_is_not_applicable);
check_assist_not_applicable(extract_function, r"fn foo(arr: &mut [i32$0]$0) {}");
}
#[test]
fn in_right_brack_is_not_applicable() {
cov_mark::check!(extract_function_in_braces_is_not_applicable);
check_assist_not_applicable(extract_function, r"fn foo(arr: &mut $0[$0i32]) {}");
}
}

View File

@ -1,3 +1,4 @@
use hir::TypeInfo;
use stdx::format_to;
use syntax::{
ast::{self, AstNode},
@ -46,21 +47,24 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
.take_while(|it| ctx.selection_trimmed().contains_range(it.text_range()))
.find_map(valid_target_expr)?;
if let Some(ty_info) = ctx.sema.type_of_expr(&to_extract) {
if ty_info.adjusted().is_unit() {
return None;
}
let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted);
if matches!(&ty, Some(ty_info) if ty_info.is_unit()) {
return None;
}
let reference_modifier = match get_receiver_type(ctx, &to_extract) {
let parent = to_extract.syntax().parent().and_then(ast::Expr::cast);
let needs_adjust = parent
.as_ref()
.map_or(false, |it| matches!(it, ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_)));
let reference_modifier = match ty.filter(|_| needs_adjust) {
Some(receiver_type) if receiver_type.is_mutable_reference() => "&mut ",
Some(receiver_type) if receiver_type.is_reference() => "&",
_ => "",
};
let parent_ref_expr = to_extract.syntax().parent().and_then(ast::RefExpr::cast);
let var_modifier = match parent_ref_expr {
Some(expr) if expr.mut_token().is_some() => "mut ",
let var_modifier = match parent {
Some(ast::Expr::RefExpr(expr)) if expr.mut_token().is_some() => "mut ",
_ => "",
};
@ -164,22 +168,6 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
}
}
fn get_receiver_type(ctx: &AssistContext<'_>, expression: &ast::Expr) -> Option<hir::Type> {
let receiver = get_receiver(expression.clone())?;
Some(ctx.sema.type_of_expr(&receiver)?.original())
}
/// In the expression `a.b.c.x()`, find `a`
fn get_receiver(expression: ast::Expr) -> Option<ast::Expr> {
match expression {
ast::Expr::FieldExpr(field) if field.expr().is_some() => {
let nested_expression = &field.expr()?;
get_receiver(nested_expression.to_owned())
}
_ => Some(expression),
}
}
#[derive(Debug)]
enum Anchor {
Before(SyntaxNode),
@ -944,6 +932,11 @@ struct S {
vec: Vec<u8>
}
struct Vec<T>;
impl<T> Vec<T> {
fn push(&mut self, _:usize) {}
}
fn foo(s: &mut S) {
$0s.vec$0.push(0);
}"#,
@ -952,6 +945,11 @@ struct S {
vec: Vec<u8>
}
struct Vec<T>;
impl<T> Vec<T> {
fn push(&mut self, _:usize) {}
}
fn foo(s: &mut S) {
let $0vec = &mut s.vec;
vec.push(0);
@ -973,6 +971,10 @@ struct X {
struct S {
vec: Vec<u8>
}
struct Vec<T>;
impl<T> Vec<T> {
fn push(&mut self, _:usize) {}
}
fn foo(f: &mut Y) {
$0f.field.field.vec$0.push(0);
@ -987,6 +989,10 @@ struct X {
struct S {
vec: Vec<u8>
}
struct Vec<T>;
impl<T> Vec<T> {
fn push(&mut self, _:usize) {}
}
fn foo(f: &mut Y) {
let $0vec = &mut f.field.field.vec;
@ -1123,7 +1129,7 @@ struct S {
}
fn foo(s: S) {
let $0x = s.sub;
let $0x = &s.sub;
x.do_thing();
}"#,
);
@ -1189,7 +1195,7 @@ impl X {
fn foo() {
let local = &mut S::new();
let $0x = &mut local.sub;
let $0x = &local.sub;
x.do_thing();
}"#,
);

View File

@ -1910,7 +1910,6 @@ fn bar(new: fn) ${0:-> _} {
#[test]
fn add_function_with_closure_arg() {
// FIXME: The argument in `bar` is wrong.
check_assist(
generate_function,
r"
@ -1925,7 +1924,7 @@ fn foo() {
bar(closure)
}
fn bar(closure: _) {
fn bar(closure: impl Fn(i64) -> i64) {
${0:todo!()}
}
",

View File

@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
// }
//
// struct Bar;
// $0impl Foo for Bar {
// $0impl Foo for Bar$0 {
// const B: u8 = 17;
// fn c() {}
// type A = String;
@ -45,6 +45,16 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
let items = impl_ast.assoc_item_list()?;
// restrict the range
// if cursor is in assoc_items, abort
let assoc_range = items.syntax().text_range();
let cursor_position = ctx.offset();
if assoc_range.contains_inclusive(cursor_position) {
cov_mark::hit!(not_applicable_editing_assoc_items);
return None;
}
let assoc_items = items.assoc_items().collect::<Vec<_>>();
let path = impl_ast
@ -264,9 +274,9 @@ trait Bar {
}
struct Foo;
impl Bar for Foo {
$0impl Bar for Foo {
type Fooo = ();
type Foo = ();$0
type Foo = ();
}"#,
r#"
trait Bar {
@ -281,4 +291,29 @@ impl Bar for Foo {
}"#,
)
}
#[test]
fn not_applicable_editing_assoc_items() {
cov_mark::check!(not_applicable_editing_assoc_items);
check_assist_not_applicable(
reorder_impl_items,
r#"
trait Bar {
type T;
const C: ();
fn a() {}
fn z() {}
fn b() {}
}
struct Foo;
impl Bar for Foo {
type T = ();$0
const C: () = ();
fn z() {}
fn a() {}
fn b() {}
}
"#,
)
}
}

View File

@ -116,9 +116,11 @@ trait AddRewrite {
new: Vec<T>,
target: TextRange,
) -> Option<()>;
fn yeet() {}
}
impl AddRewrite for Assists {
fn yeet() {}
fn add_rewrite<T: AstNode>(
&mut self,
label: &str,

View File

@ -122,6 +122,7 @@ mod handlers {
mod convert_iter_for_each_to_for;
mod convert_let_else_to_match;
mod convert_match_to_let_else;
mod convert_nested_function_to_closure;
mod convert_tuple_struct_to_named_struct;
mod convert_named_struct_to_tuple_struct;
mod convert_to_guarded_return;
@ -228,8 +229,9 @@ mod handlers {
convert_iter_for_each_to_for::convert_iter_for_each_to_for,
convert_iter_for_each_to_for::convert_for_loop_with_for_each,
convert_let_else_to_match::convert_let_else_to_match,
convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
convert_match_to_let_else::convert_match_to_let_else,
convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
convert_nested_function_to_closure::convert_nested_function_to_closure,
convert_to_guarded_return::convert_to_guarded_return,
convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro,

View File

@ -494,6 +494,31 @@ impl Point {
)
}
#[test]
fn doctest_convert_nested_function_to_closure() {
check_doc_test(
"convert_nested_function_to_closure",
r#####"
fn main() {
fn fo$0o(label: &str, number: u64) {
println!("{}: {}", label, number);
}
foo("Bar", 100);
}
"#####,
r#####"
fn main() {
let foo = |label: &str, number: u64| {
println!("{}: {}", label, number);
};
foo("Bar", 100);
}
"#####,
)
}
#[test]
fn doctest_convert_to_guarded_return() {
check_doc_test(
@ -2116,7 +2141,7 @@ trait Foo {
}
struct Bar;
$0impl Foo for Bar {
$0impl Foo for Bar$0 {
const B: u8 = 17;
fn c() {}
type A = String;

View File

@ -90,8 +90,6 @@ impl Assist {
let comment_blocks = sourcegen::CommentBlock::extract("Assist", &text);
for block in comment_blocks {
// FIXME: doesn't support blank lines yet, need to tweak
// `extract_comment_blocks` for that.
let id = block.id;
assert!(
id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),

View File

@ -23,7 +23,7 @@ pub(crate) mod env_vars;
use std::iter;
use hir::{known, ScopeDef, Variant};
use hir::{known, HasAttrs, ScopeDef, Variant};
use ide_db::{imports::import_assets::LocatedImport, SymbolKind};
use syntax::ast;
@ -173,6 +173,9 @@ impl Completions {
resolution: hir::ScopeDef,
doc_aliases: Vec<syntax::SmolStr>,
) {
if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
return;
}
let is_private_editable = match ctx.def_is_visible(&resolution) {
Visible::Yes => false,
Visible::Editable => true,
@ -198,6 +201,9 @@ impl Completions {
local_name: hir::Name,
resolution: hir::ScopeDef,
) {
if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
return;
}
let is_private_editable = match ctx.def_is_visible(&resolution) {
Visible::Yes => false,
Visible::Editable => true,
@ -220,6 +226,9 @@ impl Completions {
path_ctx: &PathCompletionCtx,
e: hir::Enum,
) {
if !ctx.check_stability(Some(&e.attrs(ctx.db))) {
return;
}
e.variants(ctx.db)
.into_iter()
.for_each(|variant| self.add_enum_variant(ctx, path_ctx, variant, None));
@ -233,6 +242,9 @@ impl Completions {
local_name: hir::Name,
doc_aliases: Vec<syntax::SmolStr>,
) {
if !ctx.check_stability(Some(&module.attrs(ctx.db))) {
return;
}
self.add_path_resolution(
ctx,
path_ctx,
@ -249,6 +261,9 @@ impl Completions {
mac: hir::Macro,
local_name: hir::Name,
) {
if !ctx.check_stability(Some(&mac.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&mac) {
Visible::Yes => false,
Visible::Editable => true,
@ -272,6 +287,9 @@ impl Completions {
func: hir::Function,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&func) {
Visible::Yes => false,
Visible::Editable => true,
@ -296,6 +314,9 @@ impl Completions {
receiver: Option<hir::Name>,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&func) {
Visible::Yes => false,
Visible::Editable => true,
@ -320,6 +341,9 @@ impl Completions {
func: hir::Function,
import: LocatedImport,
) {
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&func) {
Visible::Yes => false,
Visible::Editable => true,
@ -340,6 +364,9 @@ impl Completions {
}
pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
if !ctx.check_stability(Some(&konst.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&konst) {
Visible::Yes => false,
Visible::Editable => true,
@ -356,6 +383,9 @@ impl Completions {
ctx: &CompletionContext<'_>,
type_alias: hir::TypeAlias,
) {
if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&type_alias) {
Visible::Yes => false,
Visible::Editable => true,
@ -372,6 +402,9 @@ impl Completions {
ctx: &CompletionContext<'_>,
type_alias: hir::TypeAlias,
) {
if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
return;
}
self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
}
@ -382,6 +415,9 @@ impl Completions {
variant: hir::Variant,
path: hir::ModPath,
) {
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
return;
}
if let Some(builder) =
render_variant_lit(RenderContext::new(ctx), path_ctx, None, variant, Some(path))
{
@ -396,6 +432,9 @@ impl Completions {
variant: hir::Variant,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
return;
}
if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx {
cov_mark::hit!(enum_variant_pattern_path);
self.add_variant_pat(ctx, pat_ctx, Some(path_ctx), variant, local_name);
@ -417,6 +456,9 @@ impl Completions {
field: hir::Field,
ty: &hir::Type,
) {
if !ctx.check_stability(Some(&field.attrs(ctx.db))) {
return;
}
let is_private_editable = match ctx.is_visible(&field) {
Visible::Yes => false,
Visible::Editable => true,
@ -441,6 +483,9 @@ impl Completions {
path: Option<hir::ModPath>,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
return;
}
if let Some(builder) =
render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name)
{
@ -455,6 +500,9 @@ impl Completions {
path: Option<hir::ModPath>,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&un.attrs(ctx.db))) {
return;
}
let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
self.add_opt(item);
}
@ -466,6 +514,8 @@ impl Completions {
field: usize,
ty: &hir::Type,
) {
// Only used for (unnamed) tuples, whose all fields *are* stable. No need to check
// stability here.
let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
self.add(item);
}
@ -487,6 +537,9 @@ impl Completions {
variant: hir::Variant,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
return;
}
self.add_opt(render_variant_pat(
RenderContext::new(ctx),
pattern_ctx,
@ -504,6 +557,9 @@ impl Completions {
variant: hir::Variant,
path: hir::ModPath,
) {
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
return;
}
let path = Some(&path);
self.add_opt(render_variant_pat(
RenderContext::new(ctx),
@ -522,6 +578,9 @@ impl Completions {
strukt: hir::Struct,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
return;
}
self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
}
}

View File

@ -172,6 +172,43 @@ fn foo(s: S) { s.$0 }
);
}
#[test]
fn no_unstable_method_on_stable() {
check(
r#"
//- /main.rs crate:main deps:std
fn foo(s: std::S) { s.$0 }
//- /std.rs crate:std
pub struct S;
impl S {
#[unstable]
pub fn bar(&self) {}
}
"#,
expect![""],
);
}
#[test]
fn unstable_method_on_nightly() {
check(
r#"
//- toolchain:nightly
//- /main.rs crate:main deps:std
fn foo(s: std::S) { s.$0 }
//- /std.rs crate:std
pub struct S;
impl S {
#[unstable]
pub fn bar(&self) {}
}
"#,
expect![[r#"
me bar() fn(&self)
"#]],
);
}
#[test]
fn test_struct_field_completion_self() {
check(

View File

@ -37,9 +37,9 @@ pub(crate) fn complete_cargo_env_vars(
guard_env_macro(expanded, &ctx.sema)?;
let range = expanded.text_range_between_quotes()?;
CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| {
CARGO_DEFINED_VARS.into_iter().for_each(|&(var, detail)| {
let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
item.detail(*detail);
item.detail(detail);
item.add_to(acc);
});

View File

@ -262,8 +262,10 @@ fn import_on_the_fly(
.into_iter()
.filter(ns_filter)
.filter(|import| {
let original_item = &import.original_item;
!ctx.is_item_hidden(&import.item_to_import)
&& !ctx.is_item_hidden(&import.original_item)
&& !ctx.is_item_hidden(original_item)
&& ctx.check_stability(original_item.attrs(ctx.db).as_deref())
})
.sorted_by_key(|located_import| {
compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
@ -302,8 +304,10 @@ fn import_on_the_fly_pat_(
.into_iter()
.filter(ns_filter)
.filter(|import| {
let original_item = &import.original_item;
!ctx.is_item_hidden(&import.item_to_import)
&& !ctx.is_item_hidden(&import.original_item)
&& !ctx.is_item_hidden(original_item)
&& ctx.check_stability(original_item.attrs(ctx.db).as_deref())
})
.sorted_by_key(|located_import| {
compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)

View File

@ -150,21 +150,24 @@ fn complete_trait_impl(
impl_def: &ast::Impl,
) {
if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| {
use self::ImplCompletionKind::*;
match (item, kind) {
(hir::AssocItem::Function(func), All | Fn) => {
add_function_impl(acc, ctx, replacement_range, func, hir_impl)
get_missing_assoc_items(&ctx.sema, impl_def)
.into_iter()
.filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db))))
.for_each(|item| {
use self::ImplCompletionKind::*;
match (item, kind) {
(hir::AssocItem::Function(func), All | Fn) => {
add_function_impl(acc, ctx, replacement_range, func, hir_impl)
}
(hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
}
(hir::AssocItem::Const(const_), All | Const) => {
add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
}
_ => {}
}
(hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
}
(hir::AssocItem::Const(const_), All | Const) => {
add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
}
_ => {}
}
});
});
}
}

View File

@ -52,6 +52,9 @@ pub(crate) fn complete_use_path(
)
};
for (name, def) in module_scope {
if !ctx.check_stability(def.attrs(ctx.db).as_deref()) {
continue;
}
let is_name_already_imported = name
.as_text()
.map_or(false, |text| already_imported_names.contains(text.as_str()));

View File

@ -367,6 +367,8 @@ pub(crate) struct CompletionContext<'a> {
pub(super) krate: hir::Crate,
/// The module of the `scope`.
pub(super) module: hir::Module,
/// Whether nightly toolchain is used. Cached since this is looked up a lot.
is_nightly: bool,
/// The expected name of what we are completing.
/// This is usually the parameter name of the function argument we are completing.
@ -386,7 +388,7 @@ pub(crate) struct CompletionContext<'a> {
pub(super) depth_from_crate_root: usize,
}
impl<'a> CompletionContext<'a> {
impl CompletionContext<'_> {
/// The range of the identifier that is being completed.
pub(crate) fn source_range(&self) -> TextRange {
let kind = self.original_token.kind();
@ -459,6 +461,12 @@ impl<'a> CompletionContext<'a> {
}
}
/// Checks whether this item should be listed in regards to stability. Returns `true` if we should.
pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool {
let Some(attrs) = attrs else { return true; };
!attrs.is_unstable() || self.is_nightly
}
/// Whether the given trait is an operator trait or not.
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
match trait_.attrs(self.db).lang() {
@ -632,6 +640,11 @@ impl<'a> CompletionContext<'a> {
let krate = scope.krate();
let module = scope.module();
let toolchain = db.crate_graph()[krate.into()].channel;
// `toolchain == None` means we're in some detached files. Since we have no information on
// the toolchain being used, let's just allow unstable items to be listed.
let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
let mut locals = FxHashMap::default();
scope.process_all_names(&mut |name, scope| {
if let ScopeDef::Local(local) = scope {
@ -651,6 +664,7 @@ impl<'a> CompletionContext<'a> {
token,
krate,
module,
is_nightly,
expected_name,
expected_type,
qualifier_ctx,

View File

@ -23,6 +23,7 @@ mod type_pos;
mod use_tree;
mod visibility;
use expect_test::Expect;
use hir::PrefixKind;
use ide_db::{
base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
@ -215,6 +216,11 @@ pub(crate) fn check_edit_with_config(
assert_eq_text!(&ra_fixture_after, &actual)
}
fn check_empty(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture);
expect.assert_eq(&actual);
}
pub(crate) fn get_all_items(
config: CompletionConfig,
code: &str,

View File

@ -1,18 +1,13 @@
//! Completion tests for expressions.
use expect_test::{expect, Expect};
use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
expect.assert_eq(&actual)
}
fn check_empty(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture);
expect.assert_eq(&actual);
}
#[test]
fn complete_literal_struct_with_a_private_field() {
// `FooDesc.bar` is private, the completion should not be triggered.
@ -997,3 +992,105 @@ fn foo() { if foo {} el$0 { let x = 92; } }
"#]],
);
}
#[test]
fn expr_no_unstable_item_on_stable() {
check_empty(
r#"
//- /main.rs crate:main deps:std
use std::*;
fn main() {
$0
}
//- /std.rs crate:std
#[unstable]
pub struct UnstableThisShouldNotBeListed;
"#,
expect![[r#"
fn main() fn()
md std
bt u32
kw const
kw crate::
kw enum
kw extern
kw false
kw fn
kw for
kw if
kw if let
kw impl
kw let
kw loop
kw match
kw mod
kw return
kw self::
kw static
kw struct
kw trait
kw true
kw type
kw union
kw unsafe
kw use
kw while
kw while let
sn macro_rules
sn pd
sn ppd
"#]],
);
}
#[test]
fn expr_unstable_item_on_nightly() {
check_empty(
r#"
//- toolchain:nightly
//- /main.rs crate:main deps:std
use std::*;
fn main() {
$0
}
//- /std.rs crate:std
#[unstable]
pub struct UnstableButWeAreOnNightlyAnyway;
"#,
expect![[r#"
fn main() fn()
md std
st UnstableButWeAreOnNightlyAnyway
bt u32
kw const
kw crate::
kw enum
kw extern
kw false
kw fn
kw for
kw if
kw if let
kw impl
kw let
kw loop
kw match
kw mod
kw return
kw self::
kw static
kw struct
kw trait
kw true
kw type
kw union
kw unsafe
kw use
kw while
kw while let
sn macro_rules
sn pd
sn ppd
"#]],
);
}

Some files were not shown because too many files have changed in this diff Show More