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 }} runs-on: ${{ matrix.os }}
env: env:
CC: deny_c 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: strategy:
fail-fast: false fail-fast: false
@ -50,15 +53,15 @@ jobs:
run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml
- name: Compile (tests) - 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` ¯\_(ツ)_/¯ # It's faster to `test` before `build` ¯\_(ツ)_/¯
- name: Compile (rust-analyzer) - name: Compile (rust-analyzer)
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
run: cargo build --quiet run: cargo build --quiet --features sysroot-abi
- name: Test - name: Test
run: cargo test -- --nocapture --quiet run: cargo test --features sysroot-abi -- --nocapture --quiet
- name: Run analysis-stats on rust-analyzer - name: Run analysis-stats on rust-analyzer
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'

7
Cargo.lock generated
View File

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

View File

@ -77,6 +77,7 @@ vfs = { path = "./crates/vfs", version = "0.0.0" }
# non-local crates # non-local crates
smallvec = { version = "1.10.0", features = ["const_new", "union", "const_generics"] } 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 # 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 = { version = "=1.0.156", features = ["derive"] }
serde_json = "1.0.94" serde_json = "1.0.94"

View File

@ -4,7 +4,8 @@ use std::{mem, str::FromStr, sync::Arc};
use cfg::CfgOptions; use cfg::CfgOptions;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use test_utils::{ 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 tt::token_id::{Leaf, Subtree, TokenTree};
use vfs::{file_set::FileSet, VfsPath}; use vfs::{file_set::FileSet, VfsPath};
@ -12,7 +13,7 @@ use vfs::{file_set::FileSet, VfsPath};
use crate::{ use crate::{
input::{CrateName, CrateOrigin, LangCrateOrigin}, input::{CrateName, CrateOrigin, LangCrateOrigin},
Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition, Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition,
FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, ReleaseChannel,
SourceDatabaseExt, SourceRoot, SourceRootId, SourceDatabaseExt, SourceRoot, SourceRootId,
}; };
@ -102,7 +103,14 @@ impl ChangeFixture {
ra_fixture: &str, ra_fixture: &str,
mut proc_macro_defs: Vec<(String, ProcMacro)>, mut proc_macro_defs: Vec<(String, ProcMacro)>,
) -> ChangeFixture { ) -> 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 change = Change::new();
let mut files = Vec::new(); let mut files = Vec::new();
@ -166,7 +174,7 @@ impl ChangeFixture {
.as_deref() .as_deref()
.map(Arc::from) .map(Arc::from)
.ok_or_else(|| "target_data_layout unset".into()), .ok_or_else(|| "target_data_layout unset".into()),
None, Some(toolchain),
); );
let prev = crates.insert(crate_name.clone(), crate_id); let prev = crates.insert(crate_name.clone(), crate_id);
assert!(prev.is_none()); assert!(prev.is_none());
@ -205,7 +213,7 @@ impl ChangeFixture {
default_target_data_layout default_target_data_layout
.map(|x| x.into()) .map(|x| x.into())
.ok_or_else(|| "target_data_layout unset".into()), .ok_or_else(|| "target_data_layout unset".into()),
None, Some(toolchain),
); );
} else { } else {
for (from, to, prelude) in crate_deps { for (from, to, prelude) in crate_deps {
@ -247,7 +255,7 @@ impl ChangeFixture {
false, false,
CrateOrigin::Lang(LangCrateOrigin::Core), CrateOrigin::Lang(LangCrateOrigin::Core),
target_layout.clone(), target_layout.clone(),
None, Some(toolchain),
); );
for krate in all_crates { for krate in all_crates {
@ -286,7 +294,7 @@ impl ChangeFixture {
true, true,
CrateOrigin::Local { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
target_layout, target_layout,
None, Some(toolchain),
); );
proc_macros.insert(proc_macros_crate, Ok(proc_macro)); 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. //! A higher level attributes based on TokenTree, with also some shortcuts.
pub mod builtin;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -267,6 +269,10 @@ impl Attrs {
pub fn is_proc_macro_derive(&self) -> bool { pub fn is_proc_macro_derive(&self) -> bool {
self.by_key("proc_macro_derive").exists() self.by_key("proc_macro_derive").exists()
} }
pub fn is_unstable(&self) -> bool {
self.by_key("unstable").exists()
}
} }
use std::slice::Iter as SliceIter; use std::slice::Iter as SliceIter;

View File

@ -13,10 +13,12 @@ use cfg::{CfgExpr, CfgOptions};
use drop_bomb::DropBomb; use drop_bomb::DropBomb;
use either::Either; use either::Either;
use hir_expand::{ 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 la_arena::{Arena, ArenaMap};
use limit::Limit; use limit::Limit;
use once_cell::unsync::OnceCell;
use profile::Count; use profile::Count;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr}; use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
@ -24,7 +26,7 @@ use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
use crate::{ use crate::{
attr::Attrs, attr::Attrs,
db::DefDatabase, db::DefDatabase,
expr::{ hir::{
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat, dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
}, },
item_scope::BuiltinShadowMode, item_scope::BuiltinShadowMode,
@ -36,7 +38,43 @@ use crate::{
UnresolvedMacro, 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 /// 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. /// avoid cyclic queries in crate def map during enum processing.
@ -76,6 +114,10 @@ impl CfgExpander {
let attrs = self.parse_attrs(db, owner); let attrs = self.parse_attrs(db, owner);
attrs.is_cfg_enabled(&self.cfg_options) attrs.is_cfg_enabled(&self.cfg_options)
} }
pub(crate) fn hygiene(&self) -> &Hygiene {
&self.hygiene
}
} }
impl Expander { impl Expander {
@ -180,6 +222,10 @@ impl Expander {
mark.bomb.defuse(); 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> { pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
InFile { file_id: self.current_file_id, value } 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), // 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. // so don't return overflow error here to avoid diagnostics duplication.
cov_mark::hit!(overflow_but_not_me); 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() { } else if self.recursion_limit(db).check(self.recursion_depth + 1).is_err() {
self.recursion_depth = usize::MAX; self.recursion_depth = usize::MAX;
cov_mark::hit!(your_stack_belongs_to_me); cov_mark::hit!(your_stack_belongs_to_me);
@ -343,6 +389,8 @@ pub enum BodyDiagnostic {
MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String }, MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId }, UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId },
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath }, 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 { impl Body {

View File

@ -7,77 +7,38 @@ use base_db::CrateId;
use either::Either; use either::Either;
use hir_expand::{ use hir_expand::{
ast_id_map::AstIdMap, ast_id_map::AstIdMap,
hygiene::Hygiene,
name::{name, AsName, Name}, name::{name, AsName, Name},
AstId, ExpandError, HirFileId, InFile, AstId, ExpandError, InFile,
}; };
use intern::Interned; use intern::Interned;
use la_arena::Arena; use la_arena::Arena;
use once_cell::unsync::OnceCell;
use profile::Count; use profile::Count;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::{ use syntax::{
ast::{ ast::{
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName, LiteralKind, self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName,
SlicePatComponents, SlicePatComponents,
}, },
AstNode, AstPtr, SyntaxNodePtr, AstNode, AstPtr, SyntaxNodePtr,
}; };
use crate::{ use crate::{
adt::StructKind, body::{Body, BodyDiagnostic, BodySourceMap, Expander, ExprPtr, LabelPtr, LowerCtx, PatPtr},
body::{Body, BodySourceMap, Expander, ExprPtr, LabelPtr, LabelSource, PatPtr}, data::adt::StructKind,
body::{BodyDiagnostic, ExprSource, PatSource},
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
db::DefDatabase, db::DefDatabase,
expr::{ hir::{
dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId, dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Expr,
FloatTypeWrapper, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, ExprId, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat,
RecordFieldPat, RecordLitField, Statement, RecordLitField, Statement,
}, },
item_scope::BuiltinShadowMode, item_scope::BuiltinShadowMode,
item_tree::ItemTree,
lang_item::LangItem, lang_item::LangItem,
path::{GenericArgs, Path}, path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef}, type_ref::{Mutability, Rawness, TypeRef},
AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro, AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
}; };
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( pub(super) fn lower(
db: &dyn DefDatabase, db: &dyn DefDatabase,
expander: Expander, expander: Expander,
@ -102,9 +63,11 @@ pub(super) fn lower(
_c: Count::new(), _c: Count::new(),
}, },
expander, expander,
current_try_block: None, current_try_block_label: None,
is_lowering_assignee_expr: false, is_lowering_assignee_expr: false,
is_lowering_generator: false, is_lowering_generator: false,
label_ribs: Vec::new(),
current_binding_owner: None,
} }
.collect(params, body, is_async_fn) .collect(params, body, is_async_fn)
} }
@ -113,12 +76,57 @@ struct ExprCollector<'a> {
db: &'a dyn DefDatabase, db: &'a dyn DefDatabase,
expander: Expander, expander: Expander,
ast_id_map: Arc<AstIdMap>, ast_id_map: Arc<AstIdMap>,
body: Body,
krate: CrateId, krate: CrateId,
body: Body,
source_map: BodySourceMap, source_map: BodySourceMap,
current_try_block: Option<LabelId>,
is_lowering_assignee_expr: bool, is_lowering_assignee_expr: bool,
is_lowering_generator: bool, is_lowering_generator: bool,
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)] #[derive(Debug, Default)]
@ -162,97 +170,35 @@ impl ExprCollector<'_> {
self.body.params.push(param_pat); self.body.params.push(param_pat);
} }
for pat in param_list for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
.params()
.zip(attr_enabled)
.filter_map(|(param, enabled)| param.pat().filter(|_| 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.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) (self.body, self.source_map)
} }
fn ctx(&self) -> LowerCtx<'_> { fn ctx(&self) -> LowerCtx<'_> {
LowerCtx::new(self.db, self.expander.current_file_id) self.expander.ctx(self.db)
}
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
} }
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@ -264,6 +210,7 @@ impl ExprCollector<'_> {
let syntax_ptr = AstPtr::new(&expr); let syntax_ptr = AstPtr::new(&expr);
self.check_cfg(&expr)?; self.check_cfg(&expr)?;
// FIXME: Move some of these arms out into separate methods for clarity
Some(match expr { Some(match expr {
ast::Expr::IfExpr(e) => { ast::Expr::IfExpr(e) => {
let then_branch = self.collect_block_opt(e.then_branch()); 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) self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
} }
ast::Expr::LetExpr(e) => { 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()); let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr) self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
} }
ast::Expr::BlockExpr(e) => match e.modifier() { ast::Expr::BlockExpr(e) => match e.modifier() {
Some(ast::BlockModifier::Try(_)) => self.collect_try_block(e), Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e),
Some(ast::BlockModifier::Unsafe(_)) => { Some(ast::BlockModifier::Unsafe(_)) => {
self.collect_block_(e, |id, statements, tail| Expr::Unsafe { self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
id, id,
@ -296,28 +243,48 @@ impl ExprCollector<'_> {
} }
Some(ast::BlockModifier::Label(label)) => { Some(ast::BlockModifier::Label(label)) => {
let label = self.collect_label(label); let label = self.collect_label(label);
self.collect_block_(e, |id, statements, tail| Expr::Block { self.with_labeled_rib(label, |this| {
id, this.collect_block_(e, |id, statements, tail| Expr::Block {
statements, id,
tail, statements,
label: Some(label), 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), None => self.collect_block(e),
}, },
ast::Expr::LoopExpr(e) => { ast::Expr::LoopExpr(e) => {
let label = e.label().map(|label| self.collect_label(label)); 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) self.alloc_expr(Expr::Loop { body, label }, syntax_ptr)
} }
ast::Expr::WhileExpr(e) => { ast::Expr::WhileExpr(e) => {
let label = e.label().map(|label| self.collect_label(label)); 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()); let condition = self.collect_expr_opt(e.condition());
self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr) self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
@ -325,8 +292,8 @@ impl ExprCollector<'_> {
ast::Expr::ForExpr(e) => { ast::Expr::ForExpr(e) => {
let label = e.label().map(|label| self.collect_label(label)); let label = e.label().map(|label| self.collect_label(label));
let iterable = self.collect_expr_opt(e.iterable()); let iterable = self.collect_expr_opt(e.iterable());
let pat = self.collect_pat_opt(e.pat()); let pat = self.collect_pat_top(e.pat());
let body = self.collect_block_opt(e.loop_body()); let body = self.collect_labelled_block_opt(label, e.loop_body());
self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr) self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
} }
ast::Expr::CallExpr(e) => { ast::Expr::CallExpr(e) => {
@ -365,7 +332,7 @@ impl ExprCollector<'_> {
.arms() .arms()
.filter_map(|arm| { .filter_map(|arm| {
self.check_cfg(&arm).map(|()| MatchArm { 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()), expr: self.collect_expr_opt(arm.expr()),
guard: arm guard: arm
.guard() .guard()
@ -386,16 +353,20 @@ impl ExprCollector<'_> {
.unwrap_or(Expr::Missing); .unwrap_or(Expr::Missing);
self.alloc_expr(path, syntax_ptr) self.alloc_expr(path, syntax_ptr)
} }
ast::Expr::ContinueExpr(e) => self.alloc_expr( ast::Expr::ContinueExpr(e) => {
Expr::Continue { label: e.lifetime().map(|l| Name::new_lifetime(&l)) }, let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
syntax_ptr, self.source_map.diagnostics.push(e);
), None
});
self.alloc_expr(Expr::Continue { label }, syntax_ptr)
}
ast::Expr::BreakExpr(e) => { 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)); let expr = e.expr().map(|e| self.collect_expr(e));
self.alloc_expr( self.alloc_expr(Expr::Break { expr, label }, syntax_ptr)
Expr::Break { expr, label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
syntax_ptr,
)
} }
ast::Expr::ParenExpr(e) => { ast::Expr::ParenExpr(e) => {
let inner = self.collect_expr_opt(e.expr()); let inner = self.collect_expr_opt(e.expr());
@ -496,14 +467,16 @@ impl ExprCollector<'_> {
None => self.alloc_expr(Expr::Missing, syntax_ptr), 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 args = Vec::new();
let mut arg_types = Vec::new(); let mut arg_types = Vec::new();
if let Some(pl) = e.param_list() { if let Some(pl) = e.param_list() {
for param in pl.params() { for param in pl.params() {
let pat = self.collect_pat_opt(param.pat()); let pat = this.collect_pat_top(param.pat());
let type_ref = 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); args.push(pat);
arg_types.push(type_ref); arg_types.push(type_ref);
} }
@ -511,14 +484,13 @@ impl ExprCollector<'_> {
let ret_type = e let ret_type = e
.ret_type() .ret_type()
.and_then(|r| r.ty()) .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; let prev_is_lowering_generator = mem::take(&mut this.is_lowering_generator);
self.is_lowering_generator = false;
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() { let movability = if e.static_token().is_some() {
Movability::Static Movability::Static
} else { } else {
@ -530,19 +502,21 @@ impl ExprCollector<'_> {
} else { } else {
ClosureKind::Closure ClosureKind::Closure
}; };
self.is_lowering_generator = prev_is_lowering_generator; this.is_lowering_generator = prev_is_lowering_generator;
let capture_by =
self.alloc_expr( if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
Expr::Closure { this.is_lowering_generator = prev_is_lowering_generator;
args: args.into(), this.current_binding_owner = prev_binding_owner;
arg_types: arg_types.into(), this.body.exprs[result_expr_id] = Expr::Closure {
ret_type, args: args.into(),
body, arg_types: arg_types.into(),
closure_kind, ret_type,
}, body,
syntax_ptr, closure_kind,
) capture_by,
} };
result_expr_id
}),
ast::Expr::BinExpr(e) => { ast::Expr::BinExpr(e) => {
let op = e.op_kind(); let op = e.op_kind();
if let Some(ast::BinaryOp::Assignment { op: None }) = op { if let Some(ast::BinaryOp::Assignment { op: None }) = op {
@ -581,7 +555,17 @@ impl ExprCollector<'_> {
} }
ArrayExprKind::Repeat { initializer, repeat } => { ArrayExprKind::Repeat { initializer, repeat } => {
let initializer = self.collect_expr_opt(initializer); 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( self.alloc_expr(
Expr::Array(Array::Repeat { initializer, repeat }), Expr::Array(Array::Repeat { initializer, repeat }),
syntax_ptr, 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>) }`, /// 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(()) }` /// `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. /// 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 { 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(); let label = self.alloc_label_desugared(Label { name: Name::generate_new_name() });
self.current_try_block = let old_label = self.current_try_block_label.replace(label);
Some(self.alloc_label_desugared(Label { name: Name::generate_new_name() }));
let expr_id = self.collect_block(e); 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 callee = self.alloc_expr_desugared(Expr::Path(try_from_output));
let Expr::Block { label, tail, .. } = &mut self.body.exprs[expr_id] else { let next_tail = match btail {
unreachable!("It is the output of collect block");
};
*label = self.current_try_block;
let next_tail = match *tail {
Some(tail) => self.alloc_expr_desugared(Expr::Call { Some(tail) => self.alloc_expr_desugared(Expr::Call {
callee, callee,
args: Box::new([tail]), args: Box::new([tail]),
@ -662,10 +676,10 @@ impl ExprCollector<'_> {
} }
}; };
let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else { 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); *tail = Some(next_tail);
self.current_try_block = prev_try_block; self.current_try_block_label = old_label;
expr_id expr_id
} }
@ -735,12 +749,13 @@ impl ExprCollector<'_> {
Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false }, Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
syntax_ptr.clone(), syntax_ptr.clone(),
); );
if let Some(label) = self.current_try_block { self.alloc_expr(
let label = Some(self.body.labels[label].name.clone()); match self.current_try_block_label {
self.alloc_expr(Expr::Break { expr: Some(result), label }, syntax_ptr.clone()) Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
} else { None => Expr::Return { expr: Some(result) },
self.alloc_expr(Expr::Return { expr: Some(result) }, syntax_ptr.clone()) },
} syntax_ptr.clone(),
)
}, },
}; };
let arms = Box::new([continue_arm, break_arm]); let arms = Box::new([continue_arm, break_arm]);
@ -785,7 +800,7 @@ impl ExprCollector<'_> {
krate: *krate, krate: *krate,
}); });
} }
Some(ExpandError::RecursionOverflowPosioned) => { Some(ExpandError::RecursionOverflowPoisoned) => {
// Recursion limit has been reached in the macro expansion tree, but not in // Recursion limit has been reached in the macro expansion tree, but not in
// this very macro call. Don't add diagnostics to avoid duplication. // this very macro call. Don't add diagnostics to avoid duplication.
} }
@ -866,7 +881,7 @@ impl ExprCollector<'_> {
if self.check_cfg(&stmt).is_none() { if self.check_cfg(&stmt).is_none() {
return; return;
} }
let pat = self.collect_pat_opt(stmt.pat()); let pat = self.collect_pat_top(stmt.pat());
let type_ref = let type_ref =
stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it))); stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
let initializer = stmt.initializer().map(|e| self.collect_expr(e)); let initializer = stmt.initializer().map(|e| self.collect_expr(e));
@ -911,10 +926,20 @@ impl ExprCollector<'_> {
block: ast::BlockExpr, block: ast::BlockExpr,
mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr, mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
) -> ExprId { ) -> ExprId {
let file_local_id = self.ast_id_map.ast_id(&block); let block_has_items = {
let ast_id = AstId::new(self.expander.current_file_id, file_local_id); 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 { Some(self.db.intern_block(BlockLoc {
ast_id, ast_id,
module: self.expander.def_map.module_id(self.expander.module), 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 { fn collect_labelled_block_opt(
let label = Label { &mut self,
name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime), label: Option<LabelId>,
}; expr: Option<ast::BlockExpr>,
self.alloc_label(label, AstPtr::new(&ast_label)) ) -> 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 { // region: patterns
self.collect_pat_(pat, &mut BindingList::default())
}
fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId { fn collect_pat_top(&mut self, pat: Option<ast::Pat>) -> PatId {
match pat { match pat {
Some(pat) => self.collect_pat(pat), Some(pat) => self.collect_pat(pat, &mut BindingList::default()),
None => self.missing_pat(), 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 { let pattern = match &pat {
ast::Pat::IdentPat(bp) => { ast::Pat::IdentPat(bp) => {
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
let annotation = let annotation =
BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some()); 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 = let is_simple_ident_pat =
annotation == BindingAnnotation::Unannotated && subpat.is_none(); annotation == BindingAnnotation::Unannotated && subpat.is_none();
@ -1045,7 +1072,7 @@ impl ExprCollector<'_> {
Pat::TupleStruct { path, args, ellipsis } Pat::TupleStruct { path, args, ellipsis }
} }
ast::Pat::RefPat(p) => { 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()); let mutability = Mutability::from_mutable(p.mut_token().is_some());
Pat::Ref { pat, mutability } Pat::Ref { pat, mutability }
} }
@ -1055,10 +1082,10 @@ impl ExprCollector<'_> {
path.map(Pat::Path).unwrap_or(Pat::Missing) path.map(Pat::Path).unwrap_or(Pat::Missing)
} }
ast::Pat::OrPat(p) => { 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) 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) => { ast::Pat::TuplePat(p) => {
let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list); let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
Pat::Tuple { args, ellipsis } Pat::Tuple { args, ellipsis }
@ -1073,7 +1100,7 @@ impl ExprCollector<'_> {
.fields() .fields()
.filter_map(|f| { .filter_map(|f| {
let ast_pat = f.pat()?; 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(); let name = f.field_name()?.as_name();
Some(RecordFieldPat { name, pat }) Some(RecordFieldPat { name, pat })
}) })
@ -1092,15 +1119,9 @@ impl ExprCollector<'_> {
// FIXME properly handle `RestPat` // FIXME properly handle `RestPat`
Pat::Slice { Pat::Slice {
prefix: prefix prefix: prefix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
.into_iter() slice: slice.map(|p| self.collect_pat(p, binding_list)),
.map(|p| self.collect_pat_(p, binding_list)) suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
.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 #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676
@ -1131,12 +1152,18 @@ impl ExprCollector<'_> {
Pat::Missing Pat::Missing
} }
ast::Pat::BoxPat(boxpat) => { 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 } Pat::Box { inner }
} }
ast::Pat::ConstBlockPat(const_block_pat) => { ast::Pat::ConstBlockPat(const_block_pat) => {
if let Some(expr) = const_block_pat.block_expr() { if let Some(block) = const_block_pat.block_expr() {
let expr_id = self.collect_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) Pat::ConstBlock(expr_id)
} else { } else {
Pat::Missing Pat::Missing
@ -1148,7 +1175,7 @@ impl ExprCollector<'_> {
let src = self.expander.to_source(Either::Left(AstPtr::new(&pat))); let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
let pat = let pat =
self.collect_macro_call(call, macro_ptr, true, |this, expanded_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); self.source_map.pat_map.insert(src, pat);
return pat; return pat;
@ -1162,9 +1189,9 @@ impl ExprCollector<'_> {
self.alloc_pat(pattern, Either::Left(ptr)) 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 { match pat {
Some(pat) => self.collect_pat_(pat, binding_list), Some(pat) => self.collect_pat(pat, binding_list),
None => self.missing_pat(), None => self.missing_pat(),
} }
} }
@ -1180,12 +1207,14 @@ impl ExprCollector<'_> {
// We want to skip the `..` pattern here, since we account for it above. // We want to skip the `..` pattern here, since we account for it above.
let args = args let args = args
.filter(|p| !matches!(p, ast::Pat::RestPat(_))) .filter(|p| !matches!(p, ast::Pat::RestPat(_)))
.map(|p| self.collect_pat_(p, binding_list)) .map(|p| self.collect_pat(p, binding_list))
.collect(); .collect();
(args, ellipsis) (args, ellipsis)
} }
// endregion: patterns
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
/// not. /// not.
fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> { 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) { fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
self.body.bindings[binding_id].definitions.push(pat_id); self.body.bindings[binding_id].definitions.push(pat_id);
} }
}
impl From<ast::LiteralKind> for Literal { // region: labels
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
match ast_lit_kind { fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
// FIXME: these should have actual values filled in, but unsure on perf impact let label = Label {
LiteralKind::IntNumber(lit) => { name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { };
Literal::Float( self.alloc_label(label, AstPtr::new(&ast_label))
FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())), }
builtin,
) fn resolve_label(
} else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) { &self,
Literal::Uint(lit.value().unwrap_or(0), builtin) lifetime: Option<ast::Lifetime>,
} else { ) -> Result<Option<LabelId>, BodyDiagnostic> {
let builtin = lit.suffix().and_then(BuiltinInt::from_suffix); let Some(lifetime) = lifetime else {
Literal::Int(lit.value().unwrap_or(0) as i128, builtin) 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 syntax::ast::HasName;
use crate::{ 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}, pretty::{print_generic_args, print_path, print_type_ref},
type_ref::TypeRef, type_ref::TypeRef,
}; };
@ -13,20 +15,16 @@ use crate::{
use super::*; use super::*;
pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String { pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
let needs_semi;
let header = match owner { let header = match owner {
DefWithBodyId::FunctionId(it) => { DefWithBodyId::FunctionId(it) => {
needs_semi = false;
let item_tree_id = it.lookup(db).id; 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) => { DefWithBodyId::StaticId(it) => {
needs_semi = true;
let item_tree_id = it.lookup(db).id; let item_tree_id = it.lookup(db).id;
format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name) format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name)
} }
DefWithBodyId::ConstId(it) => { DefWithBodyId::ConstId(it) => {
needs_semi = true;
let item_tree_id = it.lookup(db).id; let item_tree_id = it.lookup(db).id;
let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name { let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name {
Some(name) => name.to_string(), 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} = ") format!("const {name} = ")
} }
DefWithBodyId::VariantId(it) => { DefWithBodyId::VariantId(it) => {
needs_semi = false;
let src = it.parent.child_source(db); let src = it.parent.child_source(db);
let variant = &src.value[it.local_id]; let variant = &src.value[it.local_id];
let name = match &variant.name() { 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 }; 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); p.print_expr(body.body_expr);
if needs_semi { if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) {
p.buf.push(';'); p.buf.push(';');
} }
p.buf p.buf
@ -219,14 +226,14 @@ impl<'a> Printer<'a> {
} }
Expr::Continue { label } => { Expr::Continue { label } => {
w!(self, "continue"); w!(self, "continue");
if let Some(label) = label { if let Some(lbl) = label {
w!(self, " {}", label); w!(self, " {}", self.body[*lbl].name);
} }
} }
Expr::Break { expr, label } => { Expr::Break { expr, label } => {
w!(self, "break"); w!(self, "break");
if let Some(label) = label { if let Some(lbl) = label {
w!(self, " {}", label); w!(self, " {}", self.body[*lbl].name);
} }
if let Some(expr) = expr { if let Some(expr) = expr {
self.whitespace(); self.whitespace();
@ -355,7 +362,7 @@ impl<'a> Printer<'a> {
self.print_expr(*index); self.print_expr(*index);
w!(self, "]"); 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 { match closure_kind {
ClosureKind::Generator(Movability::Static) => { ClosureKind::Generator(Movability::Static) => {
w!(self, "static "); w!(self, "static ");
@ -365,6 +372,12 @@ impl<'a> Printer<'a> {
} }
_ => (), _ => (),
} }
match capture_by {
CaptureBy::Value => {
w!(self, "move ");
}
CaptureBy::Ref => (),
}
w!(self, "|"); w!(self, "|");
for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
if i != 0 { if i != 0 {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,12 +12,15 @@
//! //!
//! See also a neighboring `body` module. //! See also a neighboring `body` module.
pub mod type_ref;
use std::fmt; use std::fmt;
use hir_expand::name::Name; use hir_expand::name::Name;
use intern::Interned; use intern::Interned;
use la_arena::{Idx, RawIdx}; use la_arena::{Idx, RawIdx};
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::ast;
use crate::{ use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
@ -28,10 +31,10 @@ use crate::{
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}; pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
pub type ExprId = Idx<Expr>;
pub type BindingId = Idx<Binding>; pub type BindingId = Idx<Binding>;
pub type ExprId = Idx<Expr>;
/// FIXME: this is a hacky function which should be removed /// FIXME: this is a hacky function which should be removed
pub(crate) fn dummy_expr_id() -> ExprId { pub(crate) fn dummy_expr_id() -> ExprId {
ExprId::from_raw(RawIdx::from(u32::MAX)) 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)] #[derive(Debug, Clone, Eq, PartialEq)]
pub enum Expr { pub enum Expr {
/// This is produced if the syntax tree does not have a required expression piece. /// This is produced if the syntax tree does not have a required expression piece.
@ -168,11 +210,11 @@ pub enum Expr {
arms: Box<[MatchArm]>, arms: Box<[MatchArm]>,
}, },
Continue { Continue {
label: Option<Name>, label: Option<LabelId>,
}, },
Break { Break {
expr: Option<ExprId>, expr: Option<ExprId>,
label: Option<Name>, label: Option<LabelId>,
}, },
Return { Return {
expr: Option<ExprId>, expr: Option<ExprId>,
@ -233,6 +275,7 @@ pub enum Expr {
ret_type: Option<Interned<TypeRef>>, ret_type: Option<Interned<TypeRef>>,
body: ExprId, body: ExprId,
closure_kind: ClosureKind, closure_kind: ClosureKind,
capture_by: CaptureBy,
}, },
Tuple { Tuple {
exprs: Box<[ExprId]>, exprs: Box<[ExprId]>,
@ -250,6 +293,14 @@ pub enum ClosureKind {
Async, 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Movability { pub enum Movability {
Static, Static,
@ -442,6 +493,22 @@ pub struct Binding {
pub name: Name, pub name: Name,
pub mode: BindingAnnotation, pub mode: BindingAnnotation,
pub definitions: SmallVec<[PatId; 1]>, 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)] #[derive(Debug, Clone, Eq, PartialEq)]

View File

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

View File

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

View File

@ -29,7 +29,7 @@ impl<'a> Ctx<'a> {
db, db,
tree: ItemTree::default(), tree: ItemTree::default(),
source_ast_id_map: db.ast_id_map(file), 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 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 { fn data(&mut self) -> &mut ItemTreeData {
self.tree.data_mut() self.tree.data_mut()
} }
@ -321,7 +293,7 @@ impl<'a> Ctx<'a> {
} }
}; };
let ty = Interned::new(self_type); 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( self.add_attrs(
idx.into(), idx.into(),
RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()), RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()),
@ -334,19 +306,7 @@ impl<'a> Ctx<'a> {
None => { None => {
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
let ty = Interned::new(type_ref); let ty = Interned::new(type_ref);
let mut pat = param.pat(); self.data().params.alloc(Param::Normal(ty))
// 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.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &param, self.hygiene())); 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, "("); w!(self, "(");
if !params.is_empty() { if !params.is_empty() {
self.indented(|this| { self.indented(|this| {
for (i, param) in params.clone().enumerate() { for param in params.clone() {
this.print_attrs_of(param); this.print_attrs_of(param);
match &this.tree[param] { match &this.tree[param] {
Param::Normal(name, ty) => { Param::Normal(ty) => {
match name { if flags.contains(FnFlags::HAS_SELF_PARAM) {
Some(name) => w!(this, "{}: ", name), w!(this, "self: ");
None => w!(this, "_: "),
} }
this.print_type_ref(ty); this.print_type_ref(ty);
w!(this, ","); wln!(this, ",");
if flags.contains(FnFlags::HAS_SELF_PARAM) && i == 0 {
wln!(this, " // self");
} else {
wln!(this);
}
} }
Param::Varargs => { Param::Varargs => {
wln!(this, "..."); wln!(this, "...");

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ struct Foo;
#[derive(Copy)] #[derive(Copy)]
struct Foo; struct Foo;
impl < > core::marker::Copy for Foo< > {}"#]], impl < > core::marker::Copy for Foo< > where {}"#]],
); );
} }
@ -41,7 +41,7 @@ macro Copy {}
#[derive(Copy)] #[derive(Copy)]
struct Foo; 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)] #[derive(Copy)]
struct Foo<A, B>; 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)] #[derive(Copy)]
struct Foo<A, B, 'a, 'b>; 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)] #[derive(Clone)]
struct Foo<A, B>; 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)] #[derive(Clone)]
struct Foo<const X: usize, T>(u32); 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 syntax::{ast, SmolStr};
use crate::{ use crate::{
attr_macro_as_call_id, builtin_attr, attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
attr_macro_as_call_id,
db::DefDatabase, db::DefDatabase,
item_scope::BuiltinShadowMode, item_scope::BuiltinShadowMode,
macro_id_to_def_id, macro_id_to_def_id,
@ -76,19 +77,15 @@ impl DefMap {
let pred = |n: &_| *n == name; let pred = |n: &_| *n == name;
let registered = self.registered_tools.iter().map(SmolStr::as_str); 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 // FIXME: tool modules can be shadowed by actual modules
if is_tool { if is_tool {
return true; return true;
} }
if segments.len() == 1 { if segments.len() == 1 {
let registered = self.registered_attrs.iter().map(SmolStr::as_str); let mut registered = self.registered_attrs.iter().map(SmolStr::as_str);
let is_inert = builtin_attr::INERT_ATTRIBUTES let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred);
.iter()
.map(|it| it.name)
.chain(registered)
.any(pred);
return is_inert; return is_inert;
} }
} }

View File

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

View File

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

View File

@ -1,11 +1,12 @@
//! Builtin derives. //! Builtin derives.
use base_db::{CrateOrigin, LangCrateOrigin}; use base_db::{CrateOrigin, LangCrateOrigin};
use std::collections::HashSet;
use tracing::debug; use tracing::debug;
use crate::tt::{self, TokenId}; use crate::tt::{self, TokenId};
use syntax::{ use syntax::{
ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName}, ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName, HasTypeBounds, PathType},
match_ast, match_ast,
}; };
@ -60,8 +61,11 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
struct BasicAdtInfo { struct BasicAdtInfo {
name: tt::Ident, name: tt::Ident,
/// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param. /// first field is the name, and
param_types: Vec<Option<tt::Subtree>>, /// 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> { 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(|| { let mut param_type_set: HashSet<String> = HashSet::new();
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 param_types = params let param_types = params
.into_iter() .into_iter()
.flat_map(|param_list| param_list.type_or_const_params()) .flat_map(|param_list| param_list.type_or_const_params())
.map(|param| { .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 let ty = param
.ty() .ty()
.map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0) .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) Some(ty)
} else { } else {
None None
} };
(name, ty, bounds)
}) })
.collect(); .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> { fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
let info = match parse_adt(tt) { let info = match parse_adt(tt) {
Ok(info) => info, Ok(info) => info,
Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e), Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e),
}; };
let mut where_block = vec![];
let (params, args): (Vec<_>, Vec<_>) = info let (params, args): (Vec<_>, Vec<_>) = info
.param_types .param_types
.into_iter() .into_iter()
.enumerate() .map(|(ident, param_ty, bound)| {
.map(|(idx, param_ty)| {
let ident = tt::Leaf::Ident(tt::Ident {
span: tt::TokenId::unspecified(),
text: format!("T{idx}").into(),
});
let ident_ = ident.clone(); 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 { if let Some(ty) = param_ty {
(quote! { const #ident : #ty , }, quote! { #ident_ , }) (quote! { const #ident : #ty , }, quote! { #ident_ , })
} else { } else {
@ -134,9 +218,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
} }
}) })
.unzip(); .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 name = info.name;
let expanded = quote! { let expanded = quote! {
impl < ##params > #trait_path for #name < ##args > {} impl < ##params > #trait_path for #name < ##args > where ##where_block {}
}; };
ExpandResult::ok(expanded) ExpandResult::ok(expanded)
} }

View File

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

View File

@ -120,8 +120,7 @@ impl Name {
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
static CNT: AtomicUsize = AtomicUsize::new(0); static CNT: AtomicUsize = AtomicUsize::new(0);
let c = CNT.fetch_add(1, Ordering::Relaxed); 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@gennew>{c}").into())
Name::new_text(format!("__RA_geneated_name_{c}").into())
} }
/// Returns the tuple index this name represents if it is a tuple field. /// Returns the tuple index this name represents if it is a tuple field.

View File

@ -9,7 +9,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
use base_db::CrateId; use base_db::CrateId;
use hir_def::{ use hir_def::{
expr::Movability, hir::Movability,
lang_item::{lang_attr, LangItem, LangItemTarget}, lang_item::{lang_attr, LangItem, LangItemTarget},
AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId, AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId,
}; };
@ -18,9 +18,10 @@ use hir_expand::name::name;
use crate::{ use crate::{
db::HirDatabase, db::HirDatabase,
display::HirDisplay, 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}, 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, to_assoc_type_id, to_chalk_trait_id,
traits::ChalkContext, traits::ChalkContext,
utils::generics, 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(&[]), _ => 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 // 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 // can be resolved should ever reach Chalk. impl_datum relies on that
// and will panic if the trait can't be resolved. // 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(|map| map.parent())
.and_then(|module| module.containing_block()) .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)); .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); 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 { match fps {
[] => { [] => {
debug!("Unrestricted search for {:?} impls...", trait_); 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)); 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 => { fps => {
impl_maps.into_iter().chain(block_impls).for_each(|impls| { let mut f =
result.extend( |impls: Arc<TraitImpls>| {
fps.iter().flat_map(|fp| { result.extend(fps.iter().flat_map(|fp| {
impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) 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 input_output = crate::make_type_and_const_binders(it, input_output);
let movability = match self.db.body(parent)[expr] { let movability = match self.db.body(parent)[expr] {
hir_def::expr::Expr::Closure { hir_def::hir::Expr::Closure {
closure_kind: hir_def::expr::ClosureKind::Generator(movability), closure_kind: hir_def::hir::ClosureKind::Generator(movability),
.. ..
} => movability, } => movability,
_ => unreachable!("non generator expression interned as generator"), _ => unreachable!("non generator expression interned as generator"),

View File

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

View File

@ -3,7 +3,7 @@
use base_db::CrateId; use base_db::CrateId;
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData}; use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
use hir_def::{ use hir_def::{
expr::Expr, hir::Expr,
path::Path, path::Path,
resolver::{Resolver, ValueNs}, resolver::{Resolver, ValueNs},
type_ref::ConstRef, 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] #[test]
fn or_pattern() { fn or_pattern() {
check_number( check_number(

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
//! Validation of matches. //! 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. //! checking algorithm.
//! //!
//! It is modeled on the rustc module `rustc_mir_build::thir::pattern`. //! 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 chalk_ir::Mutability;
use hir_def::{ 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 hir_expand::name::Name;
use stdx::{always, never}; use stdx::{always, never};
@ -125,15 +125,15 @@ impl<'a> PatCtxt<'a> {
let variant = self.infer.variant_resolution_for_pat(pat); let variant = self.infer.variant_resolution_for_pat(pat);
let kind = match self.body[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); 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) { let arity = match *ty.kind(Interner) {
TyKind::Tuple(arity, _) => arity, TyKind::Tuple(arity, _) => arity,
_ => { _ => {
@ -146,7 +146,7 @@ impl<'a> PatCtxt<'a> {
PatKind::Leaf { subpatterns } 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 bm = self.infer.pat_binding_modes[&pat];
let name = &self.body.bindings[id].name; let name = &self.body.bindings[id].name;
match (bm, ty.kind(Interner)) { match (bm, ty.kind(Interner)) {
@ -161,13 +161,13 @@ impl<'a> PatCtxt<'a> {
PatKind::Binding { name: name.clone(), subpattern: self.lower_opt_pattern(subpat) } 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 expected_len = variant.unwrap().variant_data(self.db.upcast()).fields().len();
let subpatterns = self.lower_tuple_subpats(args, expected_len, ellipsis); let subpatterns = self.lower_tuple_subpats(args, expected_len, ellipsis);
self.lower_variant_or_leaf(pat, ty, subpatterns) 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 variant_data = variant.unwrap().variant_data(self.db.upcast());
let subpatterns = args let subpatterns = args
.iter() .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); self.errors.push(PatternError::UnresolvedVariant);
PatKind::Wild 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); self.errors.push(PatternError::Unimplemented);
@ -279,8 +279,8 @@ impl<'a> PatCtxt<'a> {
} }
} }
fn lower_lit(&mut self, expr: hir_def::expr::ExprId) -> PatKind { fn lower_lit(&mut self, expr: hir_def::hir::ExprId) -> PatKind {
use hir_def::expr::{Expr, Literal::Bool}; use hir_def::hir::{Expr, Literal::Bool};
match self.body[expr] { match self.body[expr] {
Expr::Literal(Bool(value)) => PatKind::LiteralBool { value }, Expr::Literal(Bool(value)) => PatKind::LiteralBool { value },

View File

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

View File

@ -7,8 +7,8 @@ use std::fmt::{self, Debug};
use base_db::CrateId; use base_db::CrateId;
use chalk_ir::{BoundVar, TyKind}; use chalk_ir::{BoundVar, TyKind};
use hir_def::{ use hir_def::{
adt::VariantData,
body, body,
data::adt::VariantData,
db::DefDatabase, db::DefDatabase,
find_path, find_path,
generics::{TypeOrConstParamData, TypeParamProvenance}, generics::{TypeOrConstParamData, TypeParamProvenance},
@ -23,6 +23,7 @@ use hir_expand::{hygiene::Hygiene, name::Name};
use intern::{Internable, Interned}; use intern::{Internable, Interned};
use itertools::Itertools; use itertools::Itertools;
use smallvec::SmallVec; use smallvec::SmallVec;
use stdx::never;
use crate::{ use crate::{
db::HirDatabase, db::HirDatabase,
@ -64,6 +65,7 @@ pub struct HirFormatter<'a> {
curr_size: usize, curr_size: usize,
pub(crate) max_size: Option<usize>, pub(crate) max_size: Option<usize>,
omit_verbose_types: bool, omit_verbose_types: bool,
closure_style: ClosureStyle,
display_target: DisplayTarget, display_target: DisplayTarget,
} }
@ -87,6 +89,7 @@ pub trait HirDisplay {
max_size: Option<usize>, max_size: Option<usize>,
omit_verbose_types: bool, omit_verbose_types: bool,
display_target: DisplayTarget, display_target: DisplayTarget,
closure_style: ClosureStyle,
) -> HirDisplayWrapper<'a, Self> ) -> HirDisplayWrapper<'a, Self>
where where
Self: Sized, Self: Sized,
@ -95,7 +98,14 @@ pub trait HirDisplay {
!matches!(display_target, DisplayTarget::SourceCode { .. }), !matches!(display_target, DisplayTarget::SourceCode { .. }),
"HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead" "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. /// Returns a `Display`able type that is human-readable.
@ -109,6 +119,7 @@ pub trait HirDisplay {
t: self, t: self,
max_size: None, max_size: None,
omit_verbose_types: false, omit_verbose_types: false,
closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::Diagnostics, display_target: DisplayTarget::Diagnostics,
} }
} }
@ -128,6 +139,7 @@ pub trait HirDisplay {
t: self, t: self,
max_size, max_size,
omit_verbose_types: true, omit_verbose_types: true,
closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::Diagnostics, display_target: DisplayTarget::Diagnostics,
} }
} }
@ -147,6 +159,7 @@ pub trait HirDisplay {
curr_size: 0, curr_size: 0,
max_size: None, max_size: None,
omit_verbose_types: false, omit_verbose_types: false,
closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::SourceCode { module_id }, display_target: DisplayTarget::SourceCode { module_id },
}) { }) {
Ok(()) => {} Ok(()) => {}
@ -166,6 +179,7 @@ pub trait HirDisplay {
t: self, t: self,
max_size: None, max_size: None,
omit_verbose_types: false, omit_verbose_types: false,
closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::Test, display_target: DisplayTarget::Test,
} }
} }
@ -253,7 +267,6 @@ impl DisplayTarget {
pub enum DisplaySourceCodeError { pub enum DisplaySourceCodeError {
PathNotFound, PathNotFound,
UnknownType, UnknownType,
Closure,
Generator, Generator,
} }
@ -274,9 +287,23 @@ pub struct HirDisplayWrapper<'a, T> {
t: &'a T, t: &'a T,
max_size: Option<usize>, max_size: Option<usize>,
omit_verbose_types: bool, omit_verbose_types: bool,
closure_style: ClosureStyle,
display_target: DisplayTarget, 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> { impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
pub fn write_to<F: HirWrite>(&self, f: &mut F) -> Result<(), HirDisplayError> { pub fn write_to<F: HirWrite>(&self, f: &mut F) -> Result<(), HirDisplayError> {
self.t.hir_fmt(&mut HirFormatter { self.t.hir_fmt(&mut HirFormatter {
@ -287,8 +314,14 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
max_size: self.max_size, max_size: self.max_size,
omit_verbose_types: self.omit_verbose_types, omit_verbose_types: self.omit_verbose_types,
display_target: self.display_target, 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> impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
@ -919,26 +952,42 @@ impl HirDisplay for Ty {
} }
} }
} }
TyKind::Closure(.., substs) => { TyKind::Closure(id, substs) => {
if f.display_target.is_source_code() { if f.display_target.is_source_code() && f.closure_style != ClosureStyle::ImplFn {
return Err(HirDisplayError::DisplaySourceCodeError( never!("Only `impl Fn` is valid for displaying closures in source code");
DisplaySourceCodeError::Closure, }
)); 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); let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db);
if let Some(sig) = sig { 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() { if sig.params().is_empty() {
write!(f, "||")?;
} else if f.should_truncate() { } else if f.should_truncate() {
write!(f, "|{TYPE_HINT_TRUNCATION}|")?; write!(f, "{TYPE_HINT_TRUNCATION}")?;
} else { } else {
write!(f, "|")?;
f.write_joined(sig.params(), ", ")?; f.write_joined(sig.params(), ", ")?;
write!(f, "|")?;
}; };
match f.closure_style {
write!(f, " -> ")?; ClosureStyle::ImplFn => write!(f, ")")?,
sig.ret().hir_fmt(f)?; ClosureStyle::RANotation => write!(f, "|")?,
_ => unreachable!(),
}
if f.closure_style == ClosureStyle::RANotation || !sig.ret().is_unit() {
write!(f, " -> ")?;
sig.ret().hir_fmt(f)?;
}
} else { } else {
write!(f, "{{closure}}")?; 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 chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
use either::Either; use either::Either;
use hir_def::hir::LabelId;
use hir_def::{ use hir_def::{
body::Body, body::Body,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
data::{ConstData, StaticData}, data::{ConstData, StaticData},
expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId}, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
lang_item::{LangItem, LangItemTarget}, lang_item::{LangItem, LangItemTarget},
layout::Integer, layout::Integer,
path::{ModPath, Path}, path::{ModPath, Path},
@ -32,15 +33,15 @@ use hir_def::{
TraitId, TypeAliasId, VariantId, TraitId, TypeAliasId, VariantId,
}; };
use hir_expand::name::{name, Name}; use hir_expand::name::{name, Name};
use la_arena::ArenaMap; use la_arena::{ArenaMap, Entry};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use stdx::{always, never}; use stdx::{always, never};
use crate::{ use crate::{
db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode, db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
static_lifetime, to_assoc_type_id, AliasEq, AliasTy, DomainGoal, GenericArg, Goal, ImplTraitId, static_lifetime, to_assoc_type_id, traits::FnTrait, AliasEq, AliasTy, ClosureId, DomainGoal,
InEnvironment, Interner, ProjectionTy, RpitId, Substitution, TraitRef, Ty, TyBuilder, TyExt, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
TyKind, TraitRef, Ty, TyBuilder, TyExt, TyKind,
}; };
// This lint has a false positive here. See the link below for details. // This lint has a false positive here. See the link below for details.
@ -51,6 +52,8 @@ pub use coerce::could_coerce;
#[allow(unreachable_pub)] #[allow(unreachable_pub)]
pub use unify::could_unify; pub use unify::could_unify;
pub(crate) use self::closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
pub(crate) mod unify; pub(crate) mod unify;
mod path; mod path;
mod expr; mod expr;
@ -102,6 +105,8 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
ctx.infer_mut_body(); ctx.infer_mut_body();
ctx.infer_closures();
Arc::new(ctx.resolve_all()) Arc::new(ctx.resolve_all())
} }
@ -188,7 +193,7 @@ pub enum InferenceDiagnostic {
/// Contains the type the field resolves to /// Contains the type the field resolves to
field_with_same_name: Option<Ty>, field_with_same_name: Option<Ty>,
}, },
// FIXME: Make this proper // FIXME: This should be emitted in body lowering
BreakOutsideOfLoop { BreakOutsideOfLoop {
expr: ExprId, expr: ExprId,
is_break: bool, is_break: bool,
@ -311,6 +316,13 @@ pub enum AutoBorrow {
RawPtr(Mutability), RawPtr(Mutability),
} }
impl AutoBorrow {
fn mutability(self) -> Mutability {
let (AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) = self;
m
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PointerCast { pub enum PointerCast {
/// Go from a fn-item type to a fn-pointer type. /// 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_adjustments: FxHashMap<PatId, Vec<Ty>>,
pub pat_binding_modes: FxHashMap<PatId, BindingMode>, pub pat_binding_modes: FxHashMap<PatId, BindingMode>,
pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>, 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 { impl InferenceResult {
@ -408,6 +423,9 @@ impl InferenceResult {
_ => None, _ => None,
}) })
} }
pub(crate) fn closure_info(&self, closure: &ClosureId) -> &(Vec<CapturedItem>, FnTrait) {
self.closure_info.get(closure).unwrap()
}
} }
impl Index<ExprId> for InferenceResult { impl Index<ExprId> for InferenceResult {
@ -459,6 +477,14 @@ pub(crate) struct InferenceContext<'a> {
resume_yield_tys: Option<(Ty, Ty)>, resume_yield_tys: Option<(Ty, Ty)>,
diverges: Diverges, diverges: Diverges,
breakables: Vec<BreakableContext>, 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)] #[derive(Clone, Debug)]
@ -468,7 +494,7 @@ struct BreakableContext {
/// The coercion target of the context. /// The coercion target of the context.
coerce: Option<CoerceMany>, coerce: Option<CoerceMany>,
/// The optional label of the context. /// The optional label of the context.
label: Option<name::Name>, label: Option<LabelId>,
kind: BreakableKind, kind: BreakableKind,
} }
@ -483,21 +509,21 @@ enum BreakableKind {
fn find_breakable<'c>( fn find_breakable<'c>(
ctxs: &'c mut [BreakableContext], ctxs: &'c mut [BreakableContext],
label: Option<&name::Name>, label: Option<LabelId>,
) -> Option<&'c mut BreakableContext> { ) -> Option<&'c mut BreakableContext> {
let mut ctxs = ctxs let mut ctxs = ctxs
.iter_mut() .iter_mut()
.rev() .rev()
.take_while(|it| matches!(it.kind, BreakableKind::Block | BreakableKind::Loop)); .take_while(|it| matches!(it.kind, BreakableKind::Block | BreakableKind::Loop));
match label { 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)), None => ctxs.find(|ctx| matches!(ctx.kind, BreakableKind::Loop)),
} }
} }
fn find_continuable<'c>( fn find_continuable<'c>(
ctxs: &'c mut [BreakableContext], ctxs: &'c mut [BreakableContext],
label: Option<&name::Name>, label: Option<LabelId>,
) -> Option<&'c mut BreakableContext> { ) -> Option<&'c mut BreakableContext> {
match label { match label {
Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)), Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)),
@ -526,6 +552,10 @@ impl<'a> InferenceContext<'a> {
resolver, resolver,
diverges: Diverges::Maybe, diverges: Diverges::Maybe,
breakables: Vec::new(), 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) let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
.with_impl_trait_mode(ImplTraitLoweringMode::Param); .with_impl_trait_mode(ImplTraitLoweringMode::Param);
let mut param_tys = 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 // 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 // that are collected from the function data
if data.is_varargs() { 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) { let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
// RPIT opaque types use substitution of their parent function. // RPIT opaque types use substitution of their parent function.
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func); let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
fold_tys( let result =
return_ty, self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders);
|ty, _| { let rpits = rpits.skip_binders();
let opaque_ty_id = match ty.kind(Interner) { for (id, _) in rpits.impl_traits.iter() {
TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id, if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
_ => return ty, never!("Missed RPIT in `insert_inference_vars_for_rpit`");
}; e.insert(TyKind::Error.intern(Interner));
let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) { }
ImplTraitId::ReturnTypeImplTrait(_, idx) => idx, }
_ => unreachable!(), result
};
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,
)
} else { } else {
return_ty return_ty
}; };
@ -684,6 +694,50 @@ impl<'a> InferenceContext<'a> {
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); 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) { fn infer_body(&mut self) {
match self.return_coercion { match self.return_coercion {
Some(_) => self.infer_return(self.body.body_expr), 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. //! Inference of closure parameter types based on the closure's expected type.
use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, WhereClause}; use std::{cmp, collections::HashMap, convert::Infallible, mem};
use hir_def::{expr::ExprId, HasModule};
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 smallvec::SmallVec;
use stdx::never;
use crate::{ use crate::{
to_chalk_trait_id, utils, ChalkTraitId, DynTy, FnPointer, FnSig, Interner, Substitution, Ty, mir::{BorrowKind, ProjectionElem},
TyExt, TyKind, 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}; use super::{Expectation, InferenceContext};
@ -86,3 +103,700 @@ impl InferenceContext<'_> {
None 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 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::{ use hir_def::{
expr::ExprId, hir::ExprId,
lang_item::{LangItem, LangItemTarget}, lang_item::{LangItem, LangItemTarget},
}; };
use stdx::always; use stdx::always;
@ -22,7 +22,7 @@ use crate::{
TypeError, TypeMismatch, TypeError, TypeMismatch,
}, },
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, 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; use super::unify::InferenceTable;
@ -111,6 +111,8 @@ impl CoerceMany {
// pointers to have a chance at getting a match. See // 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 // 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)) { 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(..)) => { (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, // 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 // 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, cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyKind, TyVariableKind,
}; };
use hir_def::{ use hir_def::{
expr::{ generics::TypeOrConstParamData,
hir::{
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp, ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
}, },
generics::TypeOrConstParamData,
lang_item::LangItem, lang_item::LangItem,
path::{GenericArg, GenericArgs}, path::{GenericArg, GenericArgs},
BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, 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); let ty = self.infer_expr_inner(expr, expected);
// While we don't allow *arbitrary* coercions here, we *do* allow // While we don't allow *arbitrary* coercions here, we *do* allow
// coercions from ! to `expected`. // coercions from `!` to `expected`.
if ty.is_never() { if ty.is_never() {
if let Some(adjustments) = self.result.expr_adjustments.get(&expr) { if let Some(adjustments) = self.result.expr_adjustments.get(&expr) {
return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments { 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(); if let Some(target) = expected.only_has_type(&mut self.table) {
self.write_expr_adj( self.coerce(Some(expr), &ty, &target)
expr, .expect("never-to-any coercion should always succeed")
vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty.clone() }], } else {
); ty
adj_ty }
} else { } 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 ty
} }
} }
@ -212,7 +221,7 @@ impl<'a> InferenceContext<'a> {
self.diverges = Diverges::Maybe; self.diverges = Diverges::Maybe;
TyBuilder::unit() 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()); assert_eq!(args.len(), arg_types.len());
let mut sig_tys = Vec::with_capacity(arg_types.len() + 1); let mut sig_tys = Vec::with_capacity(arg_types.len() + 1);
@ -247,7 +256,7 @@ impl<'a> InferenceContext<'a> {
}) })
.intern(Interner); .intern(Interner);
let (ty, resume_yield_tys) = match closure_kind { let (id, ty, resume_yield_tys) = match closure_kind {
ClosureKind::Generator(_) => { ClosureKind::Generator(_) => {
// FIXME: report error when there are more than 1 parameter. // FIXME: report error when there are more than 1 parameter.
let resume_ty = match sig_tys.first() { 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_id = self.db.intern_generator((self.owner, tgt_expr)).into();
let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner); 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 => { ClosureKind::Closure | ClosureKind::Async => {
let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into(); 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()), Substitution::from1(Interner, sig_ty.clone()),
) )
.intern(Interner); .intern(Interner);
self.deferred_closures.entry(closure_id).or_default();
(closure_ty, None) 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 // FIXME: lift these out into a struct
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); 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_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
let prev_ret_coercion = let prev_ret_coercion =
mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty))); mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty)));
@ -306,6 +319,7 @@ impl<'a> InferenceContext<'a> {
self.diverges = prev_diverges; self.diverges = prev_diverges;
self.return_ty = prev_ret_ty; self.return_ty = prev_ret_ty;
self.return_coercion = prev_ret_coercion; self.return_coercion = prev_ret_coercion;
self.current_closure = prev_closure;
self.resume_yield_tys = prev_resume_yield_tys; self.resume_yield_tys = prev_resume_yield_tys;
ty ty
@ -331,43 +345,28 @@ impl<'a> InferenceContext<'a> {
let (param_tys, ret_ty) = match res { let (param_tys, ret_ty) = match res {
Some((func, params, ret_ty)) => { Some((func, params, ret_ty)) => {
let mut adjustments = auto_deref_adjust_steps(&derefs); 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 { if let Some(fn_x) = func {
match fn_x { self.write_fn_trait_method_resolution(
FnTrait::FnOnce => (), fn_x,
FnTrait::FnMut => { &derefed_callee,
if !matches!( &mut adjustments,
derefed_callee.kind(Interner), &callee_ty,
TyKind::Ref(Mutability::Mut, _, _) &params,
) { tgt_expr,
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_expr_adj(*callee, adjustments); self.write_expr_adj(*callee, adjustments);
(params, ret_ty) (params, ret_ty)
@ -459,8 +458,8 @@ impl<'a> InferenceContext<'a> {
self.resolver.reset_to_guard(g); self.resolver.reset_to_guard(g);
ty ty
} }
Expr::Continue { label } => { &Expr::Continue { label } => {
if let None = find_continuable(&mut self.breakables, label.as_ref()) { if let None = find_continuable(&mut self.breakables, label) {
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
expr: tgt_expr, expr: tgt_expr,
is_break: false, is_break: false,
@ -469,9 +468,9 @@ impl<'a> InferenceContext<'a> {
}; };
self.result.standard_types.never.clone() self.result.standard_types.never.clone()
} }
Expr::Break { expr, label } => { &Expr::Break { expr, label } => {
let val_ty = if let Some(expr) = *expr { let val_ty = if let Some(expr) = expr {
let opt_coerce_to = match find_breakable(&mut self.breakables, label.as_ref()) { let opt_coerce_to = match find_breakable(&mut self.breakables, label) {
Some(ctxt) => match &ctxt.coerce { Some(ctxt) => match &ctxt.coerce {
Some(coerce) => coerce.expected_ty(), Some(coerce) => coerce.expected_ty(),
None => { None => {
@ -490,13 +489,13 @@ impl<'a> InferenceContext<'a> {
TyBuilder::unit() 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(ctxt) => match ctxt.coerce.take() {
Some(mut coerce) => { Some(mut coerce) => {
coerce.coerce(self, *expr, &val_ty); coerce.coerce(self, expr, &val_ty);
// Avoiding borrowck // 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"); .expect("breakable stack changed during coercion");
ctxt.may_break = true; ctxt.may_break = true;
ctxt.coerce = Some(coerce); ctxt.coerce = Some(coerce);
@ -897,6 +896,41 @@ impl<'a> InferenceContext<'a> {
TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)).intern(Interner) 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( fn infer_expr_array(
&mut self, &mut self,
array: &Array, array: &Array,
@ -1900,7 +1934,6 @@ impl<'a> InferenceContext<'a> {
cb: impl FnOnce(&mut Self) -> T, cb: impl FnOnce(&mut Self) -> T,
) -> (Option<Ty>, T) { ) -> (Option<Ty>, T) {
self.breakables.push({ self.breakables.push({
let label = label.map(|label| self.body[label].name.clone());
BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label } BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label }
}); });
let res = cb(self); let res = cb(self);

View File

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

View File

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

View File

@ -1,6 +1,6 @@
//! Functions to detect special lang items //! 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 hir_expand::name::Name;
use crate::db::HirDatabase; 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) return Err(LayoutError::NotImplemented)
} }
TyKind::Error => return Err(LayoutError::HasErrorType),
TyKind::AssociatedType(_, _) TyKind::AssociatedType(_, _)
| TyKind::Error
| TyKind::Alias(_) | TyKind::Alias(_)
| TyKind::Placeholder(_) | TyKind::Placeholder(_)
| TyKind::BoundVar(_) | TyKind::BoundVar(_)

View File

@ -3,7 +3,7 @@
use std::ops::Bound; use std::ops::Bound;
use hir_def::{ use hir_def::{
adt::VariantData, data::adt::VariantData,
layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx}, layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId, 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; use super::layout_of_ty;
mod closure;
fn current_machine_data_layout() -> String { fn current_machine_data_layout() -> String {
project_model::target_data_layout::get(None, None, &HashMap::default()).unwrap() 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] #[track_caller]
fn check_size_and_align_expr(ra_fixture: &str, minicore: &str, size: u64, align: u64) { fn check_size_and_align_expr(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
let l = eval_expr(ra_fixture, minicore).unwrap(); let l = eval_expr(ra_fixture, minicore).unwrap();
assert_eq!(l.size.bytes(), size); assert_eq!(l.size.bytes(), size, "size mismatch");
assert_eq!(l.align.abi.bytes(), align); assert_eq!(l.align.abi.bytes(), align, "align mismatch");
} }
#[track_caller] #[track_caller]
@ -118,13 +120,31 @@ macro_rules! size_and_align {
}; };
} }
#[macro_export]
macro_rules! size_and_align_expr { 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)*) => { ($($t:tt)*) => {
{ {
#[allow(dead_code)] #[allow(dead_code)]
{ {
let val = { $($t)* }; let val = { $($t)* };
check_size_and_align_expr( $crate::layout::tests::check_size_and_align_expr(
stringify!($($t)*), stringify!($($t)*),
"", "",
::std::mem::size_of_val(&val) as u64, ::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) } fn foo() -> (impl T, impl T, impl T) { (2i64, 5i32, 7i32) }
foo() 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! { size_and_align_expr! {
struct Foo<T>(T, T, (T, T)); struct Foo<T>(T, T, (T, T));
trait 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, NoSolution, TyData,
}; };
use either::Either; 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 hir_expand::name;
use la_arena::{Arena, Idx}; use la_arena::{Arena, Idx};
use mir::MirEvalError; use mir::MirEvalError;

View File

@ -18,9 +18,9 @@ use chalk_ir::{
use either::Either; use either::Either;
use hir_def::{ use hir_def::{
adt::StructKind, body::Expander,
body::{Expander, LowerCtx},
builtin_type::BuiltinType, builtin_type::BuiltinType,
data::adt::StructKind,
generics::{ generics::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
}, },
@ -380,7 +380,7 @@ impl<'a> TyLoweringContext<'a> {
let macro_call = macro_call.to_node(self.db.upcast()); let macro_call = macro_call.to_node(self.db.upcast());
match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) { match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { 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); let type_ref = TypeRef::from_ast(&ctx, expanded);
drop(expander); drop(expander);
@ -988,7 +988,7 @@ impl<'a> TyLoweringContext<'a> {
// ignore `T: Drop` or `T: Destruct` bounds. // ignore `T: Drop` or `T: Destruct` bounds.
// - `T: ~const Drop` has a special meaning in Rust 1.61 that we don't implement. // - `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) // (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. // 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 let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) {
if matches!(lang, LangItem::Drop | LangItem::Destruct) { if matches!(lang, LangItem::Drop | LangItem::Destruct) {
@ -1082,23 +1082,23 @@ impl<'a> TyLoweringContext<'a> {
associated_ty_id: to_assoc_type_id(associated_ty), associated_ty_id: to_assoc_type_id(associated_ty),
substitution, 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(), binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
); );
if let Some(type_ref) = &binding.type_ref { if let Some(type_ref) = &binding.type_ref {
let ty = self.lower_ty(type_ref); let ty = self.lower_ty(type_ref);
let alias_eq = let alias_eq =
AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; 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() { for bound in binding.bounds.iter() {
preds.extend(self.lower_type_bound( predicates.extend(self.lower_type_bound(
bound, bound,
TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner), TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
false, false,
)); ));
} }
preds predicates
}) })
} }
@ -1165,7 +1165,7 @@ impl<'a> TyLoweringContext<'a> {
return None; 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. We shouldn't have repeated elements besides auto traits at this point.
bounds.dedup(); bounds.dedup();
@ -1634,7 +1634,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
let ctx_params = TyLoweringContext::new(db, &resolver) let ctx_params = TyLoweringContext::new(db, &resolver)
.with_impl_trait_mode(ImplTraitLoweringMode::Variable) .with_impl_trait_mode(ImplTraitLoweringMode::Variable)
.with_type_param_mode(ParamLoweringMode::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) let ctx_ret = TyLoweringContext::new(db, &resolver)
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable); .with_type_param_mode(ParamLoweringMode::Variable);

View File

@ -7,9 +7,11 @@ use std::{ops::ControlFlow, sync::Arc};
use base_db::{CrateId, Edition}; use base_db::{CrateId, Edition};
use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause}; use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause};
use hir_def::{ use hir_def::{
adt::StructFlags, data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, data::{adt::StructFlags, ImplData},
ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, item_scope::ItemScope,
TraitId, nameres::DefMap,
AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup,
ModuleDefId, ModuleId, TraitId,
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
use rustc_hash::{FxHashMap, FxHashSet}; 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 { let refed = Canonical {
value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone()) value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone())

View File

@ -3,11 +3,12 @@
use std::{fmt::Display, iter}; use std::{fmt::Display, iter};
use crate::{ 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 chalk_ir::Mutability;
use hir_def::{ use hir_def::{
expr::{BindingId, Expr, ExprId, Ordering, PatId}, hir::{BindingId, Expr, ExprId, Ordering, PatId},
DefWithBodyId, FieldId, UnionId, VariantId, DefWithBodyId, FieldId, UnionId, VariantId,
}; };
use la_arena::{Arena, ArenaMap, Idx, RawIdx}; use la_arena::{Arena, ArenaMap, Idx, RawIdx};
@ -19,9 +20,11 @@ mod pretty;
pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason}; pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError}; 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 smallvec::{smallvec, SmallVec};
use stdx::impl_from; use stdx::{impl_from, never};
use super::consteval::{intern_const_scalar, try_const_usize}; 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> { pub enum ProjectionElem<V, T> {
Deref, Deref,
Field(FieldId), Field(FieldId),
TupleField(usize), // FIXME: get rid of this, and use FieldId for tuples and closures
TupleOrClosureField(usize),
Index(V), Index(V),
ConstantIndex { offset: u64, min_length: u64, from_end: bool }, ConstantIndex { offset: u64, min_length: u64, from_end: bool },
Subslice { from: u64, to: u64, from_end: bool }, Subslice { from: u64, to: u64, from_end: bool },
@ -101,6 +105,63 @@ pub enum ProjectionElem<V, T> {
OpaqueCast(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>; type PlaceElem = ProjectionElem<LocalId, Ty>;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -123,7 +184,7 @@ pub enum AggregateKind {
Tuple(Ty), Tuple(Ty),
Adt(VariantId, Substitution), Adt(VariantId, Substitution),
Union(UnionId, FieldId), Union(UnionId, FieldId),
//Closure(LocalDefId, SubstsRef), Closure(Ty),
//Generator(LocalDefId, SubstsRef, Movability), //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 { pub enum BorrowKind {
/// Data must be immutable and is aliasable. /// Data must be immutable and is aliasable.
Shared, Shared,
@ -588,32 +649,32 @@ impl Display for BinOp {
} }
} }
impl From<hir_def::expr::ArithOp> for BinOp { impl From<hir_def::hir::ArithOp> for BinOp {
fn from(value: hir_def::expr::ArithOp) -> Self { fn from(value: hir_def::hir::ArithOp) -> Self {
match value { match value {
hir_def::expr::ArithOp::Add => BinOp::Add, hir_def::hir::ArithOp::Add => BinOp::Add,
hir_def::expr::ArithOp::Mul => BinOp::Mul, hir_def::hir::ArithOp::Mul => BinOp::Mul,
hir_def::expr::ArithOp::Sub => BinOp::Sub, hir_def::hir::ArithOp::Sub => BinOp::Sub,
hir_def::expr::ArithOp::Div => BinOp::Div, hir_def::hir::ArithOp::Div => BinOp::Div,
hir_def::expr::ArithOp::Rem => BinOp::Rem, hir_def::hir::ArithOp::Rem => BinOp::Rem,
hir_def::expr::ArithOp::Shl => BinOp::Shl, hir_def::hir::ArithOp::Shl => BinOp::Shl,
hir_def::expr::ArithOp::Shr => BinOp::Shr, hir_def::hir::ArithOp::Shr => BinOp::Shr,
hir_def::expr::ArithOp::BitXor => BinOp::BitXor, hir_def::hir::ArithOp::BitXor => BinOp::BitXor,
hir_def::expr::ArithOp::BitOr => BinOp::BitOr, hir_def::hir::ArithOp::BitOr => BinOp::BitOr,
hir_def::expr::ArithOp::BitAnd => BinOp::BitAnd, hir_def::hir::ArithOp::BitAnd => BinOp::BitAnd,
} }
} }
} }
impl From<hir_def::expr::CmpOp> for BinOp { impl From<hir_def::hir::CmpOp> for BinOp {
fn from(value: hir_def::expr::CmpOp) -> Self { fn from(value: hir_def::hir::CmpOp) -> Self {
match value { match value {
hir_def::expr::CmpOp::Eq { negated: false } => BinOp::Eq, hir_def::hir::CmpOp::Eq { negated: false } => BinOp::Eq,
hir_def::expr::CmpOp::Eq { negated: true } => BinOp::Ne, hir_def::hir::CmpOp::Eq { negated: true } => BinOp::Ne,
hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge, hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt, hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le, hir_def::hir::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::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
} }
} }
} }
@ -847,6 +908,87 @@ pub struct MirBody {
pub arg_count: usize, pub arg_count: usize,
pub binding_locals: ArenaMap<BindingId, LocalId>, pub binding_locals: ArenaMap<BindingId, LocalId>,
pub param_locals: Vec<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 { 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 // 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. // if needed for implementing a proper borrow checker.
use std::sync::Arc; use std::{iter, sync::Arc};
use hir_def::DefWithBodyId; use hir_def::DefWithBodyId;
use la_arena::ArenaMap; use la_arena::ArenaMap;
use stdx::never; use stdx::never;
use crate::db::HirDatabase; use crate::{db::HirDatabase, ClosureId};
use super::{ use super::{
BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem, BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
@ -29,14 +29,48 @@ pub struct BorrowckResult {
pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>, 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( pub fn borrowck_query(
db: &dyn HirDatabase, db: &dyn HirDatabase,
def: DefWithBodyId, def: DefWithBodyId,
) -> Result<Arc<BorrowckResult>, MirLowerError> { ) -> Result<Arc<[BorrowckResult]>, MirLowerError> {
let _p = profile::span("borrowck_query"); let _p = profile::span("borrowck_query");
let body = db.mir_body(def)?; let r = all_mir_bodies(db, def)
let r = BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body }; .map(|body| {
Ok(Arc::new(r)) 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 { fn is_place_direct(lvalue: &Place) -> bool {
@ -60,7 +94,7 @@ fn place_case(lvalue: &Place) -> ProjectionCase {
ProjectionElem::ConstantIndex { .. } ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } | ProjectionElem::Subslice { .. }
| ProjectionElem::Field(_) | ProjectionElem::Field(_)
| ProjectionElem::TupleField(_) | ProjectionElem::TupleOrClosureField(_)
| ProjectionElem::Index(_) => { | ProjectionElem::Index(_) => {
is_part_of = true; is_part_of = true;
} }

View File

@ -25,8 +25,8 @@ use crate::{
mapping::from_chalk, mapping::from_chalk,
method_resolution::{is_dyn_method, lookup_impl_method}, method_resolution::{is_dyn_method, lookup_impl_method},
traits::FnTrait, traits::FnTrait,
CallableDefId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, Substitution, CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
TraitEnvironment, Ty, TyBuilder, TyExt, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
}; };
use super::{ use super::{
@ -92,6 +92,7 @@ pub struct Evaluator<'a> {
enum Address { enum Address {
Stack(usize), Stack(usize),
Heap(usize), Heap(usize),
Invalid(usize),
} }
use Address::*; use Address::*;
@ -169,8 +170,10 @@ impl Address {
fn from_usize(x: usize) -> Self { fn from_usize(x: usize) -> Self {
if x > usize::MAX / 2 { if x > usize::MAX / 2 {
Stack(x - usize::MAX / 2) Stack(x - usize::MAX / 2)
} else if x > usize::MAX / 4 {
Heap(x - usize::MAX / 4)
} else { } else {
Heap(x) Invalid(x)
} }
} }
@ -181,7 +184,8 @@ impl Address {
fn to_usize(&self) -> usize { fn to_usize(&self) -> usize {
let as_num = match self { let as_num = match self {
Stack(x) => *x + usize::MAX / 2, Stack(x) => *x + usize::MAX / 2,
Heap(x) => *x, Heap(x) => *x + usize::MAX / 4,
Invalid(x) => *x,
}; };
as_num as_num
} }
@ -190,6 +194,7 @@ impl Address {
match self { match self {
Stack(x) => Stack(f(*x)), Stack(x) => Stack(f(*x)),
Heap(x) => Heap(f(*x)), Heap(x) => Heap(f(*x)),
Invalid(x) => Invalid(f(*x)),
} }
} }
@ -209,6 +214,7 @@ pub enum MirEvalError {
UndefinedBehavior(&'static str), UndefinedBehavior(&'static str),
Panic(String), Panic(String),
MirLowerError(FunctionId, MirLowerError), MirLowerError(FunctionId, MirLowerError),
MirLowerErrorForClosure(ClosureId, MirLowerError),
TypeIsUnsized(Ty, &'static str), TypeIsUnsized(Ty, &'static str),
NotSupported(String), NotSupported(String),
InvalidConst(Const), InvalidConst(Const),
@ -238,6 +244,9 @@ impl std::fmt::Debug for MirEvalError {
Self::MirLowerError(arg0, arg1) => { Self::MirLowerError(arg0, arg1) => {
f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish() 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::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(), Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
Self::InvalidConst(arg0) => { Self::InvalidConst(arg0) => {
@ -355,16 +364,15 @@ impl Evaluator<'_> {
self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?; self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?;
let mut metadata = None; // locals are always sized let mut metadata = None; // locals are always sized
for proj in &p.projection { 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 { match proj {
ProjectionElem::Deref => { 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() { metadata = if self.size_of(&ty, locals)?.is_none() {
Some(Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() }) Some(Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() })
} else { } else {
@ -377,78 +385,41 @@ impl Evaluator<'_> {
let offset = let offset =
from_bytes!(usize, self.read_memory(locals.ptr[*op], self.ptr_size())?); from_bytes!(usize, self.read_memory(locals.ptr[*op], self.ptr_size())?);
metadata = None; // Result of index is always sized metadata = None; // Result of index is always sized
match &ty.data(Interner).kind { let ty_size =
TyKind::Ref(_, _, inner) => match &inner.data(Interner).kind { self.size_of_sized(&ty, locals, "array inner type should be sized")?;
TyKind::Slice(inner) => { addr = addr.offset(ty_size * offset);
ty = inner.clone(); }
let ty_size = self.size_of_sized( &ProjectionElem::TupleOrClosureField(f) => {
&ty, let layout = self.layout(&prev_ty)?;
locals, let offset = layout.fields.offset(f).bytes_usize();
"slice inner type should be sized", addr = addr.offset(offset);
)?; metadata = None; // tuple field is always sized
let value = self.read_memory(addr, self.ptr_size() * 2)?; }
addr = Address::from_bytes(&value[0..8])?.offset(ty_size * offset); ProjectionElem::Field(f) => {
} let layout = self.layout(&prev_ty)?;
x => not_supported!("MIR index for ref type {x:?}"), let variant_layout = match &layout.variants {
}, Variants::Single { .. } => &layout,
TyKind::Array(inner, _) | TyKind::Slice(inner) => { Variants::Multiple { variants, .. } => {
ty = inner.clone(); &variants[match f.parent {
let ty_size = self.size_of_sized( hir_def::VariantId::EnumVariantId(x) => {
&ty, RustcEnumVariantIdx(x.local_id)
locals, }
"array inner type should be sized", _ => {
)?; return Err(MirEvalError::TypeError(
addr = addr.offset(ty_size * offset); "Multivariant layout only happens for enums",
} ))
x => not_supported!("MIR index for type {x:?}"), }
} }]
}
};
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 { .. } => { ProjectionElem::ConstantIndex { .. } => {
not_supported!("constant index") not_supported!("constant index")
} }
@ -845,6 +816,15 @@ impl Evaluator<'_> {
values.iter().copied(), 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 { Rvalue::Cast(kind, operand, target_ty) => match kind {
@ -1065,6 +1045,9 @@ impl Evaluator<'_> {
let (mem, pos) = match addr { let (mem, pos) = match addr {
Stack(x) => (&self.stack, x), Stack(x) => (&self.stack, x),
Heap(x) => (&self.heap, 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")) 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 { let (mem, pos) = match addr {
Stack(x) => (&mut self.stack, x), Stack(x) => (&mut self.stack, x),
Heap(x) => (&mut self.heap, x), Heap(x) => (&mut self.heap, x),
Invalid(_) => {
return Err(MirEvalError::UndefinedBehavior("write invalid memory address"))
}
}; };
mem.get_mut(pos..pos + r.len()) mem.get_mut(pos..pos + r.len())
.ok_or(MirEvalError::UndefinedBehavior("out of bound memory write"))? .ok_or(MirEvalError::UndefinedBehavior("out of bound memory write"))?
@ -1394,6 +1380,25 @@ impl Evaluator<'_> {
Ok(()) 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( fn exec_fn_def(
&mut self, &mut self,
def: FnDefId, def: FnDefId,
@ -1546,6 +1551,9 @@ impl Evaluator<'_> {
TyKind::Function(_) => { TyKind::Function(_) => {
self.exec_fn_pointer(func_data, destination, &args[1..], locals)?; 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:?}"), x => not_supported!("Call FnTrait methods with type {x:?}"),
} }
Ok(()) Ok(())

View File

@ -4,9 +4,9 @@ use std::{iter, mem, sync::Arc};
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
use hir_def::{ use hir_def::{
adt::{StructKind, VariantData},
body::Body, body::Body,
expr::{ data::adt::{StructKind, VariantData},
hir::{
Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId, Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
RecordFieldPat, RecordLitField, RecordFieldPat, RecordLitField,
}, },
@ -21,9 +21,16 @@ use la_arena::ArenaMap;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::{ use crate::{
consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch, consteval::ConstEvalError,
inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime, db::HirDatabase,
utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt, 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::*; use super::*;
@ -47,7 +54,7 @@ struct MirLowerCtx<'a> {
current_loop_blocks: Option<LoopBlocks>, current_loop_blocks: Option<LoopBlocks>,
// FIXME: we should resolve labels in HIR lowering and always work with label id here, not // FIXME: we should resolve labels in HIR lowering and always work with label id here, not
// with raw names. // with raw names.
labeled_loop_blocks: FxHashMap<Name, LoopBlocks>, labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>,
discr_temp: Option<Place>, discr_temp: Option<Place>,
db: &'a dyn HirDatabase, db: &'a dyn HirDatabase,
body: &'a Body, body: &'a Body,
@ -74,10 +81,12 @@ pub enum MirLowerError {
BreakWithoutLoop, BreakWithoutLoop,
Loop, Loop,
/// Something that should never happen and is definitely a bug, but we don't want to panic if it happened /// 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), LangItemNotFound(LangItem),
MutatingRvalue, MutatingRvalue,
UnresolvedLabel, UnresolvedLabel,
UnresolvedUpvar(Place),
UnaccessableLocal,
} }
macro_rules! not_supported { macro_rules! not_supported {
@ -88,8 +97,8 @@ macro_rules! not_supported {
macro_rules! implementation_error { macro_rules! implementation_error {
($x: expr) => {{ ($x: expr) => {{
::stdx::never!("MIR lower implementation bug: {}", $x); ::stdx::never!("MIR lower implementation bug: {}", format!($x));
return Err(MirLowerError::ImplementationError($x)); return Err(MirLowerError::ImplementationError(format!($x)));
}}; }};
} }
@ -116,7 +125,44 @@ impl MirLowerError {
type Result<T> = std::result::Result<T, 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> { fn temp(&mut self, ty: Ty) -> Result<LocalId> {
if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
implementation_error!("unsized temporaries"); implementation_error!("unsized temporaries");
@ -268,7 +314,7 @@ impl MirLowerCtx<'_> {
self.push_assignment( self.push_assignment(
current, current,
place, place,
Operand::Copy(self.result.binding_locals[pat_id].into()).into(), Operand::Copy(self.binding_local(pat_id)?.into()).into(),
expr_id.into(), expr_id.into(),
); );
Ok(Some(current)) Ok(Some(current))
@ -579,19 +625,19 @@ impl MirLowerCtx<'_> {
Ok(None) Ok(None)
} }
}, },
Expr::Break { expr, label } => { &Expr::Break { expr, label } => {
if let Some(expr) = expr { if let Some(expr) = expr {
let loop_data = match label { 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)?, 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); return Ok(None);
}; };
current = c; current = c;
} }
let end = match label { 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()?, None => self.current_loop_end()?,
}; };
self.set_goto(current, end); self.set_goto(current, end);
@ -713,20 +759,20 @@ impl MirLowerCtx<'_> {
Ok(Some(current)) Ok(Some(current))
} }
Expr::Box { .. } => not_supported!("box expression"), 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 { let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
return Ok(None); return Ok(None);
}; };
self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into()); self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
Ok(Some(current)) 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 { let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
return Ok(None); return Ok(None);
}; };
let operation = match op { let operation = match op {
hir_def::expr::UnaryOp::Not => UnOp::Not, hir_def::hir::UnaryOp::Not => UnOp::Not,
hir_def::expr::UnaryOp::Neg => UnOp::Neg, hir_def::hir::UnaryOp::Neg => UnOp::Neg,
_ => unreachable!(), _ => unreachable!(),
}; };
self.push_assignment( self.push_assignment(
@ -739,7 +785,7 @@ impl MirLowerCtx<'_> {
}, },
Expr::BinaryOp { lhs, rhs, op } => { Expr::BinaryOp { lhs, rhs, op } => {
let op = op.ok_or(MirLowerError::IncompleteExpr)?; 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() { if op.is_some() {
not_supported!("assignment with arith op (like +=)"); not_supported!("assignment with arith op (like +=)");
} }
@ -765,13 +811,13 @@ impl MirLowerCtx<'_> {
place, place,
Rvalue::CheckedBinaryOp( Rvalue::CheckedBinaryOp(
match op { match op {
hir_def::expr::BinaryOp::LogicOp(op) => match op { hir_def::hir::BinaryOp::LogicOp(op) => match op {
hir_def::expr::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit hir_def::hir::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
hir_def::expr::LogicOp::Or => BinOp::BitOr, hir_def::hir::LogicOp::Or => BinOp::BitOr,
}, },
hir_def::expr::BinaryOp::ArithOp(op) => BinOp::from(op), hir_def::hir::BinaryOp::ArithOp(op) => BinOp::from(op),
hir_def::expr::BinaryOp::CmpOp(op) => BinOp::from(op), hir_def::hir::BinaryOp::CmpOp(op) => BinOp::from(op),
hir_def::expr::BinaryOp::Assignment { .. } => unreachable!(), // handled above hir_def::hir::BinaryOp::Assignment { .. } => unreachable!(), // handled above
}, },
lhs_op, lhs_op,
rhs_op, rhs_op,
@ -823,7 +869,51 @@ impl MirLowerCtx<'_> {
); );
Ok(Some(current)) 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: _ } => { Expr::Tuple { exprs, is_assignee_expr: _ } => {
let Some(values) = exprs let Some(values) = exprs
.iter() .iter()
@ -893,7 +983,7 @@ impl MirLowerCtx<'_> {
let index = name let index = name
.as_tuple_index() .as_tuple_index()
.ok_or(MirLowerError::TypeError("named field on tuple"))?; .ok_or(MirLowerError::TypeError("named field on tuple"))?;
place.projection.push(ProjectionElem::TupleField(index)) place.projection.push(ProjectionElem::TupleOrClosureField(index))
} else { } else {
let field = let field =
self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?;
@ -910,7 +1000,7 @@ impl MirLowerCtx<'_> {
.size .size
.bytes_usize(); .bytes_usize();
let bytes = match l { let bytes = match l {
hir_def::expr::Literal::String(b) => { hir_def::hir::Literal::String(b) => {
let b = b.as_bytes(); let b = b.as_bytes();
let mut data = vec![]; let mut data = vec![];
data.extend(0usize.to_le_bytes()); data.extend(0usize.to_le_bytes());
@ -919,7 +1009,7 @@ impl MirLowerCtx<'_> {
mm.insert(0, b.to_vec()); mm.insert(0, b.to_vec());
return Ok(Operand::from_concrete_const(data, mm, ty)); 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![]; let mut data = vec![];
data.extend(0usize.to_le_bytes()); data.extend(0usize.to_le_bytes());
data.extend(b.len().to_le_bytes()); data.extend(b.len().to_le_bytes());
@ -927,11 +1017,11 @@ impl MirLowerCtx<'_> {
mm.insert(0, b.to_vec()); mm.insert(0, b.to_vec());
return Ok(Operand::from_concrete_const(data, mm, ty)); return Ok(Operand::from_concrete_const(data, mm, ty));
} }
hir_def::expr::Literal::Char(c) => u32::from(*c).to_le_bytes().into(), hir_def::hir::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
hir_def::expr::Literal::Bool(b) => vec![*b as u8], hir_def::hir::Literal::Bool(b) => vec![*b as u8],
hir_def::expr::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(), hir_def::hir::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::hir::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
hir_def::expr::Literal::Float(f, _) => match size { hir_def::hir::Literal::Float(f, _) => match size {
8 => f.into_f64().to_le_bytes().into(), 8 => f.into_f64().to_le_bytes().into(),
4 => f.into_f32().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 // bad as we may emit end (unneccessary unreachable block) for unterminating loop, but
// it should not affect correctness. // it should not affect correctness.
self.current_loop_end()?; self.current_loop_end()?;
self.labeled_loop_blocks.insert( self.labeled_loop_blocks
self.body.labels[label].name.clone(), .insert(label, self.current_loop_blocks.as_ref().unwrap().clone())
self.current_loop_blocks.as_ref().unwrap().clone(),
)
} else { } else {
None None
}; };
self.set_goto(prev_block, begin); self.set_goto(prev_block, begin);
f(self, begin)?; f(self, begin)?;
let my = mem::replace(&mut self.current_loop_blocks, prev) let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
.ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?; MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
)?;
if let Some(prev) = prev_label { 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) Ok(my.end)
} }
@ -1161,7 +1250,9 @@ impl MirLowerCtx<'_> {
let r = match self let r = match self
.current_loop_blocks .current_loop_blocks
.as_mut() .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 .end
{ {
Some(x) => x, Some(x) => x,
@ -1169,7 +1260,9 @@ impl MirLowerCtx<'_> {
let s = self.new_basic_block(); let s = self.new_basic_block();
self.current_loop_blocks self.current_loop_blocks
.as_mut() .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); .end = Some(s);
s s
} }
@ -1183,7 +1276,7 @@ impl MirLowerCtx<'_> {
/// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in
/// the appropriated places. /// 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 // 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 // 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 // the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
@ -1208,9 +1301,10 @@ impl MirLowerCtx<'_> {
.copied() .copied()
.map(MirSpan::PatId) .map(MirSpan::PatId)
.unwrap_or(MirSpan::Unknown); .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::StorageDead(l).with_span(span));
self.push_statement(current, StatementKind::StorageLive(l).with_span(span)); self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
Ok(())
} }
fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> { fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
@ -1220,14 +1314,14 @@ impl MirLowerCtx<'_> {
fn lower_block_to_place( fn lower_block_to_place(
&mut self, &mut self,
statements: &[hir_def::expr::Statement], statements: &[hir_def::hir::Statement],
mut current: BasicBlockId, mut current: BasicBlockId,
tail: Option<ExprId>, tail: Option<ExprId>,
place: Place, place: Place,
) -> Result<Option<Idx<BasicBlock>>> { ) -> Result<Option<Idx<BasicBlock>>> {
for statement in statements.iter() { for statement in statements.iter() {
match statement { match statement {
hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => { hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
if let Some(expr_id) = initializer { if let Some(expr_id) = initializer {
let else_block; let else_block;
let Some((init_place, c)) = let Some((init_place, c)) =
@ -1258,12 +1352,18 @@ impl MirLowerCtx<'_> {
} }
} }
} else { } else {
let mut err = None;
self.body.walk_bindings_in_pat(*pat, |b| { 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 { let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None); return Ok(None);
}; };
@ -1276,6 +1376,67 @@ impl MirLowerCtx<'_> {
None => Ok(Some(current)), 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> { 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>> { pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
let _p = profile::span("mir_body_query").detail(|| match def { let _p = profile::span("mir_body_query").detail(|| match def {
DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(), 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() { if let Some((_, x)) = infer.type_mismatches().next() {
return Err(MirLowerError::TypeMismatch(x.clone())); return Err(MirLowerError::TypeMismatch(x.clone()));
} }
let mut basic_blocks = Arena::new(); let mut ctx = MirLowerCtx::new(db, owner, body, infer);
let start_block =
basic_blocks.alloc(BasicBlock { statements: vec![], terminator: None, is_cleanup: false });
let mut locals = Arena::new();
// 0 is return local // 0 is return local
locals.alloc(Local { ty: infer[root_expr].clone() }); ctx.result.locals.alloc(Local { ty: infer[root_expr].clone() });
let mut binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new(); 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 // 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 substs = TyBuilder::placeholder_subst(db, fid);
let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs); let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs);
body.params ctx.lower_params_and_bindings(
.iter() body.params.iter().zip(callable_sig.params().iter()).map(|(x, y)| (*x, y.clone())),
.zip(callable_sig.params().iter()) binding_picker,
.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()
} else { } else {
if !body.params.is_empty() { ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
return Err(MirLowerError::TypeError("Unexpected parameter for non function body"));
}
vec![]
}; };
// 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)? { 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) Ok(ctx.result)
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -575,7 +575,7 @@ fn two_closures_lub() {
fn foo(c: i32) { fn foo(c: i32) {
let add = |a: i32, b: i32| a + b; let add = |a: i32, b: i32| a + b;
let sub = |a, b| a - b; let sub = |a, b| a - b;
//^^^^^^^^^^^^ |i32, i32| -> i32 //^^^^^^^^^^^^ impl Fn(i32, i32) -> i32
if c > 42 { add } else { sub }; if c > 42 { add } else { sub };
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fn(i32, i32) -> i32 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fn(i32, i32) -> i32
} }
@ -875,6 +875,16 @@ fn test() {
fn adjust_index() { fn adjust_index() {
check_no_mismatches( check_no_mismatches(
r" 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 //- minicore: index
struct Struct; struct Struct;
impl core::ops::Index<usize> for 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 _ ...!() {}': () 100..119 'for _ ...!() {}': ()
104..105 '_': {unknown} 104..105 '_': {unknown}
117..119 '{}': () 117..119 '{}': ()
124..134 '|| spam!()': || -> isize 124..134 '|| spam!()': impl Fn() -> isize
140..156 'while ...!() {}': () 140..156 'while ...!() {}': ()
154..156 '{}': () 154..156 '{}': ()
161..174 'break spam!()': ! 161..174 'break spam!()': !
@ -279,7 +279,7 @@ fn expr_macro_rules_expanded_in_various_places() {
114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': ()
118..119 '_': {unknown} 118..119 '_': {unknown}
131..133 '{}': () 131..133 '{}': ()
138..148 '|| spam!()': || -> isize 138..148 '|| spam!()': impl Fn() -> isize
154..170 'while ...!() {}': () 154..170 'while ...!() {}': ()
168..170 '{}': () 168..170 '{}': ()
175..188 'break spam!()': ! 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] #[test]
fn infer_trait_method_generic_1() { fn infer_trait_method_generic_1() {
// the trait implementation is intentionally incomplete -- it shouldn't matter // the trait implementation is intentionally incomplete -- it shouldn't matter
@ -1722,7 +1740,7 @@ fn test() {
Foo.foo(); Foo.foo();
//^^^ adjustments: Borrow(Ref(Not)) //^^^ adjustments: Borrow(Ref(Not))
(&Foo).foo(); (&Foo).foo();
// ^^^^ adjustments: , // ^^^^ adjustments: Deref(None), Borrow(Ref(Not))
} }
"#, "#,
); );

View File

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

View File

@ -270,7 +270,7 @@ fn infer_std_crash_5() {
61..320 '{ ... }': () 61..320 '{ ... }': ()
75..79 'name': &{unknown} 75..79 'name': &{unknown}
82..166 'if doe... }': &{unknown} 82..166 'if doe... }': &{unknown}
85..98 'doesnt_matter': {unknown} 85..98 'doesnt_matter': bool
99..128 '{ ... }': &{unknown} 99..128 '{ ... }': &{unknown}
113..118 'first': &{unknown} 113..118 'first': &{unknown}
134..166 '{ ... }': &{unknown} 134..166 '{ ... }': &{unknown}
@ -279,7 +279,7 @@ fn infer_std_crash_5() {
181..188 'content': &{unknown} 181..188 'content': &{unknown}
191..313 'if ICE... }': &{unknown} 191..313 'if ICE... }': &{unknown}
194..231 'ICE_RE..._VALUE': {unknown} 194..231 'ICE_RE..._VALUE': {unknown}
194..247 'ICE_RE...&name)': {unknown} 194..247 'ICE_RE...&name)': bool
241..246 '&name': &&{unknown} 241..246 '&name': &&{unknown}
242..246 'name': &{unknown} 242..246 'name': &{unknown}
248..276 '{ ... }': &{unknown} 248..276 '{ ... }': &{unknown}
@ -805,19 +805,19 @@ fn issue_4966() {
225..229 'iter': T 225..229 'iter': T
244..246 '{}': Vec<A> 244..246 '{}': Vec<A>
258..402 '{ ...r(); }': () 258..402 '{ ...r(); }': ()
268..273 'inner': Map<|&f64| -> f64> 268..273 'inner': Map<impl Fn(&f64) -> f64>
276..300 'Map { ... 0.0 }': Map<|&f64| -> f64> 276..300 'Map { ... 0.0 }': Map<impl Fn(&f64) -> f64>
285..298 '|_: &f64| 0.0': |&f64| -> f64 285..298 '|_: &f64| 0.0': impl Fn(&f64) -> f64
286..287 '_': &f64 286..287 '_': &f64
295..298 '0.0': f64 295..298 '0.0': f64
311..317 'repeat': Repeat<Map<|&f64| -> f64>> 311..317 'repeat': Repeat<Map<impl Fn(&f64) -> f64>>
320..345 'Repeat...nner }': Repeat<Map<|&f64| -> f64>> 320..345 'Repeat...nner }': Repeat<Map<impl Fn(&f64) -> f64>>
338..343 'inner': Map<|&f64| -> f64> 338..343 'inner': Map<impl Fn(&f64) -> f64>
356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<|&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<|&f64| -> f64>>>, Repeat<Map<|&f64| -> f64>>>(Repeat<Map<|&f64| -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<|&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<|&f64| -> f64>>>> 362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
372..378 'repeat': Repeat<Map<|&f64| -> f64>> 372..378 'repeat': Repeat<Map<impl Fn(&f64) -> f64>>
386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>> 386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
386..399 'vec.foo_bar()': {unknown} 386..399 'vec.foo_bar()': {unknown}
"#]], "#]],
); );
@ -852,7 +852,7 @@ fn main() {
123..126 'S()': S<i32> 123..126 'S()': S<i32>
132..133 's': S<i32> 132..133 's': S<i32>
132..144 's.g(|_x| {})': () 132..144 's.g(|_x| {})': ()
136..143 '|_x| {}': |&i32| -> () 136..143 '|_x| {}': impl Fn(&i32)
137..139 '_x': &i32 137..139 '_x': &i32
141..143 '{}': () 141..143 '{}': ()
150..151 's': S<i32> 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] #[test]
fn call_expected_type_closure() { fn call_expected_type_closure() {
check_types( check_types(
@ -1759,13 +1776,14 @@ const C: usize = 2 + 2;
#[test] #[test]
fn regression_14456() { fn regression_14456() {
check_no_mismatches( check_types(
r#" r#"
//- minicore: future //- minicore: future
async fn x() {} async fn x() {}
fn f() { fn f() {
let fut = x(); 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#" expect![[r#"
16..58 '{ ...; }; }': u32 16..58 '{ ...; }; }': u32
26..27 'x': || -> usize 26..27 'x': impl Fn() -> usize
30..55 '|| -> ...n 1; }': || -> usize 30..55 '|| -> ...n 1; }': impl Fn() -> usize
42..55 '{ return 1; }': usize 42..55 '{ return 1; }': usize
44..52 'return 1': ! 44..52 'return 1': !
51..52 '1': usize 51..52 '1': usize
@ -1925,8 +1925,8 @@ fn closure_return_unit() {
"#, "#,
expect![[r#" expect![[r#"
16..47 '{ ...; }; }': u32 16..47 '{ ...; }; }': u32
26..27 'x': || -> () 26..27 'x': impl Fn()
30..44 '|| { return; }': || -> () 30..44 '|| { return; }': impl Fn()
33..44 '{ return; }': () 33..44 '{ return; }': ()
35..41 'return': ! 35..41 'return': !
"#]], "#]],
@ -1943,8 +1943,8 @@ fn closure_return_inferred() {
"#, "#,
expect![[r#" expect![[r#"
16..46 '{ ..." }; }': u32 16..46 '{ ..." }; }': u32
26..27 'x': || -> &str 26..27 'x': impl Fn() -> &str
30..43 '|| { "test" }': || -> &str 30..43 '|| { "test" }': impl Fn() -> &str
33..43 '{ "test" }': &str 33..43 '{ "test" }': &str
35..41 '"test"': &str 35..41 '"test"': &str
"#]], "#]],
@ -2050,7 +2050,7 @@ fn fn_pointer_return() {
47..120 '{ ...hod; }': () 47..120 '{ ...hod; }': ()
57..63 'vtable': Vtable 57..63 'vtable': Vtable
66..90 'Vtable...| {} }': Vtable 66..90 'Vtable...| {} }': Vtable
83..88 '|| {}': || -> () 83..88 '|| {}': impl Fn()
86..88 '{}': () 86..88 '{}': ()
100..101 'm': fn() 100..101 'm': fn()
104..110 'vtable': Vtable 104..110 'vtable': Vtable
@ -2142,9 +2142,9 @@ fn main() {
149..151 'Ok': Ok<(), ()>(()) -> Result<(), ()> 149..151 'Ok': Ok<(), ()>(()) -> Result<(), ()>
149..155 'Ok(())': Result<(), ()> 149..155 'Ok(())': Result<(), ()>
152..154 '()': () 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(|... })': () 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<(), ()>> 175..227 'async ... }': impl Future<Output = Result<(), ()>>
191..205 'return Err(())': ! 191..205 'return Err(())': !
198..201 'Err': Err<(), ()>(()) -> Result<(), ()> 198..201 'Err': Err<(), ()>(()) -> Result<(), ()>
@ -2270,8 +2270,8 @@ fn infer_labelled_break_with_val() {
"#, "#,
expect![[r#" expect![[r#"
9..335 '{ ... }; }': () 9..335 '{ ... }; }': ()
19..21 '_x': || -> bool 19..21 '_x': impl Fn() -> bool
24..332 '|| 'ou... }': || -> bool 24..332 '|| 'ou... }': impl Fn() -> bool
27..332 ''outer... }': bool 27..332 ''outer... }': bool
40..332 '{ ... }': () 40..332 '{ ... }': ()
54..59 'inner': i8 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] #[test]
fn cfgd_out_assoc_items() { fn cfgd_out_assoc_items() {
check_types( 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] #[test]
fn issue_14275() { fn issue_14275() {
// FIXME: evaluate const generic // FIXME: evaluate const generic

View File

@ -90,7 +90,7 @@ fn infer_async_closure() {
async fn test() { async fn test() {
let f = async move |x: i32| x + 42; let f = async move |x: i32| x + 42;
f; f;
// ^ |i32| -> impl Future<Output = i32> // ^ impl Fn(i32) -> impl Future<Output = i32>
let a = f(4); let a = f(4);
a; a;
// ^ impl Future<Output = i32> // ^ impl Future<Output = i32>
@ -99,7 +99,7 @@ async fn test() {
// ^ i32 // ^ i32
let f = async move || 42; let f = async move || 42;
f; f;
// ^ || -> impl Future<Output = i32> // ^ impl Fn() -> impl Future<Output = i32>
let a = f(); let a = f();
a; a;
// ^ impl Future<Output = i32> // ^ impl Future<Output = i32>
@ -116,7 +116,7 @@ async fn test() {
}; };
let _: Option<u64> = c().await; let _: Option<u64> = c().await;
c; c;
// ^ || -> impl Future<Output = Option<u64>> // ^ impl Fn() -> impl Future<Output = Option<u64>>
} }
"#, "#,
); );
@ -550,7 +550,7 @@ fn test() -> u64 {
53..54 'a': S 53..54 'a': S
57..58 'S': S(fn(u32) -> u64) -> S 57..58 'S': S(fn(u32) -> u64) -> S
57..74 'S(|i| ...s 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 60..61 'i': u32
63..64 '2': u64 63..64 '2': u64
63..73 '2*i as u64': 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#" expect![[r#"
134..165 '{ ...(C)) }': (|&str, T| -> (), Bar<u8>) 134..165 '{ ...(C)) }': (impl Fn(&str, T), Bar<u8>)
140..163 '(|inpu...ar(C))': (|&str, T| -> (), Bar<u8>) 140..163 '(|inpu...ar(C))': (impl Fn(&str, T), Bar<u8>)
141..154 '|input, t| {}': |&str, T| -> () 141..154 '|input, t| {}': impl Fn(&str, T)
142..147 'input': &str 142..147 'input': &str
149..150 't': T 149..150 't': T
152..154 '{}': () 152..154 '{}': ()
@ -1506,8 +1506,8 @@ fn main() {
71..105 '{ ...()); }': () 71..105 '{ ...()); }': ()
77..78 'f': fn f(&dyn Fn(S)) 77..78 'f': fn f(&dyn Fn(S))
77..102 'f(&|nu...foo())': () 77..102 'f(&|nu...foo())': ()
79..101 '&|numb....foo()': &|S| -> () 79..101 '&|numb....foo()': &impl Fn(S)
80..101 '|numbe....foo()': |S| -> () 80..101 '|numbe....foo()': impl Fn(S)
81..87 'number': S 81..87 'number': S
89..95 'number': S 89..95 'number': S
89..101 'number.foo()': () 89..101 'number.foo()': ()
@ -1912,13 +1912,13 @@ fn test() {
131..132 'f': F 131..132 'f': F
151..153 '{}': Lazy<T, F> 151..153 '{}': Lazy<T, F>
251..497 '{ ...o(); }': () 251..497 '{ ...o(); }': ()
261..266 'lazy1': Lazy<Foo, || -> Foo> 261..266 'lazy1': Lazy<Foo, impl Fn() -> Foo>
283..292 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> 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, || -> Foo> 283..300 'Lazy::...| Foo)': Lazy<Foo, impl Fn() -> Foo>
293..299 '|| Foo': || -> Foo 293..299 '|| Foo': impl Fn() -> Foo
296..299 'Foo': Foo 296..299 'Foo': Foo
310..312 'r1': usize 310..312 'r1': usize
315..320 'lazy1': Lazy<Foo, || -> Foo> 315..320 'lazy1': Lazy<Foo, impl Fn() -> Foo>
315..326 'lazy1.foo()': usize 315..326 'lazy1.foo()': usize
368..383 'make_foo_fn_ptr': fn() -> Foo 368..383 'make_foo_fn_ptr': fn() -> Foo
399..410 'make_foo_fn': fn make_foo_fn() -> Foo 399..410 'make_foo_fn': fn make_foo_fn() -> Foo
@ -1963,20 +1963,20 @@ fn test() {
163..167 '1u32': u32 163..167 '1u32': u32
174..175 'x': Option<u32> 174..175 'x': Option<u32>
174..190 'x.map(...v + 1)': 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 181..182 'v': u32
184..185 'v': u32 184..185 'v': u32
184..189 'v + 1': u32 184..189 'v + 1': u32
188..189 '1': u32 188..189 '1': u32
196..197 'x': Option<u32> 196..197 'x': Option<u32>
196..212 'x.map(... 1u64)': Option<u64> 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 203..205 '_v': u32
207..211 '1u64': u64 207..211 '1u64': u64
222..223 'y': Option<i64> 222..223 'y': Option<i64>
239..240 'x': Option<u32> 239..240 'x': Option<u32>
239..252 'x.map(|_v| 1)': Option<i64> 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 246..248 '_v': u32
250..251 '1': i64 250..251 '1': i64
"#]], "#]],
@ -2005,11 +2005,11 @@ fn test<F: FnOnce(u32) -> u64>(f: F) {
//^^^^ u64 //^^^^ u64
let g = |v| v + 1; let g = |v| v + 1;
//^^^^^ u64 //^^^^^ u64
//^^^^^^^^^ |u64| -> u64 //^^^^^^^^^ impl Fn(u64) -> u64
g(1u64); g(1u64);
//^^^^^^^ u64 //^^^^^^^ u64
let h = |v| 1u128 + v; let h = |v| 1u128 + v;
//^^^^^^^^^^^^^ |u128| -> u128 //^^^^^^^^^^^^^ impl Fn(u128) -> u128
}"#, }"#,
); );
} }
@ -2062,17 +2062,17 @@ fn test() {
312..314 '{}': () 312..314 '{}': ()
330..489 '{ ... S); }': () 330..489 '{ ... S); }': ()
340..342 'x1': u64 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 345..368 'foo1(S...hod())': u64
350..351 'S': S 350..351 'S': S
353..367 '|s| s.method()': |S| -> u64 353..367 '|s| s.method()': impl Fn(S) -> u64
354..355 's': S 354..355 's': S
357..358 's': S 357..358 's': S
357..367 's.method()': u64 357..367 's.method()': u64
378..380 'x2': 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 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 389..390 's': S
392..393 's': S 392..393 's': S
392..402 's.method()': u64 392..402 's.method()': u64
@ -2081,14 +2081,14 @@ fn test() {
421..422 'S': S 421..422 'S': S
421..446 'S.foo1...hod())': u64 421..446 'S.foo1...hod())': u64
428..429 'S': S 428..429 'S': S
431..445 '|s| s.method()': |S| -> u64 431..445 '|s| s.method()': impl Fn(S) -> u64
432..433 's': S 432..433 's': S
435..436 's': S 435..436 's': S
435..445 's.method()': u64 435..445 's.method()': u64
456..458 'x4': u64 456..458 'x4': u64
461..462 'S': S 461..462 'S': S
461..486 'S.foo2...(), S)': u64 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 469..470 's': S
472..473 's': S 472..473 's': S
472..482 's.method()': u64 472..482 's.method()': u64
@ -2562,9 +2562,9 @@ fn main() {
72..74 '_v': F 72..74 '_v': F
117..120 '{ }': () 117..120 '{ }': ()
132..163 '{ ... }); }': () 132..163 '{ ... }); }': ()
138..148 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ()) 138..148 'f::<(), _>': fn f<(), impl Fn(&())>(impl Fn(&()))
138..160 'f::<()... z; })': () 138..160 'f::<()... z; })': ()
149..159 '|z| { z; }': |&()| -> () 149..159 '|z| { z; }': impl Fn(&())
150..151 'z': &() 150..151 'z': &()
153..159 '{ z; }': () 153..159 '{ z; }': ()
155..156 'z': &() 155..156 'z': &()
@ -2721,9 +2721,9 @@ fn main() {
983..998 'Vec::<i32>::new': fn new<i32>() -> Vec<i32> 983..998 'Vec::<i32>::new': fn new<i32>() -> Vec<i32>
983..1000 'Vec::<...:new()': Vec<i32> 983..1000 'Vec::<...:new()': Vec<i32>
983..1012 'Vec::<...iter()': IntoIter<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; })': () 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 1030..1031 'x': i32
1033..1074 'if x >...None }': Option<u32> 1033..1074 'if x >...None }': Option<u32>
1036..1037 'x': i32 1036..1037 'x': i32
@ -2736,7 +2736,7 @@ fn main() {
1049..1057 'x as u32': u32 1049..1057 'x as u32': u32
1066..1074 '{ None }': Option<u32> 1066..1074 '{ None }': Option<u32>
1068..1072 'None': Option<u32> 1068..1072 'None': Option<u32>
1090..1100 '|y| { y; }': |u32| -> () 1090..1100 '|y| { y; }': impl Fn(u32)
1091..1092 'y': u32 1091..1092 'y': u32
1094..1100 '{ y; }': () 1094..1100 '{ y; }': ()
1096..1097 'y': u32 1096..1097 'y': u32
@ -2979,13 +2979,13 @@ fn foo() {
52..126 '{ ...)(s) }': () 52..126 '{ ...)(s) }': ()
62..63 's': Option<i32> 62..63 's': Option<i32>
66..78 'Option::None': Option<i32> 66..78 'Option::None': Option<i32>
88..89 'f': |Option<i32>| -> () 88..89 'f': impl Fn(Option<i32>)
92..111 '|x: Op...2>| {}': |Option<i32>| -> () 92..111 '|x: Op...2>| {}': impl Fn(Option<i32>)
93..94 'x': Option<i32> 93..94 'x': Option<i32>
109..111 '{}': () 109..111 '{}': ()
117..124 '(&f)(s)': () 117..124 '(&f)(s)': ()
118..120 '&f': &|Option<i32>| -> () 118..120 '&f': &impl Fn(Option<i32>)
119..120 'f': |Option<i32>| -> () 119..120 'f': impl Fn(Option<i32>)
122..123 's': Option<i32> 122..123 's': Option<i32>
"#]], "#]],
); );
@ -3072,15 +3072,15 @@ fn foo() {
228..229 's': Option<i32> 228..229 's': Option<i32>
232..236 'None': Option<i32> 232..236 'None': Option<i32>
246..247 'f': Box<dyn FnOnce(&Option<i32>)> 246..247 'f': Box<dyn FnOnce(&Option<i32>)>
281..294 'box (|ps| {})': Box<|&Option<i32>| -> ()> 281..294 'box (|ps| {})': Box<impl Fn(&Option<i32>)>
286..293 '|ps| {}': |&Option<i32>| -> () 286..293 '|ps| {}': impl Fn(&Option<i32>)
287..289 'ps': &Option<i32> 287..289 'ps': &Option<i32>
291..293 '{}': () 291..293 '{}': ()
300..301 'f': Box<dyn FnOnce(&Option<i32>)> 300..301 'f': Box<dyn FnOnce(&Option<i32>)>
300..305 'f(&s)': () 300..305 'f(&s)': ()
302..304 '&s': &Option<i32> 302..304 '&s': &Option<i32>
303..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] #[test]
fn associated_type_sized_bounds() { fn associated_type_sized_bounds() {
check_infer( 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_ir::GoalData;
use chalk_recursive::Cache; 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 base_db::CrateId;
use hir_def::{ use hir_def::{
@ -177,8 +177,10 @@ fn is_chalk_print() -> bool {
std::env::var("CHALK_PRINT").is_ok() 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 { pub enum FnTrait {
// Warning: Order is important. If something implements `x` it should also implement
// `y` if `y <= x`.
FnOnce, FnOnce,
FnMut, FnMut,
Fn, 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 { pub fn method_name(self) -> Name {
match self { match self {
FnTrait::FnOnce => name!(call_once), FnTrait::FnOnce => name!(call_once),

View File

@ -4,7 +4,7 @@
use std::iter; use std::iter;
use base_db::CrateId; 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 either::Either;
use hir_def::{ use hir_def::{
db::DefDatabase, db::DefDatabase,
@ -12,6 +12,7 @@ use hir_def::{
GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate, GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
WherePredicateTypeTarget, WherePredicateTypeTarget,
}, },
hir::BindingAnnotation,
lang_item::LangItem, lang_item::LangItem,
resolver::{HasResolver, TypeNs}, resolver::{HasResolver, TypeNs},
type_ref::{TraitBoundModifier, TypeRef}, type_ref::{TraitBoundModifier, TypeRef},
@ -24,7 +25,8 @@ use rustc_hash::FxHashSet;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use crate::{ 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( pub(crate) fn fn_traits(
@ -352,3 +354,20 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
_ => false, _ => 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, PrivateField,
ReplaceFilterMapNextWithFindMap, ReplaceFilterMapNextWithFindMap,
TypeMismatch, TypeMismatch,
UndeclaredLabel,
UnimplementedBuiltinMacro, UnimplementedBuiltinMacro,
UnreachableLabel,
UnresolvedExternCrate, UnresolvedExternCrate,
UnresolvedField, UnresolvedField,
UnresolvedImport, UnresolvedImport,
@ -61,6 +63,13 @@ diagnostics![
UnusedMut, UnusedMut,
]; ];
#[derive(Debug)]
pub struct BreakOutsideOfLoop {
pub expr: InFile<AstPtr<ast::Expr>>,
pub is_break: bool,
pub bad_value_break: bool,
}
#[derive(Debug)] #[derive(Debug)]
pub struct UnresolvedModule { pub struct UnresolvedModule {
pub decl: InFile<AstPtr<ast::Module>>, pub decl: InFile<AstPtr<ast::Module>>,
@ -84,6 +93,17 @@ pub struct UnresolvedMacroCall {
pub path: ModPath, pub path: ModPath,
pub is_bang: bool, 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)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct InactiveCode { pub struct InactiveCode {
@ -166,13 +186,6 @@ pub struct PrivateField {
pub field: Field, 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)] #[derive(Debug)]
pub struct MissingUnsafe { pub struct MissingUnsafe {
pub expr: InFile<AstPtr<ast::Expr>>, pub expr: InFile<AstPtr<ast::Expr>>,

View File

@ -1,6 +1,6 @@
//! HirDisplay implementations for various hir types. //! HirDisplay implementations for various hir types.
use hir_def::{ use hir_def::{
adt::VariantData, data::adt::VariantData,
generics::{ generics::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
}, },
@ -8,6 +8,7 @@ use hir_def::{
type_ref::{TypeBound, TypeRef}, type_ref::{TypeBound, TypeRef},
AdtId, GenericDefId, AdtId, GenericDefId,
}; };
use hir_expand::name;
use hir_ty::{ use hir_ty::{
display::{ display::{
write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
@ -76,22 +77,22 @@ impl HirDisplay for Function {
}; };
let mut first = true; 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 { if !first {
f.write_str(", ")?; f.write_str(", ")?;
} else { } else {
first = false; first = false;
if data.has_self_param() { if local == Some(name!(self)) {
write_self_param(type_ref, f)?; write_self_param(type_ref, f)?;
continue; continue;
} }
} }
match name { match local {
Some(name) => write!(f, "{name}: ")?, Some(name) => write!(f, "{name}: ")?,
None => f.write_str("_: ")?, 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)?; type_ref.hir_fmt(f)?;
} }

View File

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

View File

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

View File

@ -14,7 +14,7 @@
//! expression, an item definition. //! expression, an item definition.
//! //!
//! Knowing only the syntax gives us relatively little info. For example, //! 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 //! `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 //! 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 //! go from [`ast::Fn`] to [`crate::Function`], and that's exactly what this
@ -88,9 +88,11 @@
use base_db::FileId; use base_db::FileId;
use hir_def::{ use hir_def::{
child_by_source::ChildBySource, child_by_source::ChildBySource,
dyn_map::DynMap, dyn_map::{
expr::{BindingId, LabelId}, keys::{self, Key},
keys::{self, Key}, DynMap,
},
hir::{BindingId, LabelId},
AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId,
GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId,
TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,

View File

@ -17,7 +17,7 @@ use hir_def::{
scope::{ExprScopes, ScopeId}, scope::{ExprScopes, ScopeId},
Body, BodySourceMap, Body, BodySourceMap,
}, },
expr::{ExprId, Pat, PatId}, hir::{ExprId, Pat, PatId},
lang_item::LangItem, lang_item::LangItem,
macro_id_to_def_id, macro_id_to_def_id,
path::{ModPath, Path, PathKind}, path::{ModPath, Path, PathKind},
@ -463,7 +463,7 @@ impl SourceAnalyzer {
db: &dyn HirDatabase, db: &dyn HirDatabase,
macro_call: InFile<&ast::MacroCall>, macro_call: InFile<&ast::MacroCall>,
) -> Option<Macro> { ) -> 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))?; let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()?).map(|it| it.into()) self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()?).map(|it| it.into())
} }

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(); 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 { if node.kind() == COMMENT {
cov_mark::hit!(extract_function_in_comment_is_not_applicable); cov_mark::hit!(extract_function_in_comment_is_not_applicable);
return None; 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 stdx::format_to;
use syntax::{ use syntax::{
ast::{self, AstNode}, 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())) .take_while(|it| ctx.selection_trimmed().contains_range(it.text_range()))
.find_map(valid_target_expr)?; .find_map(valid_target_expr)?;
if let Some(ty_info) = ctx.sema.type_of_expr(&to_extract) { let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted);
if ty_info.adjusted().is_unit() { if matches!(&ty, Some(ty_info) if ty_info.is_unit()) {
return None; 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_mutable_reference() => "&mut ",
Some(receiver_type) if receiver_type.is_reference() => "&", 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 {
let var_modifier = match parent_ref_expr { Some(ast::Expr::RefExpr(expr)) if expr.mut_token().is_some() => "mut ",
Some(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)] #[derive(Debug)]
enum Anchor { enum Anchor {
Before(SyntaxNode), Before(SyntaxNode),
@ -944,6 +932,11 @@ struct S {
vec: Vec<u8> vec: Vec<u8>
} }
struct Vec<T>;
impl<T> Vec<T> {
fn push(&mut self, _:usize) {}
}
fn foo(s: &mut S) { fn foo(s: &mut S) {
$0s.vec$0.push(0); $0s.vec$0.push(0);
}"#, }"#,
@ -952,6 +945,11 @@ struct S {
vec: Vec<u8> vec: Vec<u8>
} }
struct Vec<T>;
impl<T> Vec<T> {
fn push(&mut self, _:usize) {}
}
fn foo(s: &mut S) { fn foo(s: &mut S) {
let $0vec = &mut s.vec; let $0vec = &mut s.vec;
vec.push(0); vec.push(0);
@ -973,6 +971,10 @@ struct X {
struct S { struct S {
vec: Vec<u8> vec: Vec<u8>
} }
struct Vec<T>;
impl<T> Vec<T> {
fn push(&mut self, _:usize) {}
}
fn foo(f: &mut Y) { fn foo(f: &mut Y) {
$0f.field.field.vec$0.push(0); $0f.field.field.vec$0.push(0);
@ -987,6 +989,10 @@ struct X {
struct S { struct S {
vec: Vec<u8> vec: Vec<u8>
} }
struct Vec<T>;
impl<T> Vec<T> {
fn push(&mut self, _:usize) {}
}
fn foo(f: &mut Y) { fn foo(f: &mut Y) {
let $0vec = &mut f.field.field.vec; let $0vec = &mut f.field.field.vec;
@ -1123,7 +1129,7 @@ struct S {
} }
fn foo(s: S) { fn foo(s: S) {
let $0x = s.sub; let $0x = &s.sub;
x.do_thing(); x.do_thing();
}"#, }"#,
); );
@ -1189,7 +1195,7 @@ impl X {
fn foo() { fn foo() {
let local = &mut S::new(); let local = &mut S::new();
let $0x = &mut local.sub; let $0x = &local.sub;
x.do_thing(); x.do_thing();
}"#, }"#,
); );

View File

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

View File

@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
// } // }
// //
// struct Bar; // struct Bar;
// $0impl Foo for Bar { // $0impl Foo for Bar$0 {
// const B: u8 = 17; // const B: u8 = 17;
// fn c() {} // fn c() {}
// type A = String; // 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<()> { pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?; let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
let items = impl_ast.assoc_item_list()?; 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 assoc_items = items.assoc_items().collect::<Vec<_>>();
let path = impl_ast let path = impl_ast
@ -264,9 +274,9 @@ trait Bar {
} }
struct Foo; struct Foo;
impl Bar for Foo { $0impl Bar for Foo {
type Fooo = (); type Fooo = ();
type Foo = ();$0 type Foo = ();
}"#, }"#,
r#" r#"
trait Bar { 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>, new: Vec<T>,
target: TextRange, target: TextRange,
) -> Option<()>; ) -> Option<()>;
fn yeet() {}
} }
impl AddRewrite for Assists { impl AddRewrite for Assists {
fn yeet() {}
fn add_rewrite<T: AstNode>( fn add_rewrite<T: AstNode>(
&mut self, &mut self,
label: &str, label: &str,

View File

@ -122,6 +122,7 @@ mod handlers {
mod convert_iter_for_each_to_for; mod convert_iter_for_each_to_for;
mod convert_let_else_to_match; mod convert_let_else_to_match;
mod convert_match_to_let_else; mod convert_match_to_let_else;
mod convert_nested_function_to_closure;
mod convert_tuple_struct_to_named_struct; mod convert_tuple_struct_to_named_struct;
mod convert_named_struct_to_tuple_struct; mod convert_named_struct_to_tuple_struct;
mod convert_to_guarded_return; 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_iter_for_each_to_for,
convert_iter_for_each_to_for::convert_for_loop_with_for_each, convert_iter_for_each_to_for::convert_for_loop_with_for_each,
convert_let_else_to_match::convert_let_else_to_match, 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_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_to_guarded_return::convert_to_guarded_return,
convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct, 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, 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] #[test]
fn doctest_convert_to_guarded_return() { fn doctest_convert_to_guarded_return() {
check_doc_test( check_doc_test(
@ -2116,7 +2141,7 @@ trait Foo {
} }
struct Bar; struct Bar;
$0impl Foo for Bar { $0impl Foo for Bar$0 {
const B: u8 = 17; const B: u8 = 17;
fn c() {} fn c() {}
type A = String; type A = String;

View File

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

View File

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

View File

@ -37,9 +37,9 @@ pub(crate) fn complete_cargo_env_vars(
guard_env_macro(expanded, &ctx.sema)?; guard_env_macro(expanded, &ctx.sema)?;
let range = expanded.text_range_between_quotes()?; 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); let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
item.detail(*detail); item.detail(detail);
item.add_to(acc); item.add_to(acc);
}); });

View File

@ -262,8 +262,10 @@ fn import_on_the_fly(
.into_iter() .into_iter()
.filter(ns_filter) .filter(ns_filter)
.filter(|import| { .filter(|import| {
let original_item = &import.original_item;
!ctx.is_item_hidden(&import.item_to_import) !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| { .sorted_by_key(|located_import| {
compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased) compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
@ -302,8 +304,10 @@ fn import_on_the_fly_pat_(
.into_iter() .into_iter()
.filter(ns_filter) .filter(ns_filter)
.filter(|import| { .filter(|import| {
let original_item = &import.original_item;
!ctx.is_item_hidden(&import.item_to_import) !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| { .sorted_by_key(|located_import| {
compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased) 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, impl_def: &ast::Impl,
) { ) {
if let Some(hir_impl) = ctx.sema.to_def(impl_def) { if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| { get_missing_assoc_items(&ctx.sema, impl_def)
use self::ImplCompletionKind::*; .into_iter()
match (item, kind) { .filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db))))
(hir::AssocItem::Function(func), All | Fn) => { .for_each(|item| {
add_function_impl(acc, ctx, replacement_range, func, hir_impl) 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 { for (name, def) in module_scope {
if !ctx.check_stability(def.attrs(ctx.db).as_deref()) {
continue;
}
let is_name_already_imported = name let is_name_already_imported = name
.as_text() .as_text()
.map_or(false, |text| already_imported_names.contains(text.as_str())); .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, pub(super) krate: hir::Crate,
/// The module of the `scope`. /// The module of the `scope`.
pub(super) module: hir::Module, 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. /// The expected name of what we are completing.
/// This is usually the parameter name of the function argument 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, pub(super) depth_from_crate_root: usize,
} }
impl<'a> CompletionContext<'a> { impl CompletionContext<'_> {
/// The range of the identifier that is being completed. /// The range of the identifier that is being completed.
pub(crate) fn source_range(&self) -> TextRange { pub(crate) fn source_range(&self) -> TextRange {
let kind = self.original_token.kind(); 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. /// Whether the given trait is an operator trait or not.
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
match trait_.attrs(self.db).lang() { match trait_.attrs(self.db).lang() {
@ -632,6 +640,11 @@ impl<'a> CompletionContext<'a> {
let krate = scope.krate(); let krate = scope.krate();
let module = scope.module(); 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(); let mut locals = FxHashMap::default();
scope.process_all_names(&mut |name, scope| { scope.process_all_names(&mut |name, scope| {
if let ScopeDef::Local(local) = scope { if let ScopeDef::Local(local) = scope {
@ -651,6 +664,7 @@ impl<'a> CompletionContext<'a> {
token, token,
krate, krate,
module, module,
is_nightly,
expected_name, expected_name,
expected_type, expected_type,
qualifier_ctx, qualifier_ctx,

View File

@ -23,6 +23,7 @@ mod type_pos;
mod use_tree; mod use_tree;
mod visibility; mod visibility;
use expect_test::Expect;
use hir::PrefixKind; use hir::PrefixKind;
use ide_db::{ use ide_db::{
base_db::{fixture::ChangeFixture, FileLoader, FilePosition}, base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
@ -215,6 +216,11 @@ pub(crate) fn check_edit_with_config(
assert_eq_text!(&ra_fixture_after, &actual) 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( pub(crate) fn get_all_items(
config: CompletionConfig, config: CompletionConfig,
code: &str, code: &str,

View File

@ -1,18 +1,13 @@
//! Completion tests for expressions. //! Completion tests for expressions.
use expect_test::{expect, Expect}; 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) { fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}")); let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
expect.assert_eq(&actual) expect.assert_eq(&actual)
} }
fn check_empty(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture);
expect.assert_eq(&actual);
}
#[test] #[test]
fn complete_literal_struct_with_a_private_field() { fn complete_literal_struct_with_a_private_field() {
// `FooDesc.bar` is private, the completion should not be triggered. // `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