mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-01 19:23:50 +00:00
Merge branch 'master' into alias-based-completion2
This commit is contained in:
commit
398af0259f
9
.github/workflows/ci.yaml
vendored
9
.github/workflows/ci.yaml
vendored
@ -24,6 +24,9 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CC: deny_c
|
||||
# we want to build r-a on stable to check that it keeps building on stable,
|
||||
# but we also want to test our proc-macro-srv which depends on nightly features
|
||||
RUSTC_BOOTSTRAP: 1
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@ -50,15 +53,15 @@ jobs:
|
||||
run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml
|
||||
|
||||
- name: Compile (tests)
|
||||
run: cargo test --no-run --locked
|
||||
run: cargo test --no-run --locked --features sysroot-abi
|
||||
|
||||
# It's faster to `test` before `build` ¯\_(ツ)_/¯
|
||||
- name: Compile (rust-analyzer)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: cargo build --quiet
|
||||
run: cargo build --quiet --features sysroot-abi
|
||||
|
||||
- name: Test
|
||||
run: cargo test -- --nocapture --quiet
|
||||
run: cargo test --features sysroot-abi -- --nocapture --quiet
|
||||
|
||||
- name: Run analysis-stats on rust-analyzer
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -1250,6 +1250,7 @@ dependencies = [
|
||||
name = "proc-macro-srv-cli"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"proc-macro-api",
|
||||
"proc-macro-srv",
|
||||
]
|
||||
|
||||
@ -1457,7 +1458,7 @@ dependencies = [
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot_core 0.9.6",
|
||||
"proc-macro-api",
|
||||
"proc-macro-srv",
|
||||
"proc-macro-srv-cli",
|
||||
"profile",
|
||||
"project-model",
|
||||
"rayon",
|
||||
@ -1641,9 +1642,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "smol_str"
|
||||
version = "0.1.25"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d268d24892b932cac466d214af6ec8a3ec99873f0f8664d9a384b49596db682"
|
||||
checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -77,6 +77,7 @@ vfs = { path = "./crates/vfs", version = "0.0.0" }
|
||||
|
||||
# non-local crates
|
||||
smallvec = { version = "1.10.0", features = ["const_new", "union", "const_generics"] }
|
||||
smol_str = "0.2.0"
|
||||
# the following crates are pinned to prevent us from pulling in syn 2 until all our dependencies have moved
|
||||
serde = { version = "=1.0.156", features = ["derive"] }
|
||||
serde_json = "1.0.94"
|
||||
|
@ -4,7 +4,8 @@ use std::{mem, str::FromStr, sync::Arc};
|
||||
use cfg::CfgOptions;
|
||||
use rustc_hash::FxHashMap;
|
||||
use test_utils::{
|
||||
extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER,
|
||||
extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER,
|
||||
ESCAPED_CURSOR_MARKER,
|
||||
};
|
||||
use tt::token_id::{Leaf, Subtree, TokenTree};
|
||||
use vfs::{file_set::FileSet, VfsPath};
|
||||
@ -12,7 +13,7 @@ use vfs::{file_set::FileSet, VfsPath};
|
||||
use crate::{
|
||||
input::{CrateName, CrateOrigin, LangCrateOrigin},
|
||||
Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition,
|
||||
FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros,
|
||||
FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, ReleaseChannel,
|
||||
SourceDatabaseExt, SourceRoot, SourceRootId,
|
||||
};
|
||||
|
||||
@ -102,7 +103,14 @@ impl ChangeFixture {
|
||||
ra_fixture: &str,
|
||||
mut proc_macro_defs: Vec<(String, ProcMacro)>,
|
||||
) -> ChangeFixture {
|
||||
let (mini_core, proc_macro_names, fixture) = Fixture::parse(ra_fixture);
|
||||
let FixtureWithProjectMeta { fixture, mini_core, proc_macro_names, toolchain } =
|
||||
FixtureWithProjectMeta::parse(ra_fixture);
|
||||
let toolchain = toolchain
|
||||
.map(|it| {
|
||||
ReleaseChannel::from_str(&it)
|
||||
.unwrap_or_else(|| panic!("unknown release channel found: {it}"))
|
||||
})
|
||||
.unwrap_or(ReleaseChannel::Stable);
|
||||
let mut change = Change::new();
|
||||
|
||||
let mut files = Vec::new();
|
||||
@ -166,7 +174,7 @@ impl ChangeFixture {
|
||||
.as_deref()
|
||||
.map(Arc::from)
|
||||
.ok_or_else(|| "target_data_layout unset".into()),
|
||||
None,
|
||||
Some(toolchain),
|
||||
);
|
||||
let prev = crates.insert(crate_name.clone(), crate_id);
|
||||
assert!(prev.is_none());
|
||||
@ -205,7 +213,7 @@ impl ChangeFixture {
|
||||
default_target_data_layout
|
||||
.map(|x| x.into())
|
||||
.ok_or_else(|| "target_data_layout unset".into()),
|
||||
None,
|
||||
Some(toolchain),
|
||||
);
|
||||
} else {
|
||||
for (from, to, prelude) in crate_deps {
|
||||
@ -247,7 +255,7 @@ impl ChangeFixture {
|
||||
false,
|
||||
CrateOrigin::Lang(LangCrateOrigin::Core),
|
||||
target_layout.clone(),
|
||||
None,
|
||||
Some(toolchain),
|
||||
);
|
||||
|
||||
for krate in all_crates {
|
||||
@ -286,7 +294,7 @@ impl ChangeFixture {
|
||||
true,
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
target_layout,
|
||||
None,
|
||||
Some(toolchain),
|
||||
);
|
||||
proc_macros.insert(proc_macros_crate, Ok(proc_macro));
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||
|
||||
pub mod builtin;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@ -267,6 +269,10 @@ impl Attrs {
|
||||
pub fn is_proc_macro_derive(&self) -> bool {
|
||||
self.by_key("proc_macro_derive").exists()
|
||||
}
|
||||
|
||||
pub fn is_unstable(&self) -> bool {
|
||||
self.by_key("unstable").exists()
|
||||
}
|
||||
}
|
||||
|
||||
use std::slice::Iter as SliceIter;
|
||||
|
@ -13,10 +13,12 @@ use cfg::{CfgExpr, CfgOptions};
|
||||
use drop_bomb::DropBomb;
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
attrs::RawAttrs, hygiene::Hygiene, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId,
|
||||
ast_id_map::AstIdMap, attrs::RawAttrs, hygiene::Hygiene, name::Name, AstId, ExpandError,
|
||||
ExpandResult, HirFileId, InFile, MacroCallId,
|
||||
};
|
||||
use la_arena::{Arena, ArenaMap};
|
||||
use limit::Limit;
|
||||
use once_cell::unsync::OnceCell;
|
||||
use profile::Count;
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
|
||||
@ -24,7 +26,7 @@ use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
expr::{
|
||||
hir::{
|
||||
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
|
||||
},
|
||||
item_scope::BuiltinShadowMode,
|
||||
@ -36,7 +38,43 @@ use crate::{
|
||||
UnresolvedMacro,
|
||||
};
|
||||
|
||||
pub use lower::LowerCtx;
|
||||
pub struct LowerCtx<'a> {
|
||||
pub db: &'a dyn DefDatabase,
|
||||
hygiene: Hygiene,
|
||||
ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
|
||||
}
|
||||
|
||||
impl<'a> LowerCtx<'a> {
|
||||
pub fn new(db: &'a dyn DefDatabase, hygiene: &Hygiene, file_id: HirFileId) -> Self {
|
||||
LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: Some((file_id, OnceCell::new())) }
|
||||
}
|
||||
|
||||
pub fn with_file_id(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
|
||||
LowerCtx {
|
||||
db,
|
||||
hygiene: Hygiene::new(db.upcast(), file_id),
|
||||
ast_id_map: Some((file_id, OnceCell::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
|
||||
LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
|
||||
}
|
||||
|
||||
pub(crate) fn hygiene(&self) -> &Hygiene {
|
||||
&self.hygiene
|
||||
}
|
||||
|
||||
pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
|
||||
Path::from_src(ast, self)
|
||||
}
|
||||
|
||||
pub(crate) fn ast_id<N: syntax::AstNode>(&self, item: &N) -> Option<AstId<N>> {
|
||||
let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
|
||||
let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
|
||||
Some(InFile::new(file_id, ast_id_map.ast_id(item)))
|
||||
}
|
||||
}
|
||||
|
||||
/// A subset of Expander that only deals with cfg attributes. We only need it to
|
||||
/// avoid cyclic queries in crate def map during enum processing.
|
||||
@ -76,6 +114,10 @@ impl CfgExpander {
|
||||
let attrs = self.parse_attrs(db, owner);
|
||||
attrs.is_cfg_enabled(&self.cfg_options)
|
||||
}
|
||||
|
||||
pub(crate) fn hygiene(&self) -> &Hygiene {
|
||||
&self.hygiene
|
||||
}
|
||||
}
|
||||
|
||||
impl Expander {
|
||||
@ -180,6 +222,10 @@ impl Expander {
|
||||
mark.bomb.defuse();
|
||||
}
|
||||
|
||||
pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> {
|
||||
LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id)
|
||||
}
|
||||
|
||||
pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
|
||||
InFile { file_id: self.current_file_id, value }
|
||||
}
|
||||
@ -232,7 +278,7 @@ impl Expander {
|
||||
// The overflow error should have been reported when it occurred (see the next branch),
|
||||
// so don't return overflow error here to avoid diagnostics duplication.
|
||||
cov_mark::hit!(overflow_but_not_me);
|
||||
return ExpandResult::only_err(ExpandError::RecursionOverflowPosioned);
|
||||
return ExpandResult::only_err(ExpandError::RecursionOverflowPoisoned);
|
||||
} else if self.recursion_limit(db).check(self.recursion_depth + 1).is_err() {
|
||||
self.recursion_depth = usize::MAX;
|
||||
cov_mark::hit!(your_stack_belongs_to_me);
|
||||
@ -343,6 +389,8 @@ pub enum BodyDiagnostic {
|
||||
MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
|
||||
UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId },
|
||||
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
|
||||
UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
|
||||
UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
|
||||
}
|
||||
|
||||
impl Body {
|
||||
|
@ -7,77 +7,38 @@ use base_db::CrateId;
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
ast_id_map::AstIdMap,
|
||||
hygiene::Hygiene,
|
||||
name::{name, AsName, Name},
|
||||
AstId, ExpandError, HirFileId, InFile,
|
||||
AstId, ExpandError, InFile,
|
||||
};
|
||||
use intern::Interned;
|
||||
use la_arena::Arena;
|
||||
use once_cell::unsync::OnceCell;
|
||||
use profile::Count;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use syntax::{
|
||||
ast::{
|
||||
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName, LiteralKind,
|
||||
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName,
|
||||
SlicePatComponents,
|
||||
},
|
||||
AstNode, AstPtr, SyntaxNodePtr,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
adt::StructKind,
|
||||
body::{Body, BodySourceMap, Expander, ExprPtr, LabelPtr, LabelSource, PatPtr},
|
||||
body::{BodyDiagnostic, ExprSource, PatSource},
|
||||
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
|
||||
body::{Body, BodyDiagnostic, BodySourceMap, Expander, ExprPtr, LabelPtr, LowerCtx, PatPtr},
|
||||
data::adt::StructKind,
|
||||
db::DefDatabase,
|
||||
expr::{
|
||||
dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId,
|
||||
FloatTypeWrapper, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId,
|
||||
RecordFieldPat, RecordLitField, Statement,
|
||||
hir::{
|
||||
dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Expr,
|
||||
ExprId, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat,
|
||||
RecordLitField, Statement,
|
||||
},
|
||||
item_scope::BuiltinShadowMode,
|
||||
item_tree::ItemTree,
|
||||
lang_item::LangItem,
|
||||
path::{GenericArgs, Path},
|
||||
type_ref::{Mutability, Rawness, TypeRef},
|
||||
AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
|
||||
};
|
||||
|
||||
pub struct LowerCtx<'a> {
|
||||
pub db: &'a dyn DefDatabase,
|
||||
hygiene: Hygiene,
|
||||
ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
|
||||
}
|
||||
|
||||
impl<'a> LowerCtx<'a> {
|
||||
pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
|
||||
LowerCtx {
|
||||
db,
|
||||
hygiene: Hygiene::new(db.upcast(), file_id),
|
||||
ast_id_map: Some((file_id, OnceCell::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
|
||||
LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
|
||||
}
|
||||
|
||||
pub(crate) fn hygiene(&self) -> &Hygiene {
|
||||
&self.hygiene
|
||||
}
|
||||
|
||||
pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
|
||||
Path::from_src(ast, self)
|
||||
}
|
||||
|
||||
pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<AstId<N>> {
|
||||
let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
|
||||
let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
|
||||
Some(InFile::new(file_id, ast_id_map.ast_id(item)))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower(
|
||||
db: &dyn DefDatabase,
|
||||
expander: Expander,
|
||||
@ -102,9 +63,11 @@ pub(super) fn lower(
|
||||
_c: Count::new(),
|
||||
},
|
||||
expander,
|
||||
current_try_block: None,
|
||||
current_try_block_label: None,
|
||||
is_lowering_assignee_expr: false,
|
||||
is_lowering_generator: false,
|
||||
label_ribs: Vec::new(),
|
||||
current_binding_owner: None,
|
||||
}
|
||||
.collect(params, body, is_async_fn)
|
||||
}
|
||||
@ -113,12 +76,57 @@ struct ExprCollector<'a> {
|
||||
db: &'a dyn DefDatabase,
|
||||
expander: Expander,
|
||||
ast_id_map: Arc<AstIdMap>,
|
||||
body: Body,
|
||||
krate: CrateId,
|
||||
body: Body,
|
||||
source_map: BodySourceMap,
|
||||
current_try_block: Option<LabelId>,
|
||||
|
||||
is_lowering_assignee_expr: bool,
|
||||
is_lowering_generator: bool,
|
||||
|
||||
current_try_block_label: Option<LabelId>,
|
||||
// points to the expression that a try expression will target (replaces current_try_block_label)
|
||||
// catch_scope: Option<ExprId>,
|
||||
// points to the expression that an unlabeled control flow will target
|
||||
// loop_scope: Option<ExprId>,
|
||||
// needed to diagnose non label control flow in while conditions
|
||||
// is_in_loop_condition: bool,
|
||||
|
||||
// resolution
|
||||
label_ribs: Vec<LabelRib>,
|
||||
current_binding_owner: Option<ExprId>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct LabelRib {
|
||||
kind: RibKind,
|
||||
// Once we handle macro hygiene this will need to be a map
|
||||
label: Option<(Name, LabelId)>,
|
||||
}
|
||||
|
||||
impl LabelRib {
|
||||
fn new(kind: RibKind) -> Self {
|
||||
LabelRib { kind, label: None }
|
||||
}
|
||||
fn new_normal(label: (Name, LabelId)) -> Self {
|
||||
LabelRib { kind: RibKind::Normal, label: Some(label) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum RibKind {
|
||||
Normal,
|
||||
Closure,
|
||||
Constant,
|
||||
}
|
||||
|
||||
impl RibKind {
|
||||
/// This rib forbids referring to labels defined in upwards ribs.
|
||||
fn is_label_barrier(self) -> bool {
|
||||
match self {
|
||||
RibKind::Normal => false,
|
||||
RibKind::Closure | RibKind::Constant => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -162,97 +170,35 @@ impl ExprCollector<'_> {
|
||||
self.body.params.push(param_pat);
|
||||
}
|
||||
|
||||
for pat in param_list
|
||||
.params()
|
||||
.zip(attr_enabled)
|
||||
.filter_map(|(param, enabled)| param.pat().filter(|_| enabled))
|
||||
for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
|
||||
{
|
||||
let param_pat = self.collect_pat(pat);
|
||||
let param_pat = self.collect_pat_top(param.pat());
|
||||
self.body.params.push(param_pat);
|
||||
}
|
||||
};
|
||||
self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
|
||||
if is_async_fn {
|
||||
match body {
|
||||
Some(e) => {
|
||||
let expr = this.collect_expr(e);
|
||||
this.alloc_expr_desugared(Expr::Async {
|
||||
id: None,
|
||||
statements: Box::new([]),
|
||||
tail: Some(expr),
|
||||
})
|
||||
}
|
||||
None => this.missing_expr(),
|
||||
}
|
||||
} else {
|
||||
this.collect_expr_opt(body)
|
||||
}
|
||||
});
|
||||
|
||||
self.body.body_expr = if is_async_fn {
|
||||
self.current_try_block =
|
||||
Some(self.alloc_label_desugared(Label { name: Name::generate_new_name() }));
|
||||
let expr = self.collect_expr_opt(body);
|
||||
let expr = self.alloc_expr_desugared(Expr::Block {
|
||||
id: None,
|
||||
statements: Box::new([]),
|
||||
tail: Some(expr),
|
||||
label: self.current_try_block,
|
||||
});
|
||||
let expr = self.alloc_expr_desugared(Expr::Async {
|
||||
id: None,
|
||||
statements: Box::new([]),
|
||||
tail: Some(expr),
|
||||
});
|
||||
expr
|
||||
} else {
|
||||
self.collect_expr_opt(body)
|
||||
};
|
||||
(self.body, self.source_map)
|
||||
}
|
||||
|
||||
fn ctx(&self) -> LowerCtx<'_> {
|
||||
LowerCtx::new(self.db, self.expander.current_file_id)
|
||||
}
|
||||
|
||||
fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
|
||||
let src = self.expander.to_source(ptr);
|
||||
let id = self.make_expr(expr, src.clone());
|
||||
self.source_map.expr_map.insert(src, id);
|
||||
id
|
||||
}
|
||||
// FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
|
||||
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
|
||||
self.body.exprs.alloc(expr)
|
||||
}
|
||||
fn missing_expr(&mut self) -> ExprId {
|
||||
self.alloc_expr_desugared(Expr::Missing)
|
||||
}
|
||||
fn make_expr(&mut self, expr: Expr, src: ExprSource) -> ExprId {
|
||||
let id = self.body.exprs.alloc(expr);
|
||||
self.source_map.expr_map_back.insert(id, src);
|
||||
id
|
||||
}
|
||||
|
||||
fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
|
||||
self.body.bindings.alloc(Binding { name, mode, definitions: SmallVec::new() })
|
||||
}
|
||||
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
|
||||
let src = self.expander.to_source(ptr);
|
||||
let id = self.make_pat(pat, src.clone());
|
||||
self.source_map.pat_map.insert(src, id);
|
||||
id
|
||||
}
|
||||
// FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
|
||||
fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
|
||||
self.body.pats.alloc(pat)
|
||||
}
|
||||
fn missing_pat(&mut self) -> PatId {
|
||||
self.body.pats.alloc(Pat::Missing)
|
||||
}
|
||||
fn make_pat(&mut self, pat: Pat, src: PatSource) -> PatId {
|
||||
let id = self.body.pats.alloc(pat);
|
||||
self.source_map.pat_map_back.insert(id, src);
|
||||
id
|
||||
}
|
||||
|
||||
fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
|
||||
let src = self.expander.to_source(ptr);
|
||||
let id = self.make_label(label, src.clone());
|
||||
self.source_map.label_map.insert(src, id);
|
||||
id
|
||||
}
|
||||
// FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
|
||||
fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
|
||||
self.body.labels.alloc(label)
|
||||
}
|
||||
fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId {
|
||||
let id = self.body.labels.alloc(label);
|
||||
self.source_map.label_map_back.insert(id, src);
|
||||
id
|
||||
self.expander.ctx(self.db)
|
||||
}
|
||||
|
||||
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
|
||||
@ -264,6 +210,7 @@ impl ExprCollector<'_> {
|
||||
let syntax_ptr = AstPtr::new(&expr);
|
||||
self.check_cfg(&expr)?;
|
||||
|
||||
// FIXME: Move some of these arms out into separate methods for clarity
|
||||
Some(match expr {
|
||||
ast::Expr::IfExpr(e) => {
|
||||
let then_branch = self.collect_block_opt(e.then_branch());
|
||||
@ -281,12 +228,12 @@ impl ExprCollector<'_> {
|
||||
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::LetExpr(e) => {
|
||||
let pat = self.collect_pat_opt(e.pat());
|
||||
let pat = self.collect_pat_top(e.pat());
|
||||
let expr = self.collect_expr_opt(e.expr());
|
||||
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::BlockExpr(e) => match e.modifier() {
|
||||
Some(ast::BlockModifier::Try(_)) => self.collect_try_block(e),
|
||||
Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e),
|
||||
Some(ast::BlockModifier::Unsafe(_)) => {
|
||||
self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
|
||||
id,
|
||||
@ -296,28 +243,48 @@ impl ExprCollector<'_> {
|
||||
}
|
||||
Some(ast::BlockModifier::Label(label)) => {
|
||||
let label = self.collect_label(label);
|
||||
self.collect_block_(e, |id, statements, tail| Expr::Block {
|
||||
id,
|
||||
statements,
|
||||
tail,
|
||||
label: Some(label),
|
||||
self.with_labeled_rib(label, |this| {
|
||||
this.collect_block_(e, |id, statements, tail| Expr::Block {
|
||||
id,
|
||||
statements,
|
||||
tail,
|
||||
label: Some(label),
|
||||
})
|
||||
})
|
||||
}
|
||||
Some(ast::BlockModifier::Async(_)) => {
|
||||
self.with_label_rib(RibKind::Closure, |this| {
|
||||
this.collect_block_(e, |id, statements, tail| Expr::Async {
|
||||
id,
|
||||
statements,
|
||||
tail,
|
||||
})
|
||||
})
|
||||
}
|
||||
Some(ast::BlockModifier::Const(_)) => {
|
||||
self.with_label_rib(RibKind::Constant, |this| {
|
||||
this.collect_as_a_binding_owner_bad(
|
||||
|this| {
|
||||
this.collect_block_(e, |id, statements, tail| Expr::Const {
|
||||
id,
|
||||
statements,
|
||||
tail,
|
||||
})
|
||||
},
|
||||
syntax_ptr,
|
||||
)
|
||||
})
|
||||
}
|
||||
Some(ast::BlockModifier::Async(_)) => self
|
||||
.collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }),
|
||||
Some(ast::BlockModifier::Const(_)) => self
|
||||
.collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }),
|
||||
None => self.collect_block(e),
|
||||
},
|
||||
ast::Expr::LoopExpr(e) => {
|
||||
let label = e.label().map(|label| self.collect_label(label));
|
||||
let body = self.collect_block_opt(e.loop_body());
|
||||
let body = self.collect_labelled_block_opt(label, e.loop_body());
|
||||
self.alloc_expr(Expr::Loop { body, label }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::WhileExpr(e) => {
|
||||
let label = e.label().map(|label| self.collect_label(label));
|
||||
let body = self.collect_block_opt(e.loop_body());
|
||||
|
||||
let body = self.collect_labelled_block_opt(label, e.loop_body());
|
||||
let condition = self.collect_expr_opt(e.condition());
|
||||
|
||||
self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
|
||||
@ -325,8 +292,8 @@ impl ExprCollector<'_> {
|
||||
ast::Expr::ForExpr(e) => {
|
||||
let label = e.label().map(|label| self.collect_label(label));
|
||||
let iterable = self.collect_expr_opt(e.iterable());
|
||||
let pat = self.collect_pat_opt(e.pat());
|
||||
let body = self.collect_block_opt(e.loop_body());
|
||||
let pat = self.collect_pat_top(e.pat());
|
||||
let body = self.collect_labelled_block_opt(label, e.loop_body());
|
||||
self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::CallExpr(e) => {
|
||||
@ -365,7 +332,7 @@ impl ExprCollector<'_> {
|
||||
.arms()
|
||||
.filter_map(|arm| {
|
||||
self.check_cfg(&arm).map(|()| MatchArm {
|
||||
pat: self.collect_pat_opt(arm.pat()),
|
||||
pat: self.collect_pat_top(arm.pat()),
|
||||
expr: self.collect_expr_opt(arm.expr()),
|
||||
guard: arm
|
||||
.guard()
|
||||
@ -386,16 +353,20 @@ impl ExprCollector<'_> {
|
||||
.unwrap_or(Expr::Missing);
|
||||
self.alloc_expr(path, syntax_ptr)
|
||||
}
|
||||
ast::Expr::ContinueExpr(e) => self.alloc_expr(
|
||||
Expr::Continue { label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
|
||||
syntax_ptr,
|
||||
),
|
||||
ast::Expr::ContinueExpr(e) => {
|
||||
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
|
||||
self.source_map.diagnostics.push(e);
|
||||
None
|
||||
});
|
||||
self.alloc_expr(Expr::Continue { label }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::BreakExpr(e) => {
|
||||
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
|
||||
self.source_map.diagnostics.push(e);
|
||||
None
|
||||
});
|
||||
let expr = e.expr().map(|e| self.collect_expr(e));
|
||||
self.alloc_expr(
|
||||
Expr::Break { expr, label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
|
||||
syntax_ptr,
|
||||
)
|
||||
self.alloc_expr(Expr::Break { expr, label }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::ParenExpr(e) => {
|
||||
let inner = self.collect_expr_opt(e.expr());
|
||||
@ -496,14 +467,16 @@ impl ExprCollector<'_> {
|
||||
None => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
}
|
||||
}
|
||||
ast::Expr::ClosureExpr(e) => {
|
||||
ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
|
||||
let (result_expr_id, prev_binding_owner) =
|
||||
this.initialize_binding_owner(syntax_ptr);
|
||||
let mut args = Vec::new();
|
||||
let mut arg_types = Vec::new();
|
||||
if let Some(pl) = e.param_list() {
|
||||
for param in pl.params() {
|
||||
let pat = self.collect_pat_opt(param.pat());
|
||||
let pat = this.collect_pat_top(param.pat());
|
||||
let type_ref =
|
||||
param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
|
||||
param.ty().map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
|
||||
args.push(pat);
|
||||
arg_types.push(type_ref);
|
||||
}
|
||||
@ -511,14 +484,13 @@ impl ExprCollector<'_> {
|
||||
let ret_type = e
|
||||
.ret_type()
|
||||
.and_then(|r| r.ty())
|
||||
.map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
|
||||
.map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
|
||||
|
||||
let prev_is_lowering_generator = self.is_lowering_generator;
|
||||
self.is_lowering_generator = false;
|
||||
let prev_is_lowering_generator = mem::take(&mut this.is_lowering_generator);
|
||||
|
||||
let body = self.collect_expr_opt(e.body());
|
||||
let body = this.collect_expr_opt(e.body());
|
||||
|
||||
let closure_kind = if self.is_lowering_generator {
|
||||
let closure_kind = if this.is_lowering_generator {
|
||||
let movability = if e.static_token().is_some() {
|
||||
Movability::Static
|
||||
} else {
|
||||
@ -530,19 +502,21 @@ impl ExprCollector<'_> {
|
||||
} else {
|
||||
ClosureKind::Closure
|
||||
};
|
||||
self.is_lowering_generator = prev_is_lowering_generator;
|
||||
|
||||
self.alloc_expr(
|
||||
Expr::Closure {
|
||||
args: args.into(),
|
||||
arg_types: arg_types.into(),
|
||||
ret_type,
|
||||
body,
|
||||
closure_kind,
|
||||
},
|
||||
syntax_ptr,
|
||||
)
|
||||
}
|
||||
this.is_lowering_generator = prev_is_lowering_generator;
|
||||
let capture_by =
|
||||
if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
|
||||
this.is_lowering_generator = prev_is_lowering_generator;
|
||||
this.current_binding_owner = prev_binding_owner;
|
||||
this.body.exprs[result_expr_id] = Expr::Closure {
|
||||
args: args.into(),
|
||||
arg_types: arg_types.into(),
|
||||
ret_type,
|
||||
body,
|
||||
closure_kind,
|
||||
capture_by,
|
||||
};
|
||||
result_expr_id
|
||||
}),
|
||||
ast::Expr::BinExpr(e) => {
|
||||
let op = e.op_kind();
|
||||
if let Some(ast::BinaryOp::Assignment { op: None }) = op {
|
||||
@ -581,7 +555,17 @@ impl ExprCollector<'_> {
|
||||
}
|
||||
ArrayExprKind::Repeat { initializer, repeat } => {
|
||||
let initializer = self.collect_expr_opt(initializer);
|
||||
let repeat = self.collect_expr_opt(repeat);
|
||||
let repeat = self.with_label_rib(RibKind::Constant, |this| {
|
||||
if let Some(repeat) = repeat {
|
||||
let syntax_ptr = AstPtr::new(&repeat);
|
||||
this.collect_as_a_binding_owner_bad(
|
||||
|this| this.collect_expr(repeat),
|
||||
syntax_ptr,
|
||||
)
|
||||
} else {
|
||||
this.missing_expr()
|
||||
}
|
||||
});
|
||||
self.alloc_expr(
|
||||
Expr::Array(Array::Repeat { initializer, repeat }),
|
||||
syntax_ptr,
|
||||
@ -627,23 +611,53 @@ impl ExprCollector<'_> {
|
||||
})
|
||||
}
|
||||
|
||||
fn initialize_binding_owner(
|
||||
&mut self,
|
||||
syntax_ptr: AstPtr<ast::Expr>,
|
||||
) -> (ExprId, Option<ExprId>) {
|
||||
let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
|
||||
let prev_binding_owner = self.current_binding_owner.take();
|
||||
self.current_binding_owner = Some(result_expr_id);
|
||||
(result_expr_id, prev_binding_owner)
|
||||
}
|
||||
|
||||
/// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently
|
||||
/// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have
|
||||
/// their own body. Don't add more usage for this function so that we can remove this function after
|
||||
/// separating those bodies.
|
||||
fn collect_as_a_binding_owner_bad(
|
||||
&mut self,
|
||||
job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId,
|
||||
syntax_ptr: AstPtr<ast::Expr>,
|
||||
) -> ExprId {
|
||||
let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
|
||||
let tmp = job(self);
|
||||
self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing);
|
||||
self.current_binding_owner = prev_owner;
|
||||
id
|
||||
}
|
||||
|
||||
/// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
|
||||
/// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
|
||||
/// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
|
||||
fn collect_try_block(&mut self, e: BlockExpr) -> ExprId {
|
||||
fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
|
||||
let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else {
|
||||
return self.alloc_expr_desugared(Expr::Missing);
|
||||
return self.collect_block(e);
|
||||
};
|
||||
let prev_try_block = self.current_try_block.take();
|
||||
self.current_try_block =
|
||||
Some(self.alloc_label_desugared(Label { name: Name::generate_new_name() }));
|
||||
let expr_id = self.collect_block(e);
|
||||
let label = self.alloc_label_desugared(Label { name: Name::generate_new_name() });
|
||||
let old_label = self.current_try_block_label.replace(label);
|
||||
|
||||
let (btail, expr_id) = self.with_labeled_rib(label, |this| {
|
||||
let mut btail = None;
|
||||
let block = this.collect_block_(e, |id, statements, tail| {
|
||||
btail = tail;
|
||||
Expr::Block { id, statements, tail, label: Some(label) }
|
||||
});
|
||||
(btail, block)
|
||||
});
|
||||
|
||||
let callee = self.alloc_expr_desugared(Expr::Path(try_from_output));
|
||||
let Expr::Block { label, tail, .. } = &mut self.body.exprs[expr_id] else {
|
||||
unreachable!("It is the output of collect block");
|
||||
};
|
||||
*label = self.current_try_block;
|
||||
let next_tail = match *tail {
|
||||
let next_tail = match btail {
|
||||
Some(tail) => self.alloc_expr_desugared(Expr::Call {
|
||||
callee,
|
||||
args: Box::new([tail]),
|
||||
@ -662,10 +676,10 @@ impl ExprCollector<'_> {
|
||||
}
|
||||
};
|
||||
let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
|
||||
unreachable!("It is the output of collect block");
|
||||
unreachable!("block was lowered to non-block");
|
||||
};
|
||||
*tail = Some(next_tail);
|
||||
self.current_try_block = prev_try_block;
|
||||
self.current_try_block_label = old_label;
|
||||
expr_id
|
||||
}
|
||||
|
||||
@ -735,12 +749,13 @@ impl ExprCollector<'_> {
|
||||
Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
|
||||
syntax_ptr.clone(),
|
||||
);
|
||||
if let Some(label) = self.current_try_block {
|
||||
let label = Some(self.body.labels[label].name.clone());
|
||||
self.alloc_expr(Expr::Break { expr: Some(result), label }, syntax_ptr.clone())
|
||||
} else {
|
||||
self.alloc_expr(Expr::Return { expr: Some(result) }, syntax_ptr.clone())
|
||||
}
|
||||
self.alloc_expr(
|
||||
match self.current_try_block_label {
|
||||
Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
|
||||
None => Expr::Return { expr: Some(result) },
|
||||
},
|
||||
syntax_ptr.clone(),
|
||||
)
|
||||
},
|
||||
};
|
||||
let arms = Box::new([continue_arm, break_arm]);
|
||||
@ -785,7 +800,7 @@ impl ExprCollector<'_> {
|
||||
krate: *krate,
|
||||
});
|
||||
}
|
||||
Some(ExpandError::RecursionOverflowPosioned) => {
|
||||
Some(ExpandError::RecursionOverflowPoisoned) => {
|
||||
// Recursion limit has been reached in the macro expansion tree, but not in
|
||||
// this very macro call. Don't add diagnostics to avoid duplication.
|
||||
}
|
||||
@ -866,7 +881,7 @@ impl ExprCollector<'_> {
|
||||
if self.check_cfg(&stmt).is_none() {
|
||||
return;
|
||||
}
|
||||
let pat = self.collect_pat_opt(stmt.pat());
|
||||
let pat = self.collect_pat_top(stmt.pat());
|
||||
let type_ref =
|
||||
stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
|
||||
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
|
||||
@ -911,10 +926,20 @@ impl ExprCollector<'_> {
|
||||
block: ast::BlockExpr,
|
||||
mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
|
||||
) -> ExprId {
|
||||
let file_local_id = self.ast_id_map.ast_id(&block);
|
||||
let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
|
||||
let block_has_items = {
|
||||
let statement_has_item = block.statements().any(|stmt| match stmt {
|
||||
ast::Stmt::Item(_) => true,
|
||||
// Macro calls can be both items and expressions. The syntax library always treats
|
||||
// them as expressions here, so we undo that.
|
||||
ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))),
|
||||
_ => false,
|
||||
});
|
||||
statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
|
||||
};
|
||||
|
||||
let block_id = if ItemTree::block_has_items(self.db, ast_id.file_id, &block) {
|
||||
let block_id = if block_has_items {
|
||||
let file_local_id = self.ast_id_map.ast_id(&block);
|
||||
let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
|
||||
Some(self.db.intern_block(BlockLoc {
|
||||
ast_id,
|
||||
module: self.expander.def_map.module_id(self.expander.module),
|
||||
@ -966,32 +991,34 @@ impl ExprCollector<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
|
||||
let label = Label {
|
||||
name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
|
||||
};
|
||||
self.alloc_label(label, AstPtr::new(&ast_label))
|
||||
fn collect_labelled_block_opt(
|
||||
&mut self,
|
||||
label: Option<LabelId>,
|
||||
expr: Option<ast::BlockExpr>,
|
||||
) -> ExprId {
|
||||
match label {
|
||||
Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)),
|
||||
None => self.collect_block_opt(expr),
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
|
||||
self.collect_pat_(pat, &mut BindingList::default())
|
||||
}
|
||||
// region: patterns
|
||||
|
||||
fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
|
||||
fn collect_pat_top(&mut self, pat: Option<ast::Pat>) -> PatId {
|
||||
match pat {
|
||||
Some(pat) => self.collect_pat(pat),
|
||||
Some(pat) => self.collect_pat(pat, &mut BindingList::default()),
|
||||
None => self.missing_pat(),
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
|
||||
fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
|
||||
let pattern = match &pat {
|
||||
ast::Pat::IdentPat(bp) => {
|
||||
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
|
||||
|
||||
let annotation =
|
||||
BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
|
||||
let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat, binding_list));
|
||||
let subpat = bp.pat().map(|subpat| self.collect_pat(subpat, binding_list));
|
||||
|
||||
let is_simple_ident_pat =
|
||||
annotation == BindingAnnotation::Unannotated && subpat.is_none();
|
||||
@ -1045,7 +1072,7 @@ impl ExprCollector<'_> {
|
||||
Pat::TupleStruct { path, args, ellipsis }
|
||||
}
|
||||
ast::Pat::RefPat(p) => {
|
||||
let pat = self.collect_pat_opt_(p.pat(), binding_list);
|
||||
let pat = self.collect_pat_opt(p.pat(), binding_list);
|
||||
let mutability = Mutability::from_mutable(p.mut_token().is_some());
|
||||
Pat::Ref { pat, mutability }
|
||||
}
|
||||
@ -1055,10 +1082,10 @@ impl ExprCollector<'_> {
|
||||
path.map(Pat::Path).unwrap_or(Pat::Missing)
|
||||
}
|
||||
ast::Pat::OrPat(p) => {
|
||||
let pats = p.pats().map(|p| self.collect_pat_(p, binding_list)).collect();
|
||||
let pats = p.pats().map(|p| self.collect_pat(p, binding_list)).collect();
|
||||
Pat::Or(pats)
|
||||
}
|
||||
ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat(), binding_list),
|
||||
ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
|
||||
ast::Pat::TuplePat(p) => {
|
||||
let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
|
||||
Pat::Tuple { args, ellipsis }
|
||||
@ -1073,7 +1100,7 @@ impl ExprCollector<'_> {
|
||||
.fields()
|
||||
.filter_map(|f| {
|
||||
let ast_pat = f.pat()?;
|
||||
let pat = self.collect_pat_(ast_pat, binding_list);
|
||||
let pat = self.collect_pat(ast_pat, binding_list);
|
||||
let name = f.field_name()?.as_name();
|
||||
Some(RecordFieldPat { name, pat })
|
||||
})
|
||||
@ -1092,15 +1119,9 @@ impl ExprCollector<'_> {
|
||||
|
||||
// FIXME properly handle `RestPat`
|
||||
Pat::Slice {
|
||||
prefix: prefix
|
||||
.into_iter()
|
||||
.map(|p| self.collect_pat_(p, binding_list))
|
||||
.collect(),
|
||||
slice: slice.map(|p| self.collect_pat_(p, binding_list)),
|
||||
suffix: suffix
|
||||
.into_iter()
|
||||
.map(|p| self.collect_pat_(p, binding_list))
|
||||
.collect(),
|
||||
prefix: prefix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
|
||||
slice: slice.map(|p| self.collect_pat(p, binding_list)),
|
||||
suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
|
||||
}
|
||||
}
|
||||
#[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676
|
||||
@ -1131,12 +1152,18 @@ impl ExprCollector<'_> {
|
||||
Pat::Missing
|
||||
}
|
||||
ast::Pat::BoxPat(boxpat) => {
|
||||
let inner = self.collect_pat_opt_(boxpat.pat(), binding_list);
|
||||
let inner = self.collect_pat_opt(boxpat.pat(), binding_list);
|
||||
Pat::Box { inner }
|
||||
}
|
||||
ast::Pat::ConstBlockPat(const_block_pat) => {
|
||||
if let Some(expr) = const_block_pat.block_expr() {
|
||||
let expr_id = self.collect_block(expr);
|
||||
if let Some(block) = const_block_pat.block_expr() {
|
||||
let expr_id = self.with_label_rib(RibKind::Constant, |this| {
|
||||
let syntax_ptr = AstPtr::new(&block.clone().into());
|
||||
this.collect_as_a_binding_owner_bad(
|
||||
|this| this.collect_block(block),
|
||||
syntax_ptr,
|
||||
)
|
||||
});
|
||||
Pat::ConstBlock(expr_id)
|
||||
} else {
|
||||
Pat::Missing
|
||||
@ -1148,7 +1175,7 @@ impl ExprCollector<'_> {
|
||||
let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
|
||||
let pat =
|
||||
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
|
||||
this.collect_pat_opt_(expanded_pat, binding_list)
|
||||
this.collect_pat_opt(expanded_pat, binding_list)
|
||||
});
|
||||
self.source_map.pat_map.insert(src, pat);
|
||||
return pat;
|
||||
@ -1162,9 +1189,9 @@ impl ExprCollector<'_> {
|
||||
self.alloc_pat(pattern, Either::Left(ptr))
|
||||
}
|
||||
|
||||
fn collect_pat_opt_(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
|
||||
fn collect_pat_opt(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
|
||||
match pat {
|
||||
Some(pat) => self.collect_pat_(pat, binding_list),
|
||||
Some(pat) => self.collect_pat(pat, binding_list),
|
||||
None => self.missing_pat(),
|
||||
}
|
||||
}
|
||||
@ -1180,12 +1207,14 @@ impl ExprCollector<'_> {
|
||||
// We want to skip the `..` pattern here, since we account for it above.
|
||||
let args = args
|
||||
.filter(|p| !matches!(p, ast::Pat::RestPat(_)))
|
||||
.map(|p| self.collect_pat_(p, binding_list))
|
||||
.map(|p| self.collect_pat(p, binding_list))
|
||||
.collect();
|
||||
|
||||
(args, ellipsis)
|
||||
}
|
||||
|
||||
// endregion: patterns
|
||||
|
||||
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
|
||||
/// not.
|
||||
fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> {
|
||||
@ -1213,42 +1242,118 @@ impl ExprCollector<'_> {
|
||||
fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
|
||||
self.body.bindings[binding_id].definitions.push(pat_id);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ast::LiteralKind> for Literal {
|
||||
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
|
||||
match ast_lit_kind {
|
||||
// FIXME: these should have actual values filled in, but unsure on perf impact
|
||||
LiteralKind::IntNumber(lit) => {
|
||||
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
|
||||
Literal::Float(
|
||||
FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
|
||||
builtin,
|
||||
)
|
||||
} else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
|
||||
Literal::Uint(lit.value().unwrap_or(0), builtin)
|
||||
} else {
|
||||
let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
|
||||
Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
|
||||
// region: labels
|
||||
|
||||
fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
|
||||
let label = Label {
|
||||
name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
|
||||
};
|
||||
self.alloc_label(label, AstPtr::new(&ast_label))
|
||||
}
|
||||
|
||||
fn resolve_label(
|
||||
&self,
|
||||
lifetime: Option<ast::Lifetime>,
|
||||
) -> Result<Option<LabelId>, BodyDiagnostic> {
|
||||
let Some(lifetime) = lifetime else {
|
||||
return Ok(None)
|
||||
};
|
||||
let name = Name::new_lifetime(&lifetime);
|
||||
|
||||
for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
|
||||
if let Some((label_name, id)) = &rib.label {
|
||||
if *label_name == name {
|
||||
return if self.is_label_valid_from_rib(rib_idx) {
|
||||
Ok(Some(*id))
|
||||
} else {
|
||||
Err(BodyDiagnostic::UnreachableLabel {
|
||||
name,
|
||||
node: InFile::new(
|
||||
self.expander.current_file_id,
|
||||
AstPtr::new(&lifetime),
|
||||
),
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
LiteralKind::FloatNumber(lit) => {
|
||||
let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
|
||||
Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
|
||||
}
|
||||
LiteralKind::ByteString(bs) => {
|
||||
let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
|
||||
Literal::ByteString(text)
|
||||
}
|
||||
LiteralKind::String(s) => {
|
||||
let text = s.value().map(Box::from).unwrap_or_else(Default::default);
|
||||
Literal::String(text)
|
||||
}
|
||||
LiteralKind::Byte(b) => {
|
||||
Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
|
||||
}
|
||||
LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
|
||||
LiteralKind::Bool(val) => Literal::Bool(val),
|
||||
}
|
||||
|
||||
Err(BodyDiagnostic::UndeclaredLabel {
|
||||
name,
|
||||
node: InFile::new(self.expander.current_file_id, AstPtr::new(&lifetime)),
|
||||
})
|
||||
}
|
||||
|
||||
fn is_label_valid_from_rib(&self, rib_index: usize) -> bool {
|
||||
!self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
|
||||
}
|
||||
|
||||
fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
|
||||
self.label_ribs.push(LabelRib::new(kind));
|
||||
let res = f(self);
|
||||
self.label_ribs.pop();
|
||||
res
|
||||
}
|
||||
|
||||
fn with_labeled_rib<T>(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T {
|
||||
self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label)));
|
||||
let res = f(self);
|
||||
self.label_ribs.pop();
|
||||
res
|
||||
}
|
||||
// endregion: labels
|
||||
}
|
||||
|
||||
impl ExprCollector<'_> {
|
||||
fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
|
||||
let src = self.expander.to_source(ptr);
|
||||
let id = self.body.exprs.alloc(expr);
|
||||
self.source_map.expr_map_back.insert(id, src.clone());
|
||||
self.source_map.expr_map.insert(src, id);
|
||||
id
|
||||
}
|
||||
// FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
|
||||
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
|
||||
self.body.exprs.alloc(expr)
|
||||
}
|
||||
fn missing_expr(&mut self) -> ExprId {
|
||||
self.alloc_expr_desugared(Expr::Missing)
|
||||
}
|
||||
|
||||
fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
|
||||
self.body.bindings.alloc(Binding {
|
||||
name,
|
||||
mode,
|
||||
definitions: SmallVec::new(),
|
||||
owner: self.current_binding_owner,
|
||||
})
|
||||
}
|
||||
|
||||
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
|
||||
let src = self.expander.to_source(ptr);
|
||||
let id = self.body.pats.alloc(pat);
|
||||
self.source_map.pat_map_back.insert(id, src.clone());
|
||||
self.source_map.pat_map.insert(src, id);
|
||||
id
|
||||
}
|
||||
// FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
|
||||
fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
|
||||
self.body.pats.alloc(pat)
|
||||
}
|
||||
fn missing_pat(&mut self) -> PatId {
|
||||
self.body.pats.alloc(Pat::Missing)
|
||||
}
|
||||
|
||||
fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
|
||||
let src = self.expander.to_source(ptr);
|
||||
let id = self.body.labels.alloc(label);
|
||||
self.source_map.label_map_back.insert(id, src.clone());
|
||||
self.source_map.label_map.insert(src, id);
|
||||
id
|
||||
}
|
||||
// FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
|
||||
fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
|
||||
self.body.labels.alloc(label)
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,9 @@ use std::fmt::{self, Write};
|
||||
use syntax::ast::HasName;
|
||||
|
||||
use crate::{
|
||||
expr::{Array, BindingAnnotation, BindingId, ClosureKind, Literal, Movability, Statement},
|
||||
hir::{
|
||||
Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, Movability, Statement,
|
||||
},
|
||||
pretty::{print_generic_args, print_path, print_type_ref},
|
||||
type_ref::TypeRef,
|
||||
};
|
||||
@ -13,20 +15,16 @@ use crate::{
|
||||
use super::*;
|
||||
|
||||
pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
|
||||
let needs_semi;
|
||||
let header = match owner {
|
||||
DefWithBodyId::FunctionId(it) => {
|
||||
needs_semi = false;
|
||||
let item_tree_id = it.lookup(db).id;
|
||||
format!("fn {}(…) ", item_tree_id.item_tree(db)[item_tree_id.value].name)
|
||||
format!("fn {}", item_tree_id.item_tree(db)[item_tree_id.value].name)
|
||||
}
|
||||
DefWithBodyId::StaticId(it) => {
|
||||
needs_semi = true;
|
||||
let item_tree_id = it.lookup(db).id;
|
||||
format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name)
|
||||
}
|
||||
DefWithBodyId::ConstId(it) => {
|
||||
needs_semi = true;
|
||||
let item_tree_id = it.lookup(db).id;
|
||||
let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name {
|
||||
Some(name) => name.to_string(),
|
||||
@ -35,7 +33,6 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
|
||||
format!("const {name} = ")
|
||||
}
|
||||
DefWithBodyId::VariantId(it) => {
|
||||
needs_semi = false;
|
||||
let src = it.parent.child_source(db);
|
||||
let variant = &src.value[it.local_id];
|
||||
let name = match &variant.name() {
|
||||
@ -47,8 +44,18 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
|
||||
};
|
||||
|
||||
let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };
|
||||
if let DefWithBodyId::FunctionId(it) = owner {
|
||||
p.buf.push('(');
|
||||
body.params.iter().zip(&db.function_data(it).params).for_each(|(¶m, ty)| {
|
||||
p.print_pat(param);
|
||||
p.buf.push(':');
|
||||
p.print_type_ref(ty);
|
||||
});
|
||||
p.buf.push(')');
|
||||
p.buf.push(' ');
|
||||
}
|
||||
p.print_expr(body.body_expr);
|
||||
if needs_semi {
|
||||
if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) {
|
||||
p.buf.push(';');
|
||||
}
|
||||
p.buf
|
||||
@ -219,14 +226,14 @@ impl<'a> Printer<'a> {
|
||||
}
|
||||
Expr::Continue { label } => {
|
||||
w!(self, "continue");
|
||||
if let Some(label) = label {
|
||||
w!(self, " {}", label);
|
||||
if let Some(lbl) = label {
|
||||
w!(self, " {}", self.body[*lbl].name);
|
||||
}
|
||||
}
|
||||
Expr::Break { expr, label } => {
|
||||
w!(self, "break");
|
||||
if let Some(label) = label {
|
||||
w!(self, " {}", label);
|
||||
if let Some(lbl) = label {
|
||||
w!(self, " {}", self.body[*lbl].name);
|
||||
}
|
||||
if let Some(expr) = expr {
|
||||
self.whitespace();
|
||||
@ -355,7 +362,7 @@ impl<'a> Printer<'a> {
|
||||
self.print_expr(*index);
|
||||
w!(self, "]");
|
||||
}
|
||||
Expr::Closure { args, arg_types, ret_type, body, closure_kind } => {
|
||||
Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => {
|
||||
match closure_kind {
|
||||
ClosureKind::Generator(Movability::Static) => {
|
||||
w!(self, "static ");
|
||||
@ -365,6 +372,12 @@ impl<'a> Printer<'a> {
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
match capture_by {
|
||||
CaptureBy::Value => {
|
||||
w!(self, "move ");
|
||||
}
|
||||
CaptureBy::Ref => (),
|
||||
}
|
||||
w!(self, "|");
|
||||
for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
|
||||
if i != 0 {
|
||||
|
@ -8,7 +8,7 @@ use rustc_hash::FxHashMap;
|
||||
use crate::{
|
||||
body::Body,
|
||||
db::DefDatabase,
|
||||
expr::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
|
||||
hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
|
||||
BlockId, DefWithBodyId,
|
||||
};
|
||||
|
||||
|
@ -10,9 +10,8 @@ use syntax::ast::HasDocComments;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
dyn_map::DynMap,
|
||||
dyn_map::{keys, DynMap},
|
||||
item_scope::ItemScope,
|
||||
keys,
|
||||
src::{HasChildSource, HasSource},
|
||||
AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId,
|
||||
ModuleDefId, ModuleId, TraitId, VariantId,
|
||||
|
@ -1,5 +1,7 @@
|
||||
//! Contains basic data about various HIR declarations.
|
||||
|
||||
pub mod adt;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
|
||||
@ -28,7 +30,7 @@ use crate::{
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FunctionData {
|
||||
pub name: Name,
|
||||
pub params: Vec<(Option<Name>, Interned<TypeRef>)>,
|
||||
pub params: Vec<Interned<TypeRef>>,
|
||||
pub ret_type: Interned<TypeRef>,
|
||||
pub attrs: Attrs,
|
||||
pub visibility: RawVisibility,
|
||||
@ -98,7 +100,7 @@ impl FunctionData {
|
||||
params: enabled_params
|
||||
.clone()
|
||||
.filter_map(|id| match &item_tree[id] {
|
||||
Param::Normal(name, ty) => Some((name.clone(), ty.clone())),
|
||||
Param::Normal(ty) => Some(ty.clone()),
|
||||
Param::Varargs => None,
|
||||
})
|
||||
.collect(),
|
||||
|
@ -473,7 +473,7 @@ fn lower_struct(
|
||||
trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
|
||||
ast: &InFile<ast::StructKind>,
|
||||
) -> StructKind {
|
||||
let ctx = LowerCtx::new(db, ast.file_id);
|
||||
let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id);
|
||||
|
||||
match &ast.value {
|
||||
ast::StructKind::Tuple(fl) => {
|
@ -9,10 +9,10 @@ use la_arena::ArenaMap;
|
||||
use syntax::{ast, AstPtr};
|
||||
|
||||
use crate::{
|
||||
adt::{EnumData, StructData},
|
||||
attr::{Attrs, AttrsWithOwner},
|
||||
body::{scope::ExprScopes, Body, BodySourceMap},
|
||||
data::{
|
||||
adt::{EnumData, StructData},
|
||||
ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData,
|
||||
TraitAliasData, TraitData, TypeAliasData,
|
||||
},
|
||||
|
@ -21,6 +21,8 @@
|
||||
//!
|
||||
//! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
|
||||
//! a coincidence.
|
||||
pub mod keys;
|
||||
|
||||
use std::{
|
||||
hash::Hash,
|
||||
marker::PhantomData,
|
||||
|
@ -12,7 +12,6 @@ use hir_expand::{
|
||||
use intern::Interned;
|
||||
use la_arena::{Arena, ArenaMap, Idx};
|
||||
use once_cell::unsync::Lazy;
|
||||
use std::ops::DerefMut;
|
||||
use stdx::impl_from;
|
||||
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
|
||||
|
||||
@ -20,8 +19,7 @@ use crate::{
|
||||
body::{Expander, LowerCtx},
|
||||
child_by_source::ChildBySource,
|
||||
db::DefDatabase,
|
||||
dyn_map::DynMap,
|
||||
keys,
|
||||
dyn_map::{keys, DynMap},
|
||||
src::{HasChildSource, HasSource},
|
||||
type_ref::{LifetimeRef, TypeBound, TypeRef},
|
||||
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
|
||||
@ -177,7 +175,7 @@ impl GenericParams {
|
||||
// Don't create an `Expander` nor call `loc.source(db)` if not needed since this
|
||||
// causes a reparse after the `ItemTree` has been created.
|
||||
let mut expander = Lazy::new(|| Expander::new(db, loc.source(db).file_id, module));
|
||||
for (_, param) in &func_data.params {
|
||||
for param in &func_data.params {
|
||||
generic_params.fill_implicit_impl_trait_args(db, &mut expander, param);
|
||||
}
|
||||
|
||||
@ -329,7 +327,7 @@ impl GenericParams {
|
||||
pub(crate) fn fill_implicit_impl_trait_args(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
expander: &mut impl DerefMut<Target = Expander>,
|
||||
expander: &mut Expander,
|
||||
type_ref: &TypeRef,
|
||||
) {
|
||||
type_ref.walk(&mut |type_ref| {
|
||||
@ -351,7 +349,7 @@ impl GenericParams {
|
||||
let macro_call = mc.to_node(db.upcast());
|
||||
match expander.enter_expand::<ast::Type>(db, macro_call) {
|
||||
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
|
||||
let ctx = LowerCtx::new(db, expander.current_file_id());
|
||||
let ctx = expander.ctx(db);
|
||||
let type_ref = TypeRef::from_ast(&ctx, expanded);
|
||||
self.fill_implicit_impl_trait_args(db, expander, &type_ref);
|
||||
expander.exit(db, mark);
|
||||
|
@ -12,12 +12,15 @@
|
||||
//!
|
||||
//! See also a neighboring `body` module.
|
||||
|
||||
pub mod type_ref;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use intern::Interned;
|
||||
use la_arena::{Idx, RawIdx};
|
||||
use smallvec::SmallVec;
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
|
||||
@ -28,10 +31,10 @@ use crate::{
|
||||
|
||||
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
|
||||
|
||||
pub type ExprId = Idx<Expr>;
|
||||
|
||||
pub type BindingId = Idx<Binding>;
|
||||
|
||||
pub type ExprId = Idx<Expr>;
|
||||
|
||||
/// FIXME: this is a hacky function which should be removed
|
||||
pub(crate) fn dummy_expr_id() -> ExprId {
|
||||
ExprId::from_raw(RawIdx::from(u32::MAX))
|
||||
@ -102,6 +105,45 @@ impl Literal {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ast::LiteralKind> for Literal {
|
||||
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
|
||||
use ast::LiteralKind;
|
||||
match ast_lit_kind {
|
||||
// FIXME: these should have actual values filled in, but unsure on perf impact
|
||||
LiteralKind::IntNumber(lit) => {
|
||||
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
|
||||
Literal::Float(
|
||||
FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
|
||||
builtin,
|
||||
)
|
||||
} else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
|
||||
Literal::Uint(lit.value().unwrap_or(0), builtin)
|
||||
} else {
|
||||
let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
|
||||
Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
|
||||
}
|
||||
}
|
||||
LiteralKind::FloatNumber(lit) => {
|
||||
let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
|
||||
Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
|
||||
}
|
||||
LiteralKind::ByteString(bs) => {
|
||||
let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
|
||||
Literal::ByteString(text)
|
||||
}
|
||||
LiteralKind::String(s) => {
|
||||
let text = s.value().map(Box::from).unwrap_or_else(Default::default);
|
||||
Literal::String(text)
|
||||
}
|
||||
LiteralKind::Byte(b) => {
|
||||
Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
|
||||
}
|
||||
LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
|
||||
LiteralKind::Bool(val) => Literal::Bool(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Expr {
|
||||
/// This is produced if the syntax tree does not have a required expression piece.
|
||||
@ -168,11 +210,11 @@ pub enum Expr {
|
||||
arms: Box<[MatchArm]>,
|
||||
},
|
||||
Continue {
|
||||
label: Option<Name>,
|
||||
label: Option<LabelId>,
|
||||
},
|
||||
Break {
|
||||
expr: Option<ExprId>,
|
||||
label: Option<Name>,
|
||||
label: Option<LabelId>,
|
||||
},
|
||||
Return {
|
||||
expr: Option<ExprId>,
|
||||
@ -233,6 +275,7 @@ pub enum Expr {
|
||||
ret_type: Option<Interned<TypeRef>>,
|
||||
body: ExprId,
|
||||
closure_kind: ClosureKind,
|
||||
capture_by: CaptureBy,
|
||||
},
|
||||
Tuple {
|
||||
exprs: Box<[ExprId]>,
|
||||
@ -250,6 +293,14 @@ pub enum ClosureKind {
|
||||
Async,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CaptureBy {
|
||||
/// `move |x| y + x`.
|
||||
Value,
|
||||
/// `move` keyword was not specified.
|
||||
Ref,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Movability {
|
||||
Static,
|
||||
@ -442,6 +493,22 @@ pub struct Binding {
|
||||
pub name: Name,
|
||||
pub mode: BindingAnnotation,
|
||||
pub definitions: SmallVec<[PatId; 1]>,
|
||||
/// Id of the closure/generator that owns this binding. If it is owned by the
|
||||
/// top level expression, this field would be `None`.
|
||||
pub owner: Option<ExprId>,
|
||||
}
|
||||
|
||||
impl Binding {
|
||||
pub fn is_upvar(&self, relative_to: ExprId) -> bool {
|
||||
match self.owner {
|
||||
Some(x) => {
|
||||
// We assign expression ids in a way that outer closures will recieve
|
||||
// a lower id
|
||||
x.into_raw() < relative_to.into_raw()
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
@ -13,7 +13,7 @@ use syntax::ast::{self, HasName};
|
||||
use crate::{
|
||||
body::LowerCtx,
|
||||
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||
expr::Literal,
|
||||
hir::Literal,
|
||||
path::Path,
|
||||
};
|
||||
|
@ -152,14 +152,6 @@ impl ItemTree {
|
||||
&self.top_level
|
||||
}
|
||||
|
||||
pub fn block_has_items(
|
||||
db: &dyn DefDatabase,
|
||||
file_id: HirFileId,
|
||||
block: &ast::BlockExpr,
|
||||
) -> bool {
|
||||
lower::Ctx::new(db, file_id).block_has_items(block)
|
||||
}
|
||||
|
||||
/// Returns the inner attributes of the source file.
|
||||
pub fn top_level_attrs(&self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
|
||||
Attrs::filter(
|
||||
@ -614,7 +606,7 @@ pub struct Function {
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Param {
|
||||
Normal(Option<Name>, Interned<TypeRef>),
|
||||
Normal(Interned<TypeRef>),
|
||||
Varargs,
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ impl<'a> Ctx<'a> {
|
||||
db,
|
||||
tree: ItemTree::default(),
|
||||
source_ast_id_map: db.ast_id_map(file),
|
||||
body_ctx: crate::body::LowerCtx::new(db, file),
|
||||
body_ctx: crate::body::LowerCtx::with_file_id(db, file),
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,34 +101,6 @@ impl<'a> Ctx<'a> {
|
||||
self.tree
|
||||
}
|
||||
|
||||
pub(super) fn block_has_items(mut self, block: &ast::BlockExpr) -> bool {
|
||||
let statement_has_item = block
|
||||
.statements()
|
||||
.find_map(|stmt| match stmt {
|
||||
ast::Stmt::Item(item) => self.lower_mod_item(&item),
|
||||
// Macro calls can be both items and expressions. The syntax library always treats
|
||||
// them as expressions here, so we undo that.
|
||||
ast::Stmt::ExprStmt(es) => match es.expr()? {
|
||||
ast::Expr::MacroExpr(expr) => self.lower_mod_item(&expr.macro_call()?.into()),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.is_some();
|
||||
if statement_has_item {
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Some(ast::Expr::MacroExpr(expr)) = block.tail_expr() {
|
||||
if let Some(call) = expr.macro_call() {
|
||||
if let Some(_) = self.lower_mod_item(&call.into()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn data(&mut self) -> &mut ItemTreeData {
|
||||
self.tree.data_mut()
|
||||
}
|
||||
@ -321,7 +293,7 @@ impl<'a> Ctx<'a> {
|
||||
}
|
||||
};
|
||||
let ty = Interned::new(self_type);
|
||||
let idx = self.data().params.alloc(Param::Normal(None, ty));
|
||||
let idx = self.data().params.alloc(Param::Normal(ty));
|
||||
self.add_attrs(
|
||||
idx.into(),
|
||||
RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()),
|
||||
@ -334,19 +306,7 @@ impl<'a> Ctx<'a> {
|
||||
None => {
|
||||
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
|
||||
let ty = Interned::new(type_ref);
|
||||
let mut pat = param.pat();
|
||||
// FIXME: This really shouldn't be here, in fact FunctionData/ItemTree's function shouldn't know about
|
||||
// pattern names at all
|
||||
let name = 'name: loop {
|
||||
match pat {
|
||||
Some(ast::Pat::RefPat(ref_pat)) => pat = ref_pat.pat(),
|
||||
Some(ast::Pat::IdentPat(ident)) => {
|
||||
break 'name ident.name().map(|it| it.as_name())
|
||||
}
|
||||
_ => break 'name None,
|
||||
}
|
||||
};
|
||||
self.data().params.alloc(Param::Normal(name, ty))
|
||||
self.data().params.alloc(Param::Normal(ty))
|
||||
}
|
||||
};
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), ¶m, self.hygiene()));
|
||||
|
@ -257,21 +257,15 @@ impl<'a> Printer<'a> {
|
||||
w!(self, "(");
|
||||
if !params.is_empty() {
|
||||
self.indented(|this| {
|
||||
for (i, param) in params.clone().enumerate() {
|
||||
for param in params.clone() {
|
||||
this.print_attrs_of(param);
|
||||
match &this.tree[param] {
|
||||
Param::Normal(name, ty) => {
|
||||
match name {
|
||||
Some(name) => w!(this, "{}: ", name),
|
||||
None => w!(this, "_: "),
|
||||
Param::Normal(ty) => {
|
||||
if flags.contains(FnFlags::HAS_SELF_PARAM) {
|
||||
w!(this, "self: ");
|
||||
}
|
||||
this.print_type_ref(ty);
|
||||
w!(this, ",");
|
||||
if flags.contains(FnFlags::HAS_SELF_PARAM) && i == 0 {
|
||||
wln!(this, " // self");
|
||||
} else {
|
||||
wln!(this);
|
||||
}
|
||||
wln!(this, ",");
|
||||
}
|
||||
Param::Varargs => {
|
||||
wln!(this, "...");
|
||||
|
@ -165,7 +165,7 @@ trait Tr: SuperTrait + 'lifetime {
|
||||
fn method(&self);
|
||||
}
|
||||
"#,
|
||||
expect![[r##"
|
||||
expect![[r#"
|
||||
pub static mut ST: () = _;
|
||||
|
||||
pub(self) const _: Anon = _;
|
||||
@ -174,8 +174,8 @@ trait Tr: SuperTrait + 'lifetime {
|
||||
#[inner_attr_in_fn]
|
||||
pub(self) fn f(
|
||||
#[attr]
|
||||
arg: u8,
|
||||
_: (),
|
||||
u8,
|
||||
(),
|
||||
) -> () { ... }
|
||||
|
||||
pub(self) trait Tr<Self>
|
||||
@ -186,10 +186,10 @@ trait Tr: SuperTrait + 'lifetime {
|
||||
pub(self) type Assoc: AssocBound = Default;
|
||||
|
||||
pub(self) fn method(
|
||||
_: &Self, // self
|
||||
self: &Self,
|
||||
) -> ();
|
||||
}
|
||||
"##]],
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
@ -336,7 +336,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
|
||||
T: 'b
|
||||
{
|
||||
pub(self) fn f<G>(
|
||||
arg: impl Copy,
|
||||
impl Copy,
|
||||
) -> impl Copy
|
||||
where
|
||||
G: 'a { ... }
|
||||
|
@ -92,6 +92,7 @@ pub enum LayoutError {
|
||||
SizeOverflow,
|
||||
TargetLayoutNotAvailable,
|
||||
HasPlaceholder,
|
||||
HasErrorType,
|
||||
NotImplemented,
|
||||
Unknown,
|
||||
}
|
||||
|
@ -18,24 +18,21 @@ pub mod db;
|
||||
|
||||
pub mod attr;
|
||||
pub mod path;
|
||||
pub mod type_ref;
|
||||
pub mod builtin_type;
|
||||
pub mod builtin_attr;
|
||||
pub mod per_ns;
|
||||
pub mod item_scope;
|
||||
|
||||
pub mod dyn_map;
|
||||
pub mod keys;
|
||||
|
||||
pub mod item_tree;
|
||||
|
||||
pub mod adt;
|
||||
pub mod data;
|
||||
pub mod generics;
|
||||
pub mod lang_item;
|
||||
pub mod layout;
|
||||
|
||||
pub mod expr;
|
||||
pub mod hir;
|
||||
pub use self::hir::type_ref;
|
||||
pub mod body;
|
||||
pub mod resolver;
|
||||
|
||||
@ -82,8 +79,8 @@ use syntax::ast;
|
||||
use ::tt::token_id as tt;
|
||||
|
||||
use crate::{
|
||||
adt::VariantData,
|
||||
builtin_type::BuiltinType,
|
||||
data::adt::VariantData,
|
||||
item_tree::{
|
||||
Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
|
||||
Static, Struct, Trait, TraitAlias, TypeAlias, Union,
|
||||
@ -236,7 +233,7 @@ pub struct EnumVariantId {
|
||||
pub local_id: LocalEnumVariantId,
|
||||
}
|
||||
|
||||
pub type LocalEnumVariantId = Idx<adt::EnumVariantData>;
|
||||
pub type LocalEnumVariantId = Idx<data::adt::EnumVariantData>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct FieldId {
|
||||
@ -244,7 +241,7 @@ pub struct FieldId {
|
||||
pub local_id: LocalFieldId,
|
||||
}
|
||||
|
||||
pub type LocalFieldId = Idx<adt::FieldData>;
|
||||
pub type LocalFieldId = Idx<data::adt::FieldData>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ConstId(salsa::InternId);
|
||||
|
@ -16,7 +16,7 @@ struct Foo;
|
||||
#[derive(Copy)]
|
||||
struct Foo;
|
||||
|
||||
impl < > core::marker::Copy for Foo< > {}"#]],
|
||||
impl < > core::marker::Copy for Foo< > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ macro Copy {}
|
||||
#[derive(Copy)]
|
||||
struct Foo;
|
||||
|
||||
impl < > crate ::marker::Copy for Foo< > {}"#]],
|
||||
impl < > crate ::marker::Copy for Foo< > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ struct Foo<A, B>;
|
||||
#[derive(Copy)]
|
||||
struct Foo<A, B>;
|
||||
|
||||
impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
|
||||
impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ struct Foo<A, B, 'a, 'b>;
|
||||
#[derive(Copy)]
|
||||
struct Foo<A, B, 'a, 'b>;
|
||||
|
||||
impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
|
||||
impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ struct Foo<A, B>;
|
||||
#[derive(Clone)]
|
||||
struct Foo<A, B>;
|
||||
|
||||
impl <T0: core::clone::Clone, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
|
||||
impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Foo<A, B, > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
@ -106,6 +106,6 @@ struct Foo<const X: usize, T>(u32);
|
||||
#[derive(Clone)]
|
||||
struct Foo<const X: usize, T>(u32);
|
||||
|
||||
impl <const T0: usize, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
|
||||
impl <const X: usize, T: core::clone::Clone, > core::clone::Clone for Foo<X, T, > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ use hir_expand::{attrs::Attr, MacroCallId};
|
||||
use syntax::{ast, SmolStr};
|
||||
|
||||
use crate::{
|
||||
attr_macro_as_call_id, builtin_attr,
|
||||
attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
|
||||
attr_macro_as_call_id,
|
||||
db::DefDatabase,
|
||||
item_scope::BuiltinShadowMode,
|
||||
macro_id_to_def_id,
|
||||
@ -76,19 +77,15 @@ impl DefMap {
|
||||
let pred = |n: &_| *n == name;
|
||||
|
||||
let registered = self.registered_tools.iter().map(SmolStr::as_str);
|
||||
let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
|
||||
let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred);
|
||||
// FIXME: tool modules can be shadowed by actual modules
|
||||
if is_tool {
|
||||
return true;
|
||||
}
|
||||
|
||||
if segments.len() == 1 {
|
||||
let registered = self.registered_attrs.iter().map(SmolStr::as_str);
|
||||
let is_inert = builtin_attr::INERT_ATTRIBUTES
|
||||
.iter()
|
||||
.map(|it| it.name)
|
||||
.chain(registered)
|
||||
.any(pred);
|
||||
let mut registered = self.registered_attrs.iter().map(SmolStr::as_str);
|
||||
let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred);
|
||||
return is_inert;
|
||||
}
|
||||
}
|
||||
|
@ -331,11 +331,11 @@ impl DefMap {
|
||||
Some(local_id) => {
|
||||
let variant = EnumVariantId { parent: e, local_id };
|
||||
match &*enum_data.variants[local_id].variant_data {
|
||||
crate::adt::VariantData::Record(_) => {
|
||||
crate::data::adt::VariantData::Record(_) => {
|
||||
PerNs::types(variant.into(), Visibility::Public)
|
||||
}
|
||||
crate::adt::VariantData::Tuple(_)
|
||||
| crate::adt::VariantData::Unit => {
|
||||
crate::data::adt::VariantData::Tuple(_)
|
||||
| crate::data::adt::VariantData::Unit => {
|
||||
PerNs::both(variant.into(), variant.into(), Visibility::Public)
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ use crate::{
|
||||
body::scope::{ExprScopes, ScopeId},
|
||||
builtin_type::BuiltinType,
|
||||
db::DefDatabase,
|
||||
expr::{BindingId, ExprId, LabelId},
|
||||
generics::{GenericParams, TypeOrConstParamData},
|
||||
hir::{BindingId, ExprId, LabelId},
|
||||
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
|
||||
lang_item::LangItemTarget,
|
||||
nameres::DefMap,
|
||||
|
@ -1,11 +1,12 @@
|
||||
//! Builtin derives.
|
||||
|
||||
use base_db::{CrateOrigin, LangCrateOrigin};
|
||||
use std::collections::HashSet;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::tt::{self, TokenId};
|
||||
use syntax::{
|
||||
ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
|
||||
ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName, HasTypeBounds, PathType},
|
||||
match_ast,
|
||||
};
|
||||
|
||||
@ -60,8 +61,11 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
|
||||
|
||||
struct BasicAdtInfo {
|
||||
name: tt::Ident,
|
||||
/// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
|
||||
param_types: Vec<Option<tt::Subtree>>,
|
||||
/// first field is the name, and
|
||||
/// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
|
||||
/// third fields is where bounds, if any
|
||||
param_types: Vec<(tt::Subtree, Option<tt::Subtree>, Option<tt::Subtree>)>,
|
||||
associated_types: Vec<tt::Subtree>,
|
||||
}
|
||||
|
||||
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
||||
@ -86,18 +90,28 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
||||
},
|
||||
}
|
||||
};
|
||||
let name = name.ok_or_else(|| {
|
||||
debug!("parsed item has no name");
|
||||
ExpandError::Other("missing name".into())
|
||||
})?;
|
||||
let name_token_id =
|
||||
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
|
||||
let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
|
||||
let mut param_type_set: HashSet<String> = HashSet::new();
|
||||
let param_types = params
|
||||
.into_iter()
|
||||
.flat_map(|param_list| param_list.type_or_const_params())
|
||||
.map(|param| {
|
||||
if let ast::TypeOrConstParam::Const(param) = param {
|
||||
let name = {
|
||||
let this = param.name();
|
||||
match this {
|
||||
Some(x) => {
|
||||
param_type_set.insert(x.to_string());
|
||||
mbe::syntax_node_to_token_tree(x.syntax()).0
|
||||
}
|
||||
None => tt::Subtree::empty(),
|
||||
}
|
||||
};
|
||||
let bounds = match ¶m {
|
||||
ast::TypeOrConstParam::Type(x) => {
|
||||
x.type_bound_list().map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
|
||||
}
|
||||
ast::TypeOrConstParam::Const(_) => None,
|
||||
};
|
||||
let ty = if let ast::TypeOrConstParam::Const(param) = param {
|
||||
let ty = param
|
||||
.ty()
|
||||
.map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0)
|
||||
@ -105,27 +119,97 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
||||
Some(ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
(name, ty, bounds)
|
||||
})
|
||||
.collect();
|
||||
Ok(BasicAdtInfo { name: name_token, param_types })
|
||||
let is_associated_type = |p: &PathType| {
|
||||
if let Some(p) = p.path() {
|
||||
if let Some(parent) = p.qualifier() {
|
||||
if let Some(x) = parent.segment() {
|
||||
if let Some(x) = x.path_type() {
|
||||
if let Some(x) = x.path() {
|
||||
if let Some(pname) = x.as_single_name_ref() {
|
||||
if param_type_set.contains(&pname.to_string()) {
|
||||
// <T as Trait>::Assoc
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(pname) = parent.as_single_name_ref() {
|
||||
if param_type_set.contains(&pname.to_string()) {
|
||||
// T::Assoc
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
};
|
||||
let associated_types = node
|
||||
.descendants()
|
||||
.filter_map(PathType::cast)
|
||||
.filter(is_associated_type)
|
||||
.map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
|
||||
.collect::<Vec<_>>();
|
||||
let name = name.ok_or_else(|| {
|
||||
debug!("parsed item has no name");
|
||||
ExpandError::Other("missing name".into())
|
||||
})?;
|
||||
let name_token_id =
|
||||
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
|
||||
let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
|
||||
Ok(BasicAdtInfo { name: name_token, param_types, associated_types })
|
||||
}
|
||||
|
||||
/// Given that we are deriving a trait `DerivedTrait` for a type like:
|
||||
///
|
||||
/// ```ignore (only-for-syntax-highlight)
|
||||
/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
|
||||
/// a: A,
|
||||
/// b: B::Item,
|
||||
/// b1: <B as DeclaredTrait>::Item,
|
||||
/// c1: <C as WhereTrait>::Item,
|
||||
/// c2: Option<<C as WhereTrait>::Item>,
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// create an impl like:
|
||||
///
|
||||
/// ```ignore (only-for-syntax-highlight)
|
||||
/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where
|
||||
/// C: WhereTrait,
|
||||
/// A: DerivedTrait + B1 + ... + BN,
|
||||
/// B: DerivedTrait + B1 + ... + BN,
|
||||
/// C: DerivedTrait + B1 + ... + BN,
|
||||
/// B::Item: DerivedTrait + B1 + ... + BN,
|
||||
/// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
|
||||
/// ...
|
||||
/// {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and
|
||||
/// therefore does not get bound by the derived trait.
|
||||
fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
|
||||
let info = match parse_adt(tt) {
|
||||
Ok(info) => info,
|
||||
Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e),
|
||||
};
|
||||
let mut where_block = vec![];
|
||||
let (params, args): (Vec<_>, Vec<_>) = info
|
||||
.param_types
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(idx, param_ty)| {
|
||||
let ident = tt::Leaf::Ident(tt::Ident {
|
||||
span: tt::TokenId::unspecified(),
|
||||
text: format!("T{idx}").into(),
|
||||
});
|
||||
.map(|(ident, param_ty, bound)| {
|
||||
let ident_ = ident.clone();
|
||||
if let Some(b) = bound {
|
||||
let ident = ident.clone();
|
||||
where_block.push(quote! { #ident : #b , });
|
||||
}
|
||||
if let Some(ty) = param_ty {
|
||||
(quote! { const #ident : #ty , }, quote! { #ident_ , })
|
||||
} else {
|
||||
@ -134,9 +218,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
|
||||
}
|
||||
})
|
||||
.unzip();
|
||||
|
||||
where_block.extend(info.associated_types.iter().map(|x| {
|
||||
let x = x.clone();
|
||||
let bound = trait_path.clone();
|
||||
quote! { #x : #bound , }
|
||||
}));
|
||||
|
||||
let name = info.name;
|
||||
let expanded = quote! {
|
||||
impl < ##params > #trait_path for #name < ##args > {}
|
||||
impl < ##params > #trait_path for #name < ##args > where ##where_block {}
|
||||
};
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ pub type ExpandResult<T> = ValueResult<T, ExpandError>;
|
||||
pub enum ExpandError {
|
||||
UnresolvedProcMacro(CrateId),
|
||||
Mbe(mbe::ExpandError),
|
||||
RecursionOverflowPosioned,
|
||||
RecursionOverflowPoisoned,
|
||||
Other(Box<str>),
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ impl fmt::Display for ExpandError {
|
||||
match self {
|
||||
ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"),
|
||||
ExpandError::Mbe(it) => it.fmt(f),
|
||||
ExpandError::RecursionOverflowPosioned => {
|
||||
ExpandError::RecursionOverflowPoisoned => {
|
||||
f.write_str("overflow expanding the original macro")
|
||||
}
|
||||
ExpandError::Other(it) => f.write_str(it),
|
||||
|
@ -120,8 +120,7 @@ impl Name {
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
static CNT: AtomicUsize = AtomicUsize::new(0);
|
||||
let c = CNT.fetch_add(1, Ordering::Relaxed);
|
||||
// FIXME: Currently a `__RA_generated_name` in user code will break our analysis
|
||||
Name::new_text(format!("__RA_geneated_name_{c}").into())
|
||||
Name::new_text(format!("<ra@gennew>{c}").into())
|
||||
}
|
||||
|
||||
/// Returns the tuple index this name represents if it is a tuple field.
|
||||
|
@ -9,7 +9,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_def::{
|
||||
expr::Movability,
|
||||
hir::Movability,
|
||||
lang_item::{lang_attr, LangItem, LangItemTarget},
|
||||
AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId,
|
||||
};
|
||||
@ -18,9 +18,10 @@ use hir_expand::name::name;
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
display::HirDisplay,
|
||||
from_assoc_type_id, from_chalk_trait_id, make_binders, make_single_type_binders,
|
||||
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_binders,
|
||||
make_single_type_binders,
|
||||
mapping::{from_chalk, ToChalk, TypeAliasAsValue},
|
||||
method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
|
||||
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
|
||||
to_assoc_type_id, to_chalk_trait_id,
|
||||
traits::ChalkContext,
|
||||
utils::generics,
|
||||
@ -106,6 +107,19 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
||||
_ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
|
||||
};
|
||||
|
||||
let trait_module = trait_.module(self.db.upcast());
|
||||
let type_module = match self_ty_fp {
|
||||
Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())),
|
||||
Some(TyFingerprint::ForeignType(type_id)) => {
|
||||
Some(from_foreign_def_id(type_id).module(self.db.upcast()))
|
||||
}
|
||||
Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let mut def_blocks =
|
||||
[trait_module.containing_block(), type_module.and_then(|it| it.containing_block())];
|
||||
|
||||
// Note: Since we're using impls_for_trait, only impls where the trait
|
||||
// can be resolved should ever reach Chalk. impl_datum relies on that
|
||||
// and will panic if the trait can't be resolved.
|
||||
@ -120,6 +134,14 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
||||
.and_then(|map| map.parent())
|
||||
.and_then(|module| module.containing_block())
|
||||
})
|
||||
.inspect(|&block_id| {
|
||||
// make sure we don't search the same block twice
|
||||
def_blocks.iter_mut().for_each(|block| {
|
||||
if *block == Some(block_id) {
|
||||
*block = None;
|
||||
}
|
||||
});
|
||||
})
|
||||
.filter_map(|block_id| self.db.trait_impls_in_block(block_id));
|
||||
|
||||
let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
|
||||
@ -127,18 +149,27 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
||||
match fps {
|
||||
[] => {
|
||||
debug!("Unrestricted search for {:?} impls...", trait_);
|
||||
impl_maps.into_iter().chain(block_impls).for_each(|impls| {
|
||||
let mut f = |impls: Arc<TraitImpls>| {
|
||||
result.extend(impls.for_trait(trait_).map(id_to_chalk));
|
||||
});
|
||||
};
|
||||
impl_maps.into_iter().chain(block_impls).for_each(&mut f);
|
||||
def_blocks
|
||||
.into_iter()
|
||||
.filter_map(|it| self.db.trait_impls_in_block(it?))
|
||||
.for_each(f);
|
||||
}
|
||||
fps => {
|
||||
impl_maps.into_iter().chain(block_impls).for_each(|impls| {
|
||||
result.extend(
|
||||
fps.iter().flat_map(|fp| {
|
||||
let mut f =
|
||||
|impls: Arc<TraitImpls>| {
|
||||
result.extend(fps.iter().flat_map(|fp| {
|
||||
impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
|
||||
}),
|
||||
);
|
||||
});
|
||||
}));
|
||||
};
|
||||
impl_maps.into_iter().chain(block_impls).for_each(&mut f);
|
||||
def_blocks
|
||||
.into_iter()
|
||||
.filter_map(|it| self.db.trait_impls_in_block(it?))
|
||||
.for_each(f);
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,8 +415,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
||||
let input_output = crate::make_type_and_const_binders(it, input_output);
|
||||
|
||||
let movability = match self.db.body(parent)[expr] {
|
||||
hir_def::expr::Expr::Closure {
|
||||
closure_kind: hir_def::expr::ClosureKind::Generator(movability),
|
||||
hir_def::hir::Expr::Closure {
|
||||
closure_kind: hir_def::hir::ClosureKind::Generator(movability),
|
||||
..
|
||||
} => movability,
|
||||
_ => unreachable!("non generator expression interned as generator"),
|
||||
|
@ -12,8 +12,9 @@ use hir_def::{
|
||||
use crate::{
|
||||
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
|
||||
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
|
||||
CallableDefId, CallableSig, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
|
||||
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
|
||||
CallableDefId, CallableSig, ClosureId, DynTy, FnPointer, ImplTraitId, Interner, Lifetime,
|
||||
ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags,
|
||||
WhereClause,
|
||||
};
|
||||
|
||||
pub trait TyExt {
|
||||
@ -28,6 +29,7 @@ pub trait TyExt {
|
||||
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
|
||||
fn as_builtin(&self) -> Option<BuiltinType>;
|
||||
fn as_tuple(&self) -> Option<&Substitution>;
|
||||
fn as_closure(&self) -> Option<ClosureId>;
|
||||
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
|
||||
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
|
||||
fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
|
||||
@ -128,6 +130,13 @@ impl TyExt for Ty {
|
||||
}
|
||||
}
|
||||
|
||||
fn as_closure(&self) -> Option<ClosureId> {
|
||||
match self.kind(Interner) {
|
||||
TyKind::Closure(id, _) => Some(*id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
|
||||
match self.callable_def(db) {
|
||||
Some(CallableDefId::FunctionId(func)) => Some(func),
|
||||
|
@ -3,7 +3,7 @@
|
||||
use base_db::CrateId;
|
||||
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
|
||||
use hir_def::{
|
||||
expr::Expr,
|
||||
hir::Expr,
|
||||
path::Path,
|
||||
resolver::{Resolver, ValueNs},
|
||||
type_ref::ConstRef,
|
||||
|
@ -1105,6 +1105,81 @@ fn try_block() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closures() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: fn, copy
|
||||
const GOAL: i32 = {
|
||||
let y = 5;
|
||||
let c = |x| x + y;
|
||||
c(2)
|
||||
};
|
||||
"#,
|
||||
7,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: fn, copy
|
||||
const GOAL: i32 = {
|
||||
let y = 5;
|
||||
let c = |(a, b): &(i32, i32)| *a + *b + y;
|
||||
c(&(2, 3))
|
||||
};
|
||||
"#,
|
||||
10,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: fn, copy
|
||||
const GOAL: i32 = {
|
||||
let mut y = 5;
|
||||
let c = |x| {
|
||||
y = y + x;
|
||||
};
|
||||
c(2);
|
||||
c(3);
|
||||
y
|
||||
};
|
||||
"#,
|
||||
10,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: fn, copy
|
||||
struct X(i32);
|
||||
impl X {
|
||||
fn mult(&mut self, n: i32) {
|
||||
self.0 = self.0 * n
|
||||
}
|
||||
}
|
||||
const GOAL: i32 = {
|
||||
let x = X(1);
|
||||
let c = || {
|
||||
x.mult(2);
|
||||
|| {
|
||||
x.mult(3);
|
||||
|| {
|
||||
|| {
|
||||
x.mult(4);
|
||||
|| {
|
||||
x.mult(x.0);
|
||||
|| {
|
||||
x.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let r = c()()()()()();
|
||||
r + x.0
|
||||
};
|
||||
"#,
|
||||
24 * 24 * 2,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn or_pattern() {
|
||||
check_number(
|
||||
|
@ -6,7 +6,7 @@ use std::sync::Arc;
|
||||
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
|
||||
use hir_def::{
|
||||
db::DefDatabase,
|
||||
expr::ExprId,
|
||||
hir::ExprId,
|
||||
layout::{Layout, LayoutError, TargetDataLayout},
|
||||
AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId,
|
||||
ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
|
||||
@ -19,9 +19,9 @@ use crate::{
|
||||
consteval::ConstEvalError,
|
||||
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
|
||||
mir::{BorrowckResult, MirBody, MirLowerError},
|
||||
Binders, CallableDefId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner,
|
||||
PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId,
|
||||
ValueTyDefId,
|
||||
Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult,
|
||||
Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty,
|
||||
TyDefId, ValueTyDefId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
||||
@ -38,8 +38,11 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||
#[salsa::cycle(crate::mir::mir_body_recover)]
|
||||
fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>;
|
||||
|
||||
#[salsa::invoke(crate::mir::mir_body_for_closure_query)]
|
||||
fn mir_body_for_closure(&self, def: ClosureId) -> Result<Arc<MirBody>, MirLowerError>;
|
||||
|
||||
#[salsa::invoke(crate::mir::borrowck_query)]
|
||||
fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<BorrowckResult>, MirLowerError>;
|
||||
fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>;
|
||||
|
||||
#[salsa::invoke(crate::lower::ty_query)]
|
||||
#[salsa::cycle(crate::lower::ty_recover)]
|
||||
|
@ -16,8 +16,8 @@ use std::fmt;
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_def::{
|
||||
adt::VariantData,
|
||||
expr::{Pat, PatId},
|
||||
data::adt::VariantData,
|
||||
hir::{Pat, PatId},
|
||||
src::HasSource,
|
||||
AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, StaticId,
|
||||
StructId,
|
||||
|
@ -27,7 +27,7 @@ use crate::{
|
||||
|
||||
pub(crate) use hir_def::{
|
||||
body::Body,
|
||||
expr::{Expr, ExprId, MatchArm, Pat, PatId},
|
||||
hir::{Expr, ExprId, MatchArm, Pat, PatId},
|
||||
LocalFieldId, VariantId,
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Validation of matches.
|
||||
//!
|
||||
//! This module provides lowering from [hir_def::expr::Pat] to [self::Pat] and match
|
||||
//! This module provides lowering from [hir_def::hir::Pat] to [self::Pat] and match
|
||||
//! checking algorithm.
|
||||
//!
|
||||
//! It is modeled on the rustc module `rustc_mir_build::thir::pattern`.
|
||||
@ -12,7 +12,7 @@ pub(crate) mod usefulness;
|
||||
|
||||
use chalk_ir::Mutability;
|
||||
use hir_def::{
|
||||
adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
|
||||
body::Body, data::adt::VariantData, hir::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use stdx::{always, never};
|
||||
@ -125,15 +125,15 @@ impl<'a> PatCtxt<'a> {
|
||||
let variant = self.infer.variant_resolution_for_pat(pat);
|
||||
|
||||
let kind = match self.body[pat] {
|
||||
hir_def::expr::Pat::Wild => PatKind::Wild,
|
||||
hir_def::hir::Pat::Wild => PatKind::Wild,
|
||||
|
||||
hir_def::expr::Pat::Lit(expr) => self.lower_lit(expr),
|
||||
hir_def::hir::Pat::Lit(expr) => self.lower_lit(expr),
|
||||
|
||||
hir_def::expr::Pat::Path(ref path) => {
|
||||
hir_def::hir::Pat::Path(ref path) => {
|
||||
return self.lower_path(pat, path);
|
||||
}
|
||||
|
||||
hir_def::expr::Pat::Tuple { ref args, ellipsis } => {
|
||||
hir_def::hir::Pat::Tuple { ref args, ellipsis } => {
|
||||
let arity = match *ty.kind(Interner) {
|
||||
TyKind::Tuple(arity, _) => arity,
|
||||
_ => {
|
||||
@ -146,7 +146,7 @@ impl<'a> PatCtxt<'a> {
|
||||
PatKind::Leaf { subpatterns }
|
||||
}
|
||||
|
||||
hir_def::expr::Pat::Bind { id, subpat, .. } => {
|
||||
hir_def::hir::Pat::Bind { id, subpat, .. } => {
|
||||
let bm = self.infer.pat_binding_modes[&pat];
|
||||
let name = &self.body.bindings[id].name;
|
||||
match (bm, ty.kind(Interner)) {
|
||||
@ -161,13 +161,13 @@ impl<'a> PatCtxt<'a> {
|
||||
PatKind::Binding { name: name.clone(), subpattern: self.lower_opt_pattern(subpat) }
|
||||
}
|
||||
|
||||
hir_def::expr::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => {
|
||||
hir_def::hir::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => {
|
||||
let expected_len = variant.unwrap().variant_data(self.db.upcast()).fields().len();
|
||||
let subpatterns = self.lower_tuple_subpats(args, expected_len, ellipsis);
|
||||
self.lower_variant_or_leaf(pat, ty, subpatterns)
|
||||
}
|
||||
|
||||
hir_def::expr::Pat::Record { ref args, .. } if variant.is_some() => {
|
||||
hir_def::hir::Pat::Record { ref args, .. } if variant.is_some() => {
|
||||
let variant_data = variant.unwrap().variant_data(self.db.upcast());
|
||||
let subpatterns = args
|
||||
.iter()
|
||||
@ -187,12 +187,12 @@ impl<'a> PatCtxt<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
hir_def::expr::Pat::TupleStruct { .. } | hir_def::expr::Pat::Record { .. } => {
|
||||
hir_def::hir::Pat::TupleStruct { .. } | hir_def::hir::Pat::Record { .. } => {
|
||||
self.errors.push(PatternError::UnresolvedVariant);
|
||||
PatKind::Wild
|
||||
}
|
||||
|
||||
hir_def::expr::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
|
||||
hir_def::hir::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
|
||||
|
||||
_ => {
|
||||
self.errors.push(PatternError::Unimplemented);
|
||||
@ -279,8 +279,8 @@ impl<'a> PatCtxt<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_lit(&mut self, expr: hir_def::expr::ExprId) -> PatKind {
|
||||
use hir_def::expr::{Expr, Literal::Bool};
|
||||
fn lower_lit(&mut self, expr: hir_def::hir::ExprId) -> PatKind {
|
||||
use hir_def::hir::{Expr, Literal::Bool};
|
||||
|
||||
match self.body[expr] {
|
||||
Expr::Literal(Bool(value)) => PatKind::LiteralBool { value },
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
use hir_def::{
|
||||
body::Body,
|
||||
expr::{Expr, ExprId, UnaryOp},
|
||||
hir::{Expr, ExprId, UnaryOp},
|
||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||
DefWithBodyId,
|
||||
};
|
||||
|
@ -7,8 +7,8 @@ use std::fmt::{self, Debug};
|
||||
use base_db::CrateId;
|
||||
use chalk_ir::{BoundVar, TyKind};
|
||||
use hir_def::{
|
||||
adt::VariantData,
|
||||
body,
|
||||
data::adt::VariantData,
|
||||
db::DefDatabase,
|
||||
find_path,
|
||||
generics::{TypeOrConstParamData, TypeParamProvenance},
|
||||
@ -23,6 +23,7 @@ use hir_expand::{hygiene::Hygiene, name::Name};
|
||||
use intern::{Internable, Interned};
|
||||
use itertools::Itertools;
|
||||
use smallvec::SmallVec;
|
||||
use stdx::never;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
@ -64,6 +65,7 @@ pub struct HirFormatter<'a> {
|
||||
curr_size: usize,
|
||||
pub(crate) max_size: Option<usize>,
|
||||
omit_verbose_types: bool,
|
||||
closure_style: ClosureStyle,
|
||||
display_target: DisplayTarget,
|
||||
}
|
||||
|
||||
@ -87,6 +89,7 @@ pub trait HirDisplay {
|
||||
max_size: Option<usize>,
|
||||
omit_verbose_types: bool,
|
||||
display_target: DisplayTarget,
|
||||
closure_style: ClosureStyle,
|
||||
) -> HirDisplayWrapper<'a, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -95,7 +98,14 @@ pub trait HirDisplay {
|
||||
!matches!(display_target, DisplayTarget::SourceCode { .. }),
|
||||
"HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead"
|
||||
);
|
||||
HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target }
|
||||
HirDisplayWrapper {
|
||||
db,
|
||||
t: self,
|
||||
max_size,
|
||||
omit_verbose_types,
|
||||
display_target,
|
||||
closure_style,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a `Display`able type that is human-readable.
|
||||
@ -109,6 +119,7 @@ pub trait HirDisplay {
|
||||
t: self,
|
||||
max_size: None,
|
||||
omit_verbose_types: false,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Diagnostics,
|
||||
}
|
||||
}
|
||||
@ -128,6 +139,7 @@ pub trait HirDisplay {
|
||||
t: self,
|
||||
max_size,
|
||||
omit_verbose_types: true,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Diagnostics,
|
||||
}
|
||||
}
|
||||
@ -147,6 +159,7 @@ pub trait HirDisplay {
|
||||
curr_size: 0,
|
||||
max_size: None,
|
||||
omit_verbose_types: false,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::SourceCode { module_id },
|
||||
}) {
|
||||
Ok(()) => {}
|
||||
@ -166,6 +179,7 @@ pub trait HirDisplay {
|
||||
t: self,
|
||||
max_size: None,
|
||||
omit_verbose_types: false,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Test,
|
||||
}
|
||||
}
|
||||
@ -253,7 +267,6 @@ impl DisplayTarget {
|
||||
pub enum DisplaySourceCodeError {
|
||||
PathNotFound,
|
||||
UnknownType,
|
||||
Closure,
|
||||
Generator,
|
||||
}
|
||||
|
||||
@ -274,9 +287,23 @@ pub struct HirDisplayWrapper<'a, T> {
|
||||
t: &'a T,
|
||||
max_size: Option<usize>,
|
||||
omit_verbose_types: bool,
|
||||
closure_style: ClosureStyle,
|
||||
display_target: DisplayTarget,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ClosureStyle {
|
||||
/// `impl FnX(i32, i32) -> i32`, where `FnX` is the most special trait between `Fn`, `FnMut`, `FnOnce` that the
|
||||
/// closure implements. This is the default.
|
||||
ImplFn,
|
||||
/// `|i32, i32| -> i32`
|
||||
RANotation,
|
||||
/// `{closure#14825}`, useful for some diagnostics (like type mismatch) and internal usage.
|
||||
ClosureWithId,
|
||||
/// `…`, which is the `TYPE_HINT_TRUNCATION`
|
||||
Hide,
|
||||
}
|
||||
|
||||
impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
|
||||
pub fn write_to<F: HirWrite>(&self, f: &mut F) -> Result<(), HirDisplayError> {
|
||||
self.t.hir_fmt(&mut HirFormatter {
|
||||
@ -287,8 +314,14 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
|
||||
max_size: self.max_size,
|
||||
omit_verbose_types: self.omit_verbose_types,
|
||||
display_target: self.display_target,
|
||||
closure_style: self.closure_style,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_closure_style(mut self, c: ClosureStyle) -> Self {
|
||||
self.closure_style = c;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
|
||||
@ -919,26 +952,42 @@ impl HirDisplay for Ty {
|
||||
}
|
||||
}
|
||||
}
|
||||
TyKind::Closure(.., substs) => {
|
||||
if f.display_target.is_source_code() {
|
||||
return Err(HirDisplayError::DisplaySourceCodeError(
|
||||
DisplaySourceCodeError::Closure,
|
||||
));
|
||||
TyKind::Closure(id, substs) => {
|
||||
if f.display_target.is_source_code() && f.closure_style != ClosureStyle::ImplFn {
|
||||
never!("Only `impl Fn` is valid for displaying closures in source code");
|
||||
}
|
||||
match f.closure_style {
|
||||
ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),
|
||||
ClosureStyle::ClosureWithId => {
|
||||
return write!(f, "{{closure#{:?}}}", id.0.as_u32())
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db);
|
||||
if let Some(sig) = sig {
|
||||
let (def, _) = db.lookup_intern_closure((*id).into());
|
||||
let infer = db.infer(def);
|
||||
let (_, kind) = infer.closure_info(id);
|
||||
match f.closure_style {
|
||||
ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?,
|
||||
ClosureStyle::RANotation => write!(f, "|")?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
if sig.params().is_empty() {
|
||||
write!(f, "||")?;
|
||||
} else if f.should_truncate() {
|
||||
write!(f, "|{TYPE_HINT_TRUNCATION}|")?;
|
||||
write!(f, "{TYPE_HINT_TRUNCATION}")?;
|
||||
} else {
|
||||
write!(f, "|")?;
|
||||
f.write_joined(sig.params(), ", ")?;
|
||||
write!(f, "|")?;
|
||||
};
|
||||
|
||||
write!(f, " -> ")?;
|
||||
sig.ret().hir_fmt(f)?;
|
||||
match f.closure_style {
|
||||
ClosureStyle::ImplFn => write!(f, ")")?,
|
||||
ClosureStyle::RANotation => write!(f, "|")?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
if f.closure_style == ClosureStyle::RANotation || !sig.ret().is_unit() {
|
||||
write!(f, " -> ")?;
|
||||
sig.ret().hir_fmt(f)?;
|
||||
}
|
||||
} else {
|
||||
write!(f, "{{closure}}")?;
|
||||
}
|
||||
|
@ -18,11 +18,12 @@ use std::{convert::identity, ops::Index};
|
||||
|
||||
use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
|
||||
use either::Either;
|
||||
use hir_def::hir::LabelId;
|
||||
use hir_def::{
|
||||
body::Body,
|
||||
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||
data::{ConstData, StaticData},
|
||||
expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
|
||||
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
layout::Integer,
|
||||
path::{ModPath, Path},
|
||||
@ -32,15 +33,15 @@ use hir_def::{
|
||||
TraitId, TypeAliasId, VariantId,
|
||||
};
|
||||
use hir_expand::name::{name, Name};
|
||||
use la_arena::ArenaMap;
|
||||
use la_arena::{ArenaMap, Entry};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use stdx::{always, never};
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
|
||||
static_lifetime, to_assoc_type_id, AliasEq, AliasTy, DomainGoal, GenericArg, Goal, ImplTraitId,
|
||||
InEnvironment, Interner, ProjectionTy, RpitId, Substitution, TraitRef, Ty, TyBuilder, TyExt,
|
||||
TyKind,
|
||||
static_lifetime, to_assoc_type_id, traits::FnTrait, AliasEq, AliasTy, ClosureId, DomainGoal,
|
||||
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
|
||||
TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||
};
|
||||
|
||||
// This lint has a false positive here. See the link below for details.
|
||||
@ -51,6 +52,8 @@ pub use coerce::could_coerce;
|
||||
#[allow(unreachable_pub)]
|
||||
pub use unify::could_unify;
|
||||
|
||||
pub(crate) use self::closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
|
||||
|
||||
pub(crate) mod unify;
|
||||
mod path;
|
||||
mod expr;
|
||||
@ -102,6 +105,8 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
|
||||
|
||||
ctx.infer_mut_body();
|
||||
|
||||
ctx.infer_closures();
|
||||
|
||||
Arc::new(ctx.resolve_all())
|
||||
}
|
||||
|
||||
@ -188,7 +193,7 @@ pub enum InferenceDiagnostic {
|
||||
/// Contains the type the field resolves to
|
||||
field_with_same_name: Option<Ty>,
|
||||
},
|
||||
// FIXME: Make this proper
|
||||
// FIXME: This should be emitted in body lowering
|
||||
BreakOutsideOfLoop {
|
||||
expr: ExprId,
|
||||
is_break: bool,
|
||||
@ -311,6 +316,13 @@ pub enum AutoBorrow {
|
||||
RawPtr(Mutability),
|
||||
}
|
||||
|
||||
impl AutoBorrow {
|
||||
fn mutability(self) -> Mutability {
|
||||
let (AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) = self;
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum PointerCast {
|
||||
/// Go from a fn-item type to a fn-pointer type.
|
||||
@ -372,6 +384,9 @@ pub struct InferenceResult {
|
||||
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
|
||||
pub pat_binding_modes: FxHashMap<PatId, BindingMode>,
|
||||
pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>,
|
||||
pub(crate) closure_info: FxHashMap<ClosureId, (Vec<CapturedItem>, FnTrait)>,
|
||||
// FIXME: remove this field
|
||||
pub mutated_bindings_in_closure: FxHashSet<BindingId>,
|
||||
}
|
||||
|
||||
impl InferenceResult {
|
||||
@ -408,6 +423,9 @@ impl InferenceResult {
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
pub(crate) fn closure_info(&self, closure: &ClosureId) -> &(Vec<CapturedItem>, FnTrait) {
|
||||
self.closure_info.get(closure).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<ExprId> for InferenceResult {
|
||||
@ -459,6 +477,14 @@ pub(crate) struct InferenceContext<'a> {
|
||||
resume_yield_tys: Option<(Ty, Ty)>,
|
||||
diverges: Diverges,
|
||||
breakables: Vec<BreakableContext>,
|
||||
|
||||
// fields related to closure capture
|
||||
current_captures: Vec<CapturedItemWithoutTy>,
|
||||
current_closure: Option<ClosureId>,
|
||||
/// Stores the list of closure ids that need to be analyzed before this closure. See the
|
||||
/// comment on `InferenceContext::sort_closures`
|
||||
closure_dependecies: FxHashMap<ClosureId, Vec<ClosureId>>,
|
||||
deferred_closures: FxHashMap<ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -468,7 +494,7 @@ struct BreakableContext {
|
||||
/// The coercion target of the context.
|
||||
coerce: Option<CoerceMany>,
|
||||
/// The optional label of the context.
|
||||
label: Option<name::Name>,
|
||||
label: Option<LabelId>,
|
||||
kind: BreakableKind,
|
||||
}
|
||||
|
||||
@ -483,21 +509,21 @@ enum BreakableKind {
|
||||
|
||||
fn find_breakable<'c>(
|
||||
ctxs: &'c mut [BreakableContext],
|
||||
label: Option<&name::Name>,
|
||||
label: Option<LabelId>,
|
||||
) -> Option<&'c mut BreakableContext> {
|
||||
let mut ctxs = ctxs
|
||||
.iter_mut()
|
||||
.rev()
|
||||
.take_while(|it| matches!(it.kind, BreakableKind::Block | BreakableKind::Loop));
|
||||
match label {
|
||||
Some(_) => ctxs.find(|ctx| ctx.label.as_ref() == label),
|
||||
Some(_) => ctxs.find(|ctx| ctx.label == label),
|
||||
None => ctxs.find(|ctx| matches!(ctx.kind, BreakableKind::Loop)),
|
||||
}
|
||||
}
|
||||
|
||||
fn find_continuable<'c>(
|
||||
ctxs: &'c mut [BreakableContext],
|
||||
label: Option<&name::Name>,
|
||||
label: Option<LabelId>,
|
||||
) -> Option<&'c mut BreakableContext> {
|
||||
match label {
|
||||
Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)),
|
||||
@ -526,6 +552,10 @@ impl<'a> InferenceContext<'a> {
|
||||
resolver,
|
||||
diverges: Diverges::Maybe,
|
||||
breakables: Vec::new(),
|
||||
current_captures: vec![],
|
||||
current_closure: None,
|
||||
deferred_closures: FxHashMap::default(),
|
||||
closure_dependecies: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -617,7 +647,7 @@ impl<'a> InferenceContext<'a> {
|
||||
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Param);
|
||||
let mut param_tys =
|
||||
data.params.iter().map(|(_, type_ref)| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
|
||||
data.params.iter().map(|type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
|
||||
// Check if function contains a va_list, if it does then we append it to the parameter types
|
||||
// that are collected from the function data
|
||||
if data.is_varargs() {
|
||||
@ -646,36 +676,16 @@ impl<'a> InferenceContext<'a> {
|
||||
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
|
||||
// RPIT opaque types use substitution of their parent function.
|
||||
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
|
||||
fold_tys(
|
||||
return_ty,
|
||||
|ty, _| {
|
||||
let opaque_ty_id = match ty.kind(Interner) {
|
||||
TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
|
||||
_ => return ty,
|
||||
};
|
||||
let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
|
||||
ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let bounds = (*rpits).map_ref(|rpits| {
|
||||
rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter())
|
||||
});
|
||||
let var = self.table.new_type_var();
|
||||
let var_subst = Substitution::from1(Interner, var.clone());
|
||||
for bound in bounds {
|
||||
let predicate =
|
||||
bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
|
||||
let (var_predicate, binders) = predicate
|
||||
.substitute(Interner, &var_subst)
|
||||
.into_value_and_skipped_binders();
|
||||
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
|
||||
self.push_obligation(var_predicate.cast(Interner));
|
||||
}
|
||||
self.result.type_of_rpit.insert(idx, var.clone());
|
||||
var
|
||||
},
|
||||
DebruijnIndex::INNERMOST,
|
||||
)
|
||||
let result =
|
||||
self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders);
|
||||
let rpits = rpits.skip_binders();
|
||||
for (id, _) in rpits.impl_traits.iter() {
|
||||
if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
|
||||
never!("Missed RPIT in `insert_inference_vars_for_rpit`");
|
||||
e.insert(TyKind::Error.intern(Interner));
|
||||
}
|
||||
}
|
||||
result
|
||||
} else {
|
||||
return_ty
|
||||
};
|
||||
@ -684,6 +694,50 @@ impl<'a> InferenceContext<'a> {
|
||||
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
|
||||
}
|
||||
|
||||
fn insert_inference_vars_for_rpit<T>(
|
||||
&mut self,
|
||||
t: T,
|
||||
rpits: Arc<chalk_ir::Binders<crate::ReturnTypeImplTraits>>,
|
||||
fn_placeholders: Substitution,
|
||||
) -> T
|
||||
where
|
||||
T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
|
||||
{
|
||||
fold_tys(
|
||||
t,
|
||||
|ty, _| {
|
||||
let opaque_ty_id = match ty.kind(Interner) {
|
||||
TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
|
||||
_ => return ty,
|
||||
};
|
||||
let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
|
||||
ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let bounds = (*rpits)
|
||||
.map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter()));
|
||||
let var = self.table.new_type_var();
|
||||
let var_subst = Substitution::from1(Interner, var.clone());
|
||||
for bound in bounds {
|
||||
let predicate =
|
||||
bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
|
||||
let (var_predicate, binders) =
|
||||
predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
|
||||
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
|
||||
let var_predicate = self.insert_inference_vars_for_rpit(
|
||||
var_predicate,
|
||||
rpits.clone(),
|
||||
fn_placeholders.clone(),
|
||||
);
|
||||
self.push_obligation(var_predicate.cast(Interner));
|
||||
}
|
||||
self.result.type_of_rpit.insert(idx, var.clone());
|
||||
var
|
||||
},
|
||||
DebruijnIndex::INNERMOST,
|
||||
)
|
||||
}
|
||||
|
||||
fn infer_body(&mut self) {
|
||||
match self.return_coercion {
|
||||
Some(_) => self.infer_return(self.body.body_expr),
|
||||
|
@ -1,12 +1,29 @@
|
||||
//! Inference of closure parameter types based on the closure's expected type.
|
||||
|
||||
use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, WhereClause};
|
||||
use hir_def::{expr::ExprId, HasModule};
|
||||
use std::{cmp, collections::HashMap, convert::Infallible, mem};
|
||||
|
||||
use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, Mutability, TyKind, WhereClause};
|
||||
use hir_def::{
|
||||
hir::{
|
||||
Array, BinaryOp, BindingAnnotation, BindingId, CaptureBy, Expr, ExprId, Pat, PatId,
|
||||
Statement, UnaryOp,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||
FieldId, HasModule, VariantId,
|
||||
};
|
||||
use hir_expand::name;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use stdx::never;
|
||||
|
||||
use crate::{
|
||||
to_chalk_trait_id, utils, ChalkTraitId, DynTy, FnPointer, FnSig, Interner, Substitution, Ty,
|
||||
TyExt, TyKind,
|
||||
mir::{BorrowKind, ProjectionElem},
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
traits::FnTrait,
|
||||
utils::{self, pattern_matching_dereference_count},
|
||||
Adjust, Adjustment, Canonical, CanonicalVarKinds, ChalkTraitId, ClosureId, DynTy, FnPointer,
|
||||
FnSig, InEnvironment, Interner, Substitution, Ty, TyBuilder, TyExt,
|
||||
};
|
||||
|
||||
use super::{Expectation, InferenceContext};
|
||||
@ -86,3 +103,700 @@ impl InferenceContext<'_> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// The below functions handle capture and closure kind (Fn, FnMut, ..)
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct HirPlace {
|
||||
pub(crate) local: BindingId,
|
||||
pub(crate) projections: Vec<ProjectionElem<Infallible, Ty>>,
|
||||
}
|
||||
impl HirPlace {
|
||||
fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty {
|
||||
let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone());
|
||||
for p in &self.projections {
|
||||
ty = p.projected_ty(ty, ctx.db, |_, _| {
|
||||
unreachable!("Closure field only happens in MIR");
|
||||
});
|
||||
}
|
||||
ty.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum CaptureKind {
|
||||
ByRef(BorrowKind),
|
||||
ByValue,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct CapturedItem {
|
||||
pub(crate) place: HirPlace,
|
||||
pub(crate) kind: CaptureKind,
|
||||
pub(crate) ty: Ty,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct CapturedItemWithoutTy {
|
||||
pub(crate) place: HirPlace,
|
||||
pub(crate) kind: CaptureKind,
|
||||
}
|
||||
|
||||
impl CapturedItemWithoutTy {
|
||||
fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem {
|
||||
let ty = self.place.ty(ctx).clone();
|
||||
let ty = match &self.kind {
|
||||
CaptureKind::ByValue => ty,
|
||||
CaptureKind::ByRef(bk) => {
|
||||
let m = match bk {
|
||||
BorrowKind::Mut { .. } => Mutability::Mut,
|
||||
_ => Mutability::Not,
|
||||
};
|
||||
TyKind::Ref(m, static_lifetime(), ty).intern(Interner)
|
||||
}
|
||||
};
|
||||
CapturedItem { place: self.place, kind: self.kind, ty }
|
||||
}
|
||||
}
|
||||
|
||||
impl InferenceContext<'_> {
|
||||
fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
|
||||
let r = self.place_of_expr_without_adjust(tgt_expr)?;
|
||||
let default = vec![];
|
||||
let adjustments = self.result.expr_adjustments.get(&tgt_expr).unwrap_or(&default);
|
||||
apply_adjusts_to_place(r, adjustments)
|
||||
}
|
||||
|
||||
fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
|
||||
match &self.body[tgt_expr] {
|
||||
Expr::Path(p) => {
|
||||
let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
|
||||
if let Some(r) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) {
|
||||
if let ResolveValueResult::ValueNs(v) = r {
|
||||
if let ValueNs::LocalBinding(b) = v {
|
||||
return Some(HirPlace { local: b, projections: vec![] });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Field { expr, name } => {
|
||||
let mut place = self.place_of_expr(*expr)?;
|
||||
if let TyKind::Tuple(..) = self.expr_ty(*expr).kind(Interner) {
|
||||
let index = name.as_tuple_index()?;
|
||||
place.projections.push(ProjectionElem::TupleOrClosureField(index))
|
||||
} else {
|
||||
let field = self.result.field_resolution(tgt_expr)?;
|
||||
place.projections.push(ProjectionElem::Field(field));
|
||||
}
|
||||
return Some(place);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn push_capture(&mut self, capture: CapturedItemWithoutTy) {
|
||||
self.current_captures.push(capture);
|
||||
}
|
||||
|
||||
fn ref_expr(&mut self, expr: ExprId) {
|
||||
if let Some(place) = self.place_of_expr(expr) {
|
||||
self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared));
|
||||
}
|
||||
self.walk_expr(expr);
|
||||
}
|
||||
|
||||
fn add_capture(&mut self, place: HirPlace, kind: CaptureKind) {
|
||||
if self.is_upvar(&place) {
|
||||
self.push_capture(CapturedItemWithoutTy { place, kind });
|
||||
}
|
||||
}
|
||||
|
||||
fn mutate_expr(&mut self, expr: ExprId) {
|
||||
if let Some(place) = self.place_of_expr(expr) {
|
||||
self.add_capture(
|
||||
place,
|
||||
CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }),
|
||||
);
|
||||
}
|
||||
self.walk_expr(expr);
|
||||
}
|
||||
|
||||
fn consume_expr(&mut self, expr: ExprId) {
|
||||
if let Some(place) = self.place_of_expr(expr) {
|
||||
self.consume_place(place);
|
||||
}
|
||||
self.walk_expr(expr);
|
||||
}
|
||||
|
||||
fn consume_place(&mut self, place: HirPlace) {
|
||||
if self.is_upvar(&place) {
|
||||
let ty = place.ty(self).clone();
|
||||
let kind = if self.is_ty_copy(ty) {
|
||||
CaptureKind::ByRef(BorrowKind::Shared)
|
||||
} else {
|
||||
CaptureKind::ByValue
|
||||
};
|
||||
self.push_capture(CapturedItemWithoutTy { place, kind });
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) {
|
||||
if let Some((last, rest)) = adjustment.split_last() {
|
||||
match last.kind {
|
||||
Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => {
|
||||
self.walk_expr_with_adjust(tgt_expr, rest)
|
||||
}
|
||||
Adjust::Deref(Some(m)) => match m.0 {
|
||||
Some(m) => {
|
||||
self.ref_capture_with_adjusts(m, tgt_expr, rest);
|
||||
}
|
||||
None => unreachable!(),
|
||||
},
|
||||
Adjust::Borrow(b) => {
|
||||
self.ref_capture_with_adjusts(b.mutability(), tgt_expr, rest);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.walk_expr_without_adjust(tgt_expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) {
|
||||
let capture_kind = match m {
|
||||
Mutability::Mut => {
|
||||
CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false })
|
||||
}
|
||||
Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared),
|
||||
};
|
||||
if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) {
|
||||
if let Some(place) = apply_adjusts_to_place(place, rest) {
|
||||
if self.is_upvar(&place) {
|
||||
self.push_capture(CapturedItemWithoutTy { place, kind: capture_kind });
|
||||
}
|
||||
}
|
||||
}
|
||||
self.walk_expr_with_adjust(tgt_expr, rest);
|
||||
}
|
||||
|
||||
fn walk_expr(&mut self, tgt_expr: ExprId) {
|
||||
if let Some(x) = self.result.expr_adjustments.get_mut(&tgt_expr) {
|
||||
// FIXME: this take is completely unneeded, and just is here to make borrow checker
|
||||
// happy. Remove it if you can.
|
||||
let x_taken = mem::take(x);
|
||||
self.walk_expr_with_adjust(tgt_expr, &x_taken);
|
||||
*self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken;
|
||||
} else {
|
||||
self.walk_expr_without_adjust(tgt_expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
|
||||
match &self.body[tgt_expr] {
|
||||
Expr::If { condition, then_branch, else_branch } => {
|
||||
self.consume_expr(*condition);
|
||||
self.consume_expr(*then_branch);
|
||||
if let &Some(expr) = else_branch {
|
||||
self.consume_expr(expr);
|
||||
}
|
||||
}
|
||||
Expr::Async { statements, tail, .. }
|
||||
| Expr::Const { statements, tail, .. }
|
||||
| Expr::Unsafe { statements, tail, .. }
|
||||
| Expr::Block { statements, tail, .. } => {
|
||||
for s in statements.iter() {
|
||||
match s {
|
||||
Statement::Let { pat, type_ref: _, initializer, else_branch } => {
|
||||
if let Some(else_branch) = else_branch {
|
||||
self.consume_expr(*else_branch);
|
||||
if let Some(initializer) = initializer {
|
||||
self.consume_expr(*initializer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if let Some(initializer) = initializer {
|
||||
self.walk_expr(*initializer);
|
||||
if let Some(place) = self.place_of_expr(*initializer) {
|
||||
let ty = self.expr_ty(*initializer);
|
||||
self.consume_with_pat(
|
||||
place,
|
||||
ty,
|
||||
BindingAnnotation::Unannotated,
|
||||
*pat,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::Expr { expr, has_semi: _ } => {
|
||||
self.consume_expr(*expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(tail) = tail {
|
||||
self.consume_expr(*tail);
|
||||
}
|
||||
}
|
||||
Expr::While { condition, body, label: _ }
|
||||
| Expr::For { iterable: condition, pat: _, body, label: _ } => {
|
||||
self.consume_expr(*condition);
|
||||
self.consume_expr(*body);
|
||||
}
|
||||
Expr::Call { callee, args, is_assignee_expr: _ } => {
|
||||
self.consume_expr(*callee);
|
||||
self.consume_exprs(args.iter().copied());
|
||||
}
|
||||
Expr::MethodCall { receiver, args, .. } => {
|
||||
self.consume_expr(*receiver);
|
||||
self.consume_exprs(args.iter().copied());
|
||||
}
|
||||
Expr::Match { expr, arms } => {
|
||||
self.consume_expr(*expr);
|
||||
for arm in arms.iter() {
|
||||
self.consume_expr(arm.expr);
|
||||
}
|
||||
}
|
||||
Expr::Break { expr, label: _ }
|
||||
| Expr::Return { expr }
|
||||
| Expr::Yield { expr }
|
||||
| Expr::Yeet { expr } => {
|
||||
if let &Some(expr) = expr {
|
||||
self.consume_expr(expr);
|
||||
}
|
||||
}
|
||||
Expr::RecordLit { fields, spread, .. } => {
|
||||
if let &Some(expr) = spread {
|
||||
self.consume_expr(expr);
|
||||
}
|
||||
self.consume_exprs(fields.iter().map(|x| x.expr));
|
||||
}
|
||||
Expr::Field { expr, name: _ } => self.select_from_expr(*expr),
|
||||
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
|
||||
if let Some((f, _)) = self.result.method_resolution(tgt_expr) {
|
||||
let mutability = 'b: {
|
||||
if let Some(deref_trait) =
|
||||
self.resolve_lang_item(LangItem::DerefMut).and_then(|x| x.as_trait())
|
||||
{
|
||||
if let Some(deref_fn) =
|
||||
self.db.trait_data(deref_trait).method_by_name(&name![deref_mut])
|
||||
{
|
||||
break 'b deref_fn == f;
|
||||
}
|
||||
}
|
||||
false
|
||||
};
|
||||
if mutability {
|
||||
self.mutate_expr(*expr);
|
||||
} else {
|
||||
self.ref_expr(*expr);
|
||||
}
|
||||
} else {
|
||||
self.select_from_expr(*expr);
|
||||
}
|
||||
}
|
||||
Expr::UnaryOp { expr, op: _ }
|
||||
| Expr::Array(Array::Repeat { initializer: expr, repeat: _ })
|
||||
| Expr::Await { expr }
|
||||
| Expr::Loop { body: expr, label: _ }
|
||||
| Expr::Let { pat: _, expr }
|
||||
| Expr::Box { expr }
|
||||
| Expr::Cast { expr, type_ref: _ } => {
|
||||
self.consume_expr(*expr);
|
||||
}
|
||||
Expr::Ref { expr, rawness: _, mutability } => match mutability {
|
||||
hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr),
|
||||
hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr),
|
||||
},
|
||||
Expr::BinaryOp { lhs, rhs, op } => {
|
||||
let Some(op) = op else {
|
||||
return;
|
||||
};
|
||||
if matches!(op, BinaryOp::Assignment { .. }) {
|
||||
self.mutate_expr(*lhs);
|
||||
self.consume_expr(*rhs);
|
||||
return;
|
||||
}
|
||||
self.consume_expr(*lhs);
|
||||
self.consume_expr(*rhs);
|
||||
}
|
||||
Expr::Range { lhs, rhs, range_type: _ } => {
|
||||
if let &Some(expr) = lhs {
|
||||
self.consume_expr(expr);
|
||||
}
|
||||
if let &Some(expr) = rhs {
|
||||
self.consume_expr(expr);
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index } => {
|
||||
self.select_from_expr(*base);
|
||||
self.consume_expr(*index);
|
||||
}
|
||||
Expr::Closure { .. } => {
|
||||
let ty = self.expr_ty(tgt_expr);
|
||||
let TyKind::Closure(id, _) = ty.kind(Interner) else {
|
||||
never!("closure type is always closure");
|
||||
return;
|
||||
};
|
||||
let (captures, _) =
|
||||
self.result.closure_info.get(id).expect(
|
||||
"We sort closures, so we should always have data for inner closures",
|
||||
);
|
||||
let mut cc = mem::take(&mut self.current_captures);
|
||||
cc.extend(
|
||||
captures
|
||||
.iter()
|
||||
.filter(|x| self.is_upvar(&x.place))
|
||||
.map(|x| CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind }),
|
||||
);
|
||||
self.current_captures = cc;
|
||||
}
|
||||
Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ })
|
||||
| Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||
self.consume_exprs(exprs.iter().copied())
|
||||
}
|
||||
Expr::Missing
|
||||
| Expr::Continue { .. }
|
||||
| Expr::Path(_)
|
||||
| Expr::Literal(_)
|
||||
| Expr::Underscore => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_ty(&mut self, expr: ExprId) -> Ty {
|
||||
self.result[expr].clone()
|
||||
}
|
||||
|
||||
fn is_upvar(&self, place: &HirPlace) -> bool {
|
||||
let b = &self.body[place.local];
|
||||
if let Some(c) = self.current_closure {
|
||||
let (_, root) = self.db.lookup_intern_closure(c.into());
|
||||
return b.is_upvar(root);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_ty_copy(&self, ty: Ty) -> bool {
|
||||
if let TyKind::Closure(id, _) = ty.kind(Interner) {
|
||||
// FIXME: We handle closure as a special case, since chalk consider every closure as copy. We
|
||||
// should probably let chalk know which closures are copy, but I don't know how doing it
|
||||
// without creating query cycles.
|
||||
return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true);
|
||||
}
|
||||
let crate_id = self.owner.module(self.db.upcast()).krate();
|
||||
let Some(copy_trait) = self.db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else {
|
||||
return false;
|
||||
};
|
||||
let trait_ref = TyBuilder::trait_ref(self.db, copy_trait).push(ty).build();
|
||||
let env = self.db.trait_environment_for_body(self.owner);
|
||||
let goal = Canonical {
|
||||
value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
|
||||
binders: CanonicalVarKinds::empty(Interner),
|
||||
};
|
||||
self.db.trait_solve(crate_id, None, goal).is_some()
|
||||
}
|
||||
|
||||
fn select_from_expr(&mut self, expr: ExprId) {
|
||||
self.walk_expr(expr);
|
||||
}
|
||||
|
||||
fn adjust_for_move_closure(&mut self) {
|
||||
for capture in &mut self.current_captures {
|
||||
if let Some(first_deref) =
|
||||
capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref)
|
||||
{
|
||||
capture.place.projections.truncate(first_deref);
|
||||
}
|
||||
capture.kind = CaptureKind::ByValue;
|
||||
}
|
||||
}
|
||||
|
||||
fn minimize_captures(&mut self) {
|
||||
self.current_captures.sort_by_key(|x| x.place.projections.len());
|
||||
let mut hash_map = HashMap::<HirPlace, usize>::new();
|
||||
let result = mem::take(&mut self.current_captures);
|
||||
for item in result {
|
||||
let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] };
|
||||
let mut it = item.place.projections.iter();
|
||||
let prev_index = loop {
|
||||
if let Some(k) = hash_map.get(&lookup_place) {
|
||||
break Some(*k);
|
||||
}
|
||||
match it.next() {
|
||||
Some(x) => lookup_place.projections.push(x.clone()),
|
||||
None => break None,
|
||||
}
|
||||
};
|
||||
match prev_index {
|
||||
Some(p) => {
|
||||
self.current_captures[p].kind =
|
||||
cmp::max(item.kind, self.current_captures[p].kind);
|
||||
}
|
||||
None => {
|
||||
hash_map.insert(item.place.clone(), self.current_captures.len());
|
||||
self.current_captures.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_with_pat(
|
||||
&mut self,
|
||||
mut place: HirPlace,
|
||||
mut ty: Ty,
|
||||
mut bm: BindingAnnotation,
|
||||
pat: PatId,
|
||||
) {
|
||||
match &self.body[pat] {
|
||||
Pat::Missing | Pat::Wild => (),
|
||||
Pat::Tuple { args, ellipsis } => {
|
||||
pattern_matching_dereference(&mut ty, &mut bm, &mut place);
|
||||
let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
|
||||
let subst = match ty.kind(Interner) {
|
||||
TyKind::Tuple(_, s) => s,
|
||||
_ => return,
|
||||
};
|
||||
let fields = subst.iter(Interner).map(|x| x.assert_ty_ref(Interner)).enumerate();
|
||||
let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
|
||||
for (arg, (i, ty)) in it {
|
||||
let mut p = place.clone();
|
||||
p.projections.push(ProjectionElem::TupleOrClosureField(i));
|
||||
self.consume_with_pat(p, ty.clone(), bm, *arg);
|
||||
}
|
||||
}
|
||||
Pat::Or(pats) => {
|
||||
for pat in pats.iter() {
|
||||
self.consume_with_pat(place.clone(), ty.clone(), bm, *pat);
|
||||
}
|
||||
}
|
||||
Pat::Record { args, .. } => {
|
||||
pattern_matching_dereference(&mut ty, &mut bm, &mut place);
|
||||
let subst = match ty.kind(Interner) {
|
||||
TyKind::Adt(_, s) => s,
|
||||
_ => return,
|
||||
};
|
||||
let Some(variant) = self.result.variant_resolution_for_pat(pat) else {
|
||||
return;
|
||||
};
|
||||
match variant {
|
||||
VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
|
||||
self.consume_place(place)
|
||||
}
|
||||
VariantId::StructId(s) => {
|
||||
let vd = &*self.db.struct_data(s).variant_data;
|
||||
let field_types = self.db.field_types(variant);
|
||||
for field_pat in args.iter() {
|
||||
let arg = field_pat.pat;
|
||||
let Some(local_id) = vd.field(&field_pat.name) else {
|
||||
continue;
|
||||
};
|
||||
let mut p = place.clone();
|
||||
p.projections.push(ProjectionElem::Field(FieldId {
|
||||
parent: variant.into(),
|
||||
local_id,
|
||||
}));
|
||||
self.consume_with_pat(
|
||||
p,
|
||||
field_types[local_id].clone().substitute(Interner, subst),
|
||||
bm,
|
||||
arg,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Pat::Range { .. }
|
||||
| Pat::Slice { .. }
|
||||
| Pat::ConstBlock(_)
|
||||
| Pat::Path(_)
|
||||
| Pat::Lit(_) => self.consume_place(place),
|
||||
Pat::Bind { id, subpat: _ } => {
|
||||
let mode = self.body.bindings[*id].mode;
|
||||
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
|
||||
bm = mode;
|
||||
}
|
||||
let capture_kind = match bm {
|
||||
BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
|
||||
self.consume_place(place);
|
||||
return;
|
||||
}
|
||||
BindingAnnotation::Ref => BorrowKind::Shared,
|
||||
BindingAnnotation::RefMut => BorrowKind::Mut { allow_two_phase_borrow: false },
|
||||
};
|
||||
self.add_capture(place, CaptureKind::ByRef(capture_kind));
|
||||
}
|
||||
Pat::TupleStruct { path: _, args, ellipsis } => {
|
||||
pattern_matching_dereference(&mut ty, &mut bm, &mut place);
|
||||
let subst = match ty.kind(Interner) {
|
||||
TyKind::Adt(_, s) => s,
|
||||
_ => return,
|
||||
};
|
||||
let Some(variant) = self.result.variant_resolution_for_pat(pat) else {
|
||||
return;
|
||||
};
|
||||
match variant {
|
||||
VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
|
||||
self.consume_place(place)
|
||||
}
|
||||
VariantId::StructId(s) => {
|
||||
let vd = &*self.db.struct_data(s).variant_data;
|
||||
let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
|
||||
let fields = vd.fields().iter();
|
||||
let it =
|
||||
al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
|
||||
let field_types = self.db.field_types(variant);
|
||||
for (arg, (i, _)) in it {
|
||||
let mut p = place.clone();
|
||||
p.projections.push(ProjectionElem::Field(FieldId {
|
||||
parent: variant.into(),
|
||||
local_id: i,
|
||||
}));
|
||||
self.consume_with_pat(
|
||||
p,
|
||||
field_types[i].clone().substitute(Interner, subst),
|
||||
bm,
|
||||
*arg,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Pat::Ref { pat, mutability: _ } => {
|
||||
if let Some((inner, _, _)) = ty.as_reference() {
|
||||
ty = inner.clone();
|
||||
place.projections.push(ProjectionElem::Deref);
|
||||
self.consume_with_pat(place, ty, bm, *pat)
|
||||
}
|
||||
}
|
||||
Pat::Box { .. } => (), // not supported
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_exprs(&mut self, exprs: impl Iterator<Item = ExprId>) {
|
||||
for expr in exprs {
|
||||
self.consume_expr(expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn closure_kind(&self) -> FnTrait {
|
||||
let mut r = FnTrait::Fn;
|
||||
for x in &self.current_captures {
|
||||
r = cmp::min(
|
||||
r,
|
||||
match &x.kind {
|
||||
CaptureKind::ByRef(BorrowKind::Unique | BorrowKind::Mut { .. }) => {
|
||||
FnTrait::FnMut
|
||||
}
|
||||
CaptureKind::ByRef(BorrowKind::Shallow | BorrowKind::Shared) => FnTrait::Fn,
|
||||
CaptureKind::ByValue => FnTrait::FnOnce,
|
||||
},
|
||||
)
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait {
|
||||
let (_, root) = self.db.lookup_intern_closure(closure.into());
|
||||
self.current_closure = Some(closure);
|
||||
let Expr::Closure { body, capture_by, .. } = &self.body[root] else {
|
||||
unreachable!("Closure expression id is always closure");
|
||||
};
|
||||
self.consume_expr(*body);
|
||||
for item in &self.current_captures {
|
||||
if matches!(item.kind, CaptureKind::ByRef(BorrowKind::Mut { .. })) {
|
||||
// FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in
|
||||
// MIR. I didn't do that due duplicate diagnostics.
|
||||
self.result.mutated_bindings_in_closure.insert(item.place.local);
|
||||
}
|
||||
}
|
||||
// closure_kind should be done before adjust_for_move_closure
|
||||
let closure_kind = self.closure_kind();
|
||||
match capture_by {
|
||||
CaptureBy::Value => self.adjust_for_move_closure(),
|
||||
CaptureBy::Ref => (),
|
||||
}
|
||||
self.minimize_captures();
|
||||
let result = mem::take(&mut self.current_captures);
|
||||
let captures = result.into_iter().map(|x| x.with_ty(self)).collect::<Vec<_>>();
|
||||
self.result.closure_info.insert(closure, (captures, closure_kind));
|
||||
closure_kind
|
||||
}
|
||||
|
||||
pub(crate) fn infer_closures(&mut self) {
|
||||
let deferred_closures = self.sort_closures();
|
||||
for (closure, exprs) in deferred_closures.into_iter().rev() {
|
||||
self.current_captures = vec![];
|
||||
let kind = self.analyze_closure(closure);
|
||||
|
||||
for (derefed_callee, callee_ty, params, expr) in exprs {
|
||||
if let &Expr::Call { callee, .. } = &self.body[expr] {
|
||||
let mut adjustments =
|
||||
self.result.expr_adjustments.remove(&callee).unwrap_or_default();
|
||||
self.write_fn_trait_method_resolution(
|
||||
kind,
|
||||
&derefed_callee,
|
||||
&mut adjustments,
|
||||
&callee_ty,
|
||||
¶ms,
|
||||
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));
|
||||
}
|
||||
|
@ -7,9 +7,9 @@
|
||||
|
||||
use std::{iter, sync::Arc};
|
||||
|
||||
use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyVariableKind};
|
||||
use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyKind, TyVariableKind};
|
||||
use hir_def::{
|
||||
expr::ExprId,
|
||||
hir::ExprId,
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
};
|
||||
use stdx::always;
|
||||
@ -22,7 +22,7 @@ use crate::{
|
||||
TypeError, TypeMismatch,
|
||||
},
|
||||
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner,
|
||||
Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
|
||||
Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
};
|
||||
|
||||
use super::unify::InferenceTable;
|
||||
@ -111,6 +111,8 @@ impl CoerceMany {
|
||||
// pointers to have a chance at getting a match. See
|
||||
// https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
|
||||
let sig = match (self.merged_ty().kind(Interner), expr_ty.kind(Interner)) {
|
||||
(TyKind::FnDef(x, _), TyKind::FnDef(y, _)) if x == y => None,
|
||||
(TyKind::Closure(x, _), TyKind::Closure(y, _)) if x == y => None,
|
||||
(TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => {
|
||||
// FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
|
||||
// we should be coercing the closure to a fn pointer of the safety of the FnDef
|
||||
|
@ -10,10 +10,10 @@ use chalk_ir::{
|
||||
cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyKind, TyVariableKind,
|
||||
};
|
||||
use hir_def::{
|
||||
expr::{
|
||||
generics::TypeOrConstParamData,
|
||||
hir::{
|
||||
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
|
||||
},
|
||||
generics::TypeOrConstParamData,
|
||||
lang_item::LangItem,
|
||||
path::{GenericArg, GenericArgs},
|
||||
BlockId, ConstParamId, FieldId, ItemContainerId, Lookup,
|
||||
@ -86,10 +86,10 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
|
||||
fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
|
||||
let ty = self.infer_expr_inner(expr, expected);
|
||||
// While we don't allow *arbitrary* coercions here, we *do* allow
|
||||
// coercions from ! to `expected`.
|
||||
// coercions from `!` to `expected`.
|
||||
if ty.is_never() {
|
||||
if let Some(adjustments) = self.result.expr_adjustments.get(&expr) {
|
||||
return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments {
|
||||
@ -99,13 +99,22 @@ impl<'a> InferenceContext<'a> {
|
||||
};
|
||||
}
|
||||
|
||||
let adj_ty = self.table.new_type_var();
|
||||
self.write_expr_adj(
|
||||
expr,
|
||||
vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty.clone() }],
|
||||
);
|
||||
adj_ty
|
||||
if let Some(target) = expected.only_has_type(&mut self.table) {
|
||||
self.coerce(Some(expr), &ty, &target)
|
||||
.expect("never-to-any coercion should always succeed")
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
} else {
|
||||
if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
|
||||
let could_unify = self.unify(&ty, &expected_ty);
|
||||
if !could_unify {
|
||||
self.result.type_mismatches.insert(
|
||||
expr.into(),
|
||||
TypeMismatch { expected: expected_ty, actual: ty.clone() },
|
||||
);
|
||||
}
|
||||
}
|
||||
ty
|
||||
}
|
||||
}
|
||||
@ -212,7 +221,7 @@ impl<'a> InferenceContext<'a> {
|
||||
self.diverges = Diverges::Maybe;
|
||||
TyBuilder::unit()
|
||||
}
|
||||
Expr::Closure { body, args, ret_type, arg_types, closure_kind } => {
|
||||
Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => {
|
||||
assert_eq!(args.len(), arg_types.len());
|
||||
|
||||
let mut sig_tys = Vec::with_capacity(arg_types.len() + 1);
|
||||
@ -247,7 +256,7 @@ impl<'a> InferenceContext<'a> {
|
||||
})
|
||||
.intern(Interner);
|
||||
|
||||
let (ty, resume_yield_tys) = match closure_kind {
|
||||
let (id, ty, resume_yield_tys) = match closure_kind {
|
||||
ClosureKind::Generator(_) => {
|
||||
// FIXME: report error when there are more than 1 parameter.
|
||||
let resume_ty = match sig_tys.first() {
|
||||
@ -267,7 +276,7 @@ impl<'a> InferenceContext<'a> {
|
||||
let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into();
|
||||
let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner);
|
||||
|
||||
(generator_ty, Some((resume_ty, yield_ty)))
|
||||
(None, generator_ty, Some((resume_ty, yield_ty)))
|
||||
}
|
||||
ClosureKind::Closure | ClosureKind::Async => {
|
||||
let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
|
||||
@ -276,8 +285,11 @@ impl<'a> InferenceContext<'a> {
|
||||
Substitution::from1(Interner, sig_ty.clone()),
|
||||
)
|
||||
.intern(Interner);
|
||||
|
||||
(closure_ty, None)
|
||||
self.deferred_closures.entry(closure_id).or_default();
|
||||
if let Some(c) = self.current_closure {
|
||||
self.closure_dependecies.entry(c).or_default().push(closure_id);
|
||||
}
|
||||
(Some(closure_id), closure_ty, None)
|
||||
}
|
||||
};
|
||||
|
||||
@ -293,6 +305,7 @@ impl<'a> InferenceContext<'a> {
|
||||
|
||||
// FIXME: lift these out into a struct
|
||||
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||
let prev_closure = mem::replace(&mut self.current_closure, id);
|
||||
let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
|
||||
let prev_ret_coercion =
|
||||
mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty)));
|
||||
@ -306,6 +319,7 @@ impl<'a> InferenceContext<'a> {
|
||||
self.diverges = prev_diverges;
|
||||
self.return_ty = prev_ret_ty;
|
||||
self.return_coercion = prev_ret_coercion;
|
||||
self.current_closure = prev_closure;
|
||||
self.resume_yield_tys = prev_resume_yield_tys;
|
||||
|
||||
ty
|
||||
@ -331,43 +345,28 @@ impl<'a> InferenceContext<'a> {
|
||||
let (param_tys, ret_ty) = match res {
|
||||
Some((func, params, ret_ty)) => {
|
||||
let mut adjustments = auto_deref_adjust_steps(&derefs);
|
||||
if let TyKind::Closure(c, _) =
|
||||
self.table.resolve_completely(callee_ty.clone()).kind(Interner)
|
||||
{
|
||||
if let Some(par) = self.current_closure {
|
||||
self.closure_dependecies.entry(par).or_default().push(*c);
|
||||
}
|
||||
self.deferred_closures.entry(*c).or_default().push((
|
||||
derefed_callee.clone(),
|
||||
callee_ty.clone(),
|
||||
params.clone(),
|
||||
tgt_expr,
|
||||
));
|
||||
}
|
||||
if let Some(fn_x) = func {
|
||||
match fn_x {
|
||||
FnTrait::FnOnce => (),
|
||||
FnTrait::FnMut => {
|
||||
if !matches!(
|
||||
derefed_callee.kind(Interner),
|
||||
TyKind::Ref(Mutability::Mut, _, _)
|
||||
) {
|
||||
adjustments.push(Adjustment::borrow(
|
||||
Mutability::Mut,
|
||||
derefed_callee.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
FnTrait::Fn => {
|
||||
if !matches!(
|
||||
derefed_callee.kind(Interner),
|
||||
TyKind::Ref(Mutability::Not, _, _)
|
||||
) {
|
||||
adjustments.push(Adjustment::borrow(
|
||||
Mutability::Not,
|
||||
derefed_callee.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
let trait_ = fn_x
|
||||
.get_id(self.db, self.table.trait_env.krate)
|
||||
.expect("We just used it");
|
||||
let trait_data = self.db.trait_data(trait_);
|
||||
if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
|
||||
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
|
||||
.push(callee_ty.clone())
|
||||
.push(TyBuilder::tuple_with(params.iter().cloned()))
|
||||
.build();
|
||||
self.write_method_resolution(tgt_expr, func, subst)
|
||||
}
|
||||
self.write_fn_trait_method_resolution(
|
||||
fn_x,
|
||||
&derefed_callee,
|
||||
&mut adjustments,
|
||||
&callee_ty,
|
||||
¶ms,
|
||||
tgt_expr,
|
||||
);
|
||||
}
|
||||
self.write_expr_adj(*callee, adjustments);
|
||||
(params, ret_ty)
|
||||
@ -459,8 +458,8 @@ impl<'a> InferenceContext<'a> {
|
||||
self.resolver.reset_to_guard(g);
|
||||
ty
|
||||
}
|
||||
Expr::Continue { label } => {
|
||||
if let None = find_continuable(&mut self.breakables, label.as_ref()) {
|
||||
&Expr::Continue { label } => {
|
||||
if let None = find_continuable(&mut self.breakables, label) {
|
||||
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
|
||||
expr: tgt_expr,
|
||||
is_break: false,
|
||||
@ -469,9 +468,9 @@ impl<'a> InferenceContext<'a> {
|
||||
};
|
||||
self.result.standard_types.never.clone()
|
||||
}
|
||||
Expr::Break { expr, label } => {
|
||||
let val_ty = if let Some(expr) = *expr {
|
||||
let opt_coerce_to = match find_breakable(&mut self.breakables, label.as_ref()) {
|
||||
&Expr::Break { expr, label } => {
|
||||
let val_ty = if let Some(expr) = expr {
|
||||
let opt_coerce_to = match find_breakable(&mut self.breakables, label) {
|
||||
Some(ctxt) => match &ctxt.coerce {
|
||||
Some(coerce) => coerce.expected_ty(),
|
||||
None => {
|
||||
@ -490,13 +489,13 @@ impl<'a> InferenceContext<'a> {
|
||||
TyBuilder::unit()
|
||||
};
|
||||
|
||||
match find_breakable(&mut self.breakables, label.as_ref()) {
|
||||
match find_breakable(&mut self.breakables, label) {
|
||||
Some(ctxt) => match ctxt.coerce.take() {
|
||||
Some(mut coerce) => {
|
||||
coerce.coerce(self, *expr, &val_ty);
|
||||
coerce.coerce(self, expr, &val_ty);
|
||||
|
||||
// Avoiding borrowck
|
||||
let ctxt = find_breakable(&mut self.breakables, label.as_ref())
|
||||
let ctxt = find_breakable(&mut self.breakables, label)
|
||||
.expect("breakable stack changed during coercion");
|
||||
ctxt.may_break = true;
|
||||
ctxt.coerce = Some(coerce);
|
||||
@ -897,6 +896,41 @@ impl<'a> InferenceContext<'a> {
|
||||
TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)).intern(Interner)
|
||||
}
|
||||
|
||||
pub(crate) fn write_fn_trait_method_resolution(
|
||||
&mut self,
|
||||
fn_x: FnTrait,
|
||||
derefed_callee: &Ty,
|
||||
adjustments: &mut Vec<Adjustment>,
|
||||
callee_ty: &Ty,
|
||||
params: &Vec<Ty>,
|
||||
tgt_expr: ExprId,
|
||||
) {
|
||||
match fn_x {
|
||||
FnTrait::FnOnce => (),
|
||||
FnTrait::FnMut => {
|
||||
if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Mut, _, _)) {
|
||||
adjustments.push(Adjustment::borrow(Mutability::Mut, derefed_callee.clone()));
|
||||
}
|
||||
}
|
||||
FnTrait::Fn => {
|
||||
if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Not, _, _)) {
|
||||
adjustments.push(Adjustment::borrow(Mutability::Not, derefed_callee.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some(trait_) = fn_x.get_id(self.db, self.table.trait_env.krate) else {
|
||||
return;
|
||||
};
|
||||
let trait_data = self.db.trait_data(trait_);
|
||||
if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
|
||||
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
|
||||
.push(callee_ty.clone())
|
||||
.push(TyBuilder::tuple_with(params.iter().cloned()))
|
||||
.build();
|
||||
self.write_method_resolution(tgt_expr, func, subst.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_expr_array(
|
||||
&mut self,
|
||||
array: &Array,
|
||||
@ -1900,7 +1934,6 @@ impl<'a> InferenceContext<'a> {
|
||||
cb: impl FnOnce(&mut Self) -> T,
|
||||
) -> (Option<Ty>, T) {
|
||||
self.breakables.push({
|
||||
let label = label.map(|label| self.body[label].name.clone());
|
||||
BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label }
|
||||
});
|
||||
let res = cb(self);
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
use chalk_ir::Mutability;
|
||||
use hir_def::{
|
||||
expr::{Array, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
|
||||
hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
|
||||
lang_item::LangItem,
|
||||
};
|
||||
use hir_expand::name;
|
||||
@ -80,6 +80,9 @@ impl<'a> InferenceContext<'a> {
|
||||
self.infer_mut_expr(*expr, m);
|
||||
for arm in arms.iter() {
|
||||
self.infer_mut_expr(arm.expr, Mutability::Not);
|
||||
if let Some(g) = arm.guard {
|
||||
self.infer_mut_expr(g, Mutability::Not);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Yield { expr }
|
||||
@ -158,14 +161,19 @@ impl<'a> InferenceContext<'a> {
|
||||
let mutability = lower_to_chalk_mutability(*mutability);
|
||||
self.infer_mut_expr(*expr, mutability);
|
||||
}
|
||||
Expr::BinaryOp { lhs, rhs, op: Some(BinaryOp::Assignment { .. }) } => {
|
||||
self.infer_mut_expr(*lhs, Mutability::Mut);
|
||||
self.infer_mut_expr(*rhs, Mutability::Not);
|
||||
}
|
||||
Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs })
|
||||
| Expr::BinaryOp { lhs, rhs, op: _ }
|
||||
| Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => {
|
||||
self.infer_mut_expr(*lhs, Mutability::Not);
|
||||
self.infer_mut_expr(*rhs, Mutability::Not);
|
||||
}
|
||||
// not implemented
|
||||
Expr::Closure { .. } => (),
|
||||
Expr::Closure { body, .. } => {
|
||||
self.infer_mut_expr(*body, Mutability::Not);
|
||||
}
|
||||
Expr::Tuple { exprs, is_assignee_expr: _ }
|
||||
| Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) => {
|
||||
self.infer_mut_not_expr_iter(exprs.iter().copied());
|
||||
|
@ -5,7 +5,7 @@ use std::iter::repeat_with;
|
||||
use chalk_ir::Mutability;
|
||||
use hir_def::{
|
||||
body::Body,
|
||||
expr::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
|
||||
hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
|
||||
path::Path,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
@ -6,8 +6,8 @@ use chalk_ir::{
|
||||
DebruijnIndex,
|
||||
};
|
||||
use hir_def::{
|
||||
adt::VariantData, attr::Attrs, visibility::Visibility, AdtId, EnumVariantId, HasModule, Lookup,
|
||||
ModuleId, VariantId,
|
||||
attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule,
|
||||
Lookup, ModuleId, VariantId,
|
||||
};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Functions to detect special lang items
|
||||
|
||||
use hir_def::{adt::StructFlags, lang_item::LangItem, AdtId};
|
||||
use hir_def::{data::adt::StructFlags, lang_item::LangItem, AdtId};
|
||||
use hir_expand::name::Name;
|
||||
|
||||
use crate::db::HirDatabase;
|
||||
|
@ -229,11 +229,24 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
|
||||
}
|
||||
}
|
||||
}
|
||||
TyKind::Closure(_, _) | TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
|
||||
TyKind::Closure(c, _) => {
|
||||
let (def, _) = db.lookup_intern_closure((*c).into());
|
||||
let infer = db.infer(def);
|
||||
let (captures, _) = infer.closure_info(c);
|
||||
let fields = captures
|
||||
.iter()
|
||||
.map(|x| layout_of_ty(db, &x.ty, krate))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let fields = fields.iter().collect::<Vec<_>>();
|
||||
let fields = fields.iter().collect::<Vec<_>>();
|
||||
cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized)
|
||||
.ok_or(LayoutError::Unknown)?
|
||||
}
|
||||
TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
|
||||
return Err(LayoutError::NotImplemented)
|
||||
}
|
||||
TyKind::Error => return Err(LayoutError::HasErrorType),
|
||||
TyKind::AssociatedType(_, _)
|
||||
| TyKind::Error
|
||||
| TyKind::Alias(_)
|
||||
| TyKind::Placeholder(_)
|
||||
| TyKind::BoundVar(_)
|
||||
|
@ -3,7 +3,7 @@
|
||||
use std::ops::Bound;
|
||||
|
||||
use hir_def::{
|
||||
adt::VariantData,
|
||||
data::adt::VariantData,
|
||||
layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
|
||||
AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId,
|
||||
};
|
||||
|
@ -11,6 +11,8 @@ use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution};
|
||||
|
||||
use super::layout_of_ty;
|
||||
|
||||
mod closure;
|
||||
|
||||
fn current_machine_data_layout() -> String {
|
||||
project_model::target_data_layout::get(None, None, &HashMap::default()).unwrap()
|
||||
}
|
||||
@ -81,8 +83,8 @@ fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64)
|
||||
#[track_caller]
|
||||
fn check_size_and_align_expr(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
|
||||
let l = eval_expr(ra_fixture, minicore).unwrap();
|
||||
assert_eq!(l.size.bytes(), size);
|
||||
assert_eq!(l.align.abi.bytes(), align);
|
||||
assert_eq!(l.size.bytes(), size, "size mismatch");
|
||||
assert_eq!(l.align.abi.bytes(), align, "align mismatch");
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
@ -118,13 +120,31 @@ macro_rules! size_and_align {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! size_and_align_expr {
|
||||
(minicore: $($x:tt),*; stmts: [$($s:tt)*] $($t:tt)*) => {
|
||||
{
|
||||
#[allow(dead_code)]
|
||||
#[allow(unused_must_use)]
|
||||
#[allow(path_statements)]
|
||||
{
|
||||
$($s)*
|
||||
let val = { $($t)* };
|
||||
$crate::layout::tests::check_size_and_align_expr(
|
||||
&format!("{{ {} let val = {{ {} }}; val }}", stringify!($($s)*), stringify!($($t)*)),
|
||||
&format!("//- minicore: {}\n", stringify!($($x),*)),
|
||||
::std::mem::size_of_val(&val) as u64,
|
||||
::std::mem::align_of_val(&val) as u64,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
($($t:tt)*) => {
|
||||
{
|
||||
#[allow(dead_code)]
|
||||
{
|
||||
let val = { $($t)* };
|
||||
check_size_and_align_expr(
|
||||
$crate::layout::tests::check_size_and_align_expr(
|
||||
stringify!($($t)*),
|
||||
"",
|
||||
::std::mem::size_of_val(&val) as u64,
|
||||
@ -212,6 +232,45 @@ fn return_position_impl_trait() {
|
||||
fn foo() -> (impl T, impl T, impl T) { (2i64, 5i32, 7i32) }
|
||||
foo()
|
||||
}
|
||||
size_and_align_expr! {
|
||||
minicore: iterators;
|
||||
stmts: []
|
||||
trait Tr {}
|
||||
impl Tr for i32 {}
|
||||
fn foo() -> impl Iterator<Item = impl Tr> {
|
||||
[1, 2, 3].into_iter()
|
||||
}
|
||||
let mut iter = foo();
|
||||
let item = iter.next();
|
||||
(iter, item)
|
||||
}
|
||||
size_and_align_expr! {
|
||||
minicore: future;
|
||||
stmts: []
|
||||
use core::{future::Future, task::{Poll, Context}, pin::pin};
|
||||
use std::{task::Wake, sync::Arc};
|
||||
trait Tr {}
|
||||
impl Tr for i32 {}
|
||||
async fn f() -> impl Tr {
|
||||
2
|
||||
}
|
||||
fn unwrap_fut<T>(inp: impl Future<Output = T>) -> Poll<T> {
|
||||
// In a normal test we could use `loop {}` or `panic!()` here,
|
||||
// but rustc actually runs this code.
|
||||
let pinned = pin!(inp);
|
||||
struct EmptyWaker;
|
||||
impl Wake for EmptyWaker {
|
||||
fn wake(self: Arc<Self>) {
|
||||
}
|
||||
}
|
||||
let waker = Arc::new(EmptyWaker).into();
|
||||
let mut context = Context::from_waker(&waker);
|
||||
let x = pinned.poll(&mut context);
|
||||
x
|
||||
}
|
||||
let x = unwrap_fut(f());
|
||||
x
|
||||
}
|
||||
size_and_align_expr! {
|
||||
struct Foo<T>(T, T, (T, T));
|
||||
trait T {}
|
||||
|
175
crates/hir-ty/src/layout/tests/closure.rs
Normal file
175
crates/hir-ty/src/layout/tests/closure.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -44,7 +44,7 @@ use chalk_ir::{
|
||||
NoSolution, TyData,
|
||||
};
|
||||
use either::Either;
|
||||
use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
|
||||
use hir_def::{hir::ExprId, type_ref::Rawness, TypeOrConstParamId};
|
||||
use hir_expand::name;
|
||||
use la_arena::{Arena, Idx};
|
||||
use mir::MirEvalError;
|
||||
|
@ -18,9 +18,9 @@ use chalk_ir::{
|
||||
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
adt::StructKind,
|
||||
body::{Expander, LowerCtx},
|
||||
body::Expander,
|
||||
builtin_type::BuiltinType,
|
||||
data::adt::StructKind,
|
||||
generics::{
|
||||
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
|
||||
},
|
||||
@ -380,7 +380,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
let macro_call = macro_call.to_node(self.db.upcast());
|
||||
match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
|
||||
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
|
||||
let ctx = LowerCtx::new(self.db.upcast(), expander.current_file_id());
|
||||
let ctx = expander.ctx(self.db.upcast());
|
||||
let type_ref = TypeRef::from_ast(&ctx, expanded);
|
||||
|
||||
drop(expander);
|
||||
@ -988,7 +988,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
// ignore `T: Drop` or `T: Destruct` bounds.
|
||||
// - `T: ~const Drop` has a special meaning in Rust 1.61 that we don't implement.
|
||||
// (So ideally, we'd only ignore `~const Drop` here)
|
||||
// - `Destruct` impls are built-in in 1.62 (current nightlies as of 08-04-2022), so until
|
||||
// - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until
|
||||
// the builtin impls are supported by Chalk, we ignore them here.
|
||||
if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) {
|
||||
if matches!(lang, LangItem::Drop | LangItem::Destruct) {
|
||||
@ -1082,23 +1082,23 @@ impl<'a> TyLoweringContext<'a> {
|
||||
associated_ty_id: to_assoc_type_id(associated_ty),
|
||||
substitution,
|
||||
};
|
||||
let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity(
|
||||
let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
|
||||
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
|
||||
);
|
||||
if let Some(type_ref) = &binding.type_ref {
|
||||
let ty = self.lower_ty(type_ref);
|
||||
let alias_eq =
|
||||
AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
|
||||
preds.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
|
||||
predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
|
||||
}
|
||||
for bound in binding.bounds.iter() {
|
||||
preds.extend(self.lower_type_bound(
|
||||
predicates.extend(self.lower_type_bound(
|
||||
bound,
|
||||
TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
|
||||
false,
|
||||
));
|
||||
}
|
||||
preds
|
||||
predicates
|
||||
})
|
||||
}
|
||||
|
||||
@ -1165,7 +1165,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
return None;
|
||||
}
|
||||
|
||||
// As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
|
||||
// As multiple occurrences of the same auto traits *are* permitted, we deduplicate the
|
||||
// bounds. We shouldn't have repeated elements besides auto traits at this point.
|
||||
bounds.dedup();
|
||||
|
||||
@ -1634,7 +1634,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
|
||||
let ctx_params = TyLoweringContext::new(db, &resolver)
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Variable)
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
let params = data.params.iter().map(|(_, tr)| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
|
||||
let params = data.params.iter().map(|tr| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
|
||||
let ctx_ret = TyLoweringContext::new(db, &resolver)
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
|
@ -7,9 +7,11 @@ use std::{ops::ControlFlow, sync::Arc};
|
||||
use base_db::{CrateId, Edition};
|
||||
use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause};
|
||||
use hir_def::{
|
||||
adt::StructFlags, data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId,
|
||||
ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId,
|
||||
TraitId,
|
||||
data::{adt::StructFlags, ImplData},
|
||||
item_scope::ItemScope,
|
||||
nameres::DefMap,
|
||||
AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup,
|
||||
ModuleDefId, ModuleId, TraitId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
@ -952,7 +954,14 @@ fn iterate_method_candidates_with_autoref(
|
||||
)
|
||||
};
|
||||
|
||||
iterate_method_candidates_by_receiver(receiver_ty, first_adjustment.clone())?;
|
||||
let mut maybe_reborrowed = first_adjustment.clone();
|
||||
if let Some((_, _, m)) = receiver_ty.value.as_reference() {
|
||||
// Prefer reborrow of references to move
|
||||
maybe_reborrowed.autoref = Some(m);
|
||||
maybe_reborrowed.autoderefs += 1;
|
||||
}
|
||||
|
||||
iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?;
|
||||
|
||||
let refed = Canonical {
|
||||
value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone())
|
||||
|
@ -3,11 +3,12 @@
|
||||
use std::{fmt::Display, iter};
|
||||
|
||||
use crate::{
|
||||
infer::PointerCast, Const, ConstScalar, InferenceResult, Interner, MemoryMap, Substitution, Ty,
|
||||
db::HirDatabase, infer::PointerCast, ClosureId, Const, ConstScalar, InferenceResult, Interner,
|
||||
MemoryMap, Substitution, Ty, TyKind,
|
||||
};
|
||||
use chalk_ir::Mutability;
|
||||
use hir_def::{
|
||||
expr::{BindingId, Expr, ExprId, Ordering, PatId},
|
||||
hir::{BindingId, Expr, ExprId, Ordering, PatId},
|
||||
DefWithBodyId, FieldId, UnionId, VariantId,
|
||||
};
|
||||
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
|
||||
@ -19,9 +20,11 @@ mod pretty;
|
||||
|
||||
pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
|
||||
pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError};
|
||||
pub use lower::{lower_to_mir, mir_body_query, mir_body_recover, MirLowerError};
|
||||
pub use lower::{
|
||||
lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
|
||||
};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use stdx::impl_from;
|
||||
use stdx::{impl_from, never};
|
||||
|
||||
use super::consteval::{intern_const_scalar, try_const_usize};
|
||||
|
||||
@ -89,11 +92,12 @@ impl Operand {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ProjectionElem<V, T> {
|
||||
Deref,
|
||||
Field(FieldId),
|
||||
TupleField(usize),
|
||||
// FIXME: get rid of this, and use FieldId for tuples and closures
|
||||
TupleOrClosureField(usize),
|
||||
Index(V),
|
||||
ConstantIndex { offset: u64, min_length: u64, from_end: bool },
|
||||
Subslice { from: u64, to: u64, from_end: bool },
|
||||
@ -101,6 +105,63 @@ pub enum ProjectionElem<V, T> {
|
||||
OpaqueCast(T),
|
||||
}
|
||||
|
||||
impl<V, T> ProjectionElem<V, T> {
|
||||
pub fn projected_ty(
|
||||
&self,
|
||||
base: Ty,
|
||||
db: &dyn HirDatabase,
|
||||
closure_field: impl FnOnce(ClosureId, usize) -> Ty,
|
||||
) -> Ty {
|
||||
match self {
|
||||
ProjectionElem::Deref => match &base.data(Interner).kind {
|
||||
TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
|
||||
_ => {
|
||||
never!("Overloaded deref is not a projection");
|
||||
return TyKind::Error.intern(Interner);
|
||||
}
|
||||
},
|
||||
ProjectionElem::Field(f) => match &base.data(Interner).kind {
|
||||
TyKind::Adt(_, subst) => {
|
||||
db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
|
||||
}
|
||||
_ => {
|
||||
never!("Only adt has field");
|
||||
return TyKind::Error.intern(Interner);
|
||||
}
|
||||
},
|
||||
ProjectionElem::TupleOrClosureField(f) => match &base.data(Interner).kind {
|
||||
TyKind::Tuple(_, subst) => subst
|
||||
.as_slice(Interner)
|
||||
.get(*f)
|
||||
.map(|x| x.assert_ty_ref(Interner))
|
||||
.cloned()
|
||||
.unwrap_or_else(|| {
|
||||
never!("Out of bound tuple field");
|
||||
TyKind::Error.intern(Interner)
|
||||
}),
|
||||
TyKind::Closure(id, _) => closure_field(*id, *f),
|
||||
_ => {
|
||||
never!("Only tuple or closure has tuple or closure field");
|
||||
return TyKind::Error.intern(Interner);
|
||||
}
|
||||
},
|
||||
ProjectionElem::Index(_) => match &base.data(Interner).kind {
|
||||
TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(),
|
||||
_ => {
|
||||
never!("Overloaded index is not a projection");
|
||||
return TyKind::Error.intern(Interner);
|
||||
}
|
||||
},
|
||||
ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::OpaqueCast(_) => {
|
||||
never!("We don't emit these yet");
|
||||
return TyKind::Error.intern(Interner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type PlaceElem = ProjectionElem<LocalId, Ty>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -123,7 +184,7 @@ pub enum AggregateKind {
|
||||
Tuple(Ty),
|
||||
Adt(VariantId, Substitution),
|
||||
Union(UnionId, FieldId),
|
||||
//Closure(LocalDefId, SubstsRef),
|
||||
Closure(Ty),
|
||||
//Generator(LocalDefId, SubstsRef, Movability),
|
||||
}
|
||||
|
||||
@ -418,7 +479,7 @@ pub enum Terminator {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
|
||||
pub enum BorrowKind {
|
||||
/// Data must be immutable and is aliasable.
|
||||
Shared,
|
||||
@ -588,32 +649,32 @@ impl Display for BinOp {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<hir_def::expr::ArithOp> for BinOp {
|
||||
fn from(value: hir_def::expr::ArithOp) -> Self {
|
||||
impl From<hir_def::hir::ArithOp> for BinOp {
|
||||
fn from(value: hir_def::hir::ArithOp) -> Self {
|
||||
match value {
|
||||
hir_def::expr::ArithOp::Add => BinOp::Add,
|
||||
hir_def::expr::ArithOp::Mul => BinOp::Mul,
|
||||
hir_def::expr::ArithOp::Sub => BinOp::Sub,
|
||||
hir_def::expr::ArithOp::Div => BinOp::Div,
|
||||
hir_def::expr::ArithOp::Rem => BinOp::Rem,
|
||||
hir_def::expr::ArithOp::Shl => BinOp::Shl,
|
||||
hir_def::expr::ArithOp::Shr => BinOp::Shr,
|
||||
hir_def::expr::ArithOp::BitXor => BinOp::BitXor,
|
||||
hir_def::expr::ArithOp::BitOr => BinOp::BitOr,
|
||||
hir_def::expr::ArithOp::BitAnd => BinOp::BitAnd,
|
||||
hir_def::hir::ArithOp::Add => BinOp::Add,
|
||||
hir_def::hir::ArithOp::Mul => BinOp::Mul,
|
||||
hir_def::hir::ArithOp::Sub => BinOp::Sub,
|
||||
hir_def::hir::ArithOp::Div => BinOp::Div,
|
||||
hir_def::hir::ArithOp::Rem => BinOp::Rem,
|
||||
hir_def::hir::ArithOp::Shl => BinOp::Shl,
|
||||
hir_def::hir::ArithOp::Shr => BinOp::Shr,
|
||||
hir_def::hir::ArithOp::BitXor => BinOp::BitXor,
|
||||
hir_def::hir::ArithOp::BitOr => BinOp::BitOr,
|
||||
hir_def::hir::ArithOp::BitAnd => BinOp::BitAnd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<hir_def::expr::CmpOp> for BinOp {
|
||||
fn from(value: hir_def::expr::CmpOp) -> Self {
|
||||
impl From<hir_def::hir::CmpOp> for BinOp {
|
||||
fn from(value: hir_def::hir::CmpOp) -> Self {
|
||||
match value {
|
||||
hir_def::expr::CmpOp::Eq { negated: false } => BinOp::Eq,
|
||||
hir_def::expr::CmpOp::Eq { negated: true } => BinOp::Ne,
|
||||
hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
|
||||
hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
|
||||
hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
|
||||
hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
|
||||
hir_def::hir::CmpOp::Eq { negated: false } => BinOp::Eq,
|
||||
hir_def::hir::CmpOp::Eq { negated: true } => BinOp::Ne,
|
||||
hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
|
||||
hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
|
||||
hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
|
||||
hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -847,6 +908,87 @@ pub struct MirBody {
|
||||
pub arg_count: usize,
|
||||
pub binding_locals: ArenaMap<BindingId, LocalId>,
|
||||
pub param_locals: Vec<LocalId>,
|
||||
/// This field stores the closures directly owned by this body. It is used
|
||||
/// in traversing every mir body.
|
||||
pub closures: Vec<ClosureId>,
|
||||
}
|
||||
|
||||
impl MirBody {
|
||||
fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) {
|
||||
fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) {
|
||||
match op {
|
||||
Operand::Copy(p) | Operand::Move(p) => {
|
||||
f(p);
|
||||
}
|
||||
Operand::Constant(_) => (),
|
||||
}
|
||||
}
|
||||
for (_, block) in self.basic_blocks.iter_mut() {
|
||||
for statement in &mut block.statements {
|
||||
match &mut statement.kind {
|
||||
StatementKind::Assign(p, r) => {
|
||||
f(p);
|
||||
match r {
|
||||
Rvalue::ShallowInitBox(o, _)
|
||||
| Rvalue::UnaryOp(_, o)
|
||||
| Rvalue::Cast(_, o, _)
|
||||
| Rvalue::Use(o) => for_operand(o, &mut f),
|
||||
Rvalue::CopyForDeref(p)
|
||||
| Rvalue::Discriminant(p)
|
||||
| Rvalue::Len(p)
|
||||
| Rvalue::Ref(_, p) => f(p),
|
||||
Rvalue::CheckedBinaryOp(_, o1, o2) => {
|
||||
for_operand(o1, &mut f);
|
||||
for_operand(o2, &mut f);
|
||||
}
|
||||
Rvalue::Aggregate(_, ops) => {
|
||||
for op in ops {
|
||||
for_operand(op, &mut f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
StatementKind::Deinit(p) => f(p),
|
||||
StatementKind::StorageLive(_)
|
||||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Nop => (),
|
||||
}
|
||||
}
|
||||
match &mut block.terminator {
|
||||
Some(x) => match x {
|
||||
Terminator::SwitchInt { discr, .. } => for_operand(discr, &mut f),
|
||||
Terminator::FalseEdge { .. }
|
||||
| Terminator::FalseUnwind { .. }
|
||||
| Terminator::Goto { .. }
|
||||
| Terminator::Resume
|
||||
| Terminator::GeneratorDrop
|
||||
| Terminator::Abort
|
||||
| Terminator::Return
|
||||
| Terminator::Unreachable => (),
|
||||
Terminator::Drop { place, .. } => {
|
||||
f(place);
|
||||
}
|
||||
Terminator::DropAndReplace { place, value, .. } => {
|
||||
f(place);
|
||||
for_operand(value, &mut f);
|
||||
}
|
||||
Terminator::Call { func, args, destination, .. } => {
|
||||
for_operand(func, &mut f);
|
||||
args.iter_mut().for_each(|x| for_operand(x, &mut f));
|
||||
f(destination);
|
||||
}
|
||||
Terminator::Assert { cond, .. } => {
|
||||
for_operand(cond, &mut f);
|
||||
}
|
||||
Terminator::Yield { value, resume_arg, .. } => {
|
||||
for_operand(value, &mut f);
|
||||
f(resume_arg);
|
||||
}
|
||||
},
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn const_as_usize(c: &Const) -> usize {
|
||||
|
@ -3,13 +3,13 @@
|
||||
// Currently it is an ad-hoc implementation, only useful for mutability analysis. Feel free to remove all of these
|
||||
// if needed for implementing a proper borrow checker.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{iter, sync::Arc};
|
||||
|
||||
use hir_def::DefWithBodyId;
|
||||
use la_arena::ArenaMap;
|
||||
use stdx::never;
|
||||
|
||||
use crate::db::HirDatabase;
|
||||
use crate::{db::HirDatabase, ClosureId};
|
||||
|
||||
use super::{
|
||||
BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
|
||||
@ -29,14 +29,48 @@ pub struct BorrowckResult {
|
||||
pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
|
||||
}
|
||||
|
||||
fn all_mir_bodies(
|
||||
db: &dyn HirDatabase,
|
||||
def: DefWithBodyId,
|
||||
) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
|
||||
fn for_closure(
|
||||
db: &dyn HirDatabase,
|
||||
c: ClosureId,
|
||||
) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
|
||||
match db.mir_body_for_closure(c) {
|
||||
Ok(body) => {
|
||||
let closures = body.closures.clone();
|
||||
Box::new(
|
||||
iter::once(Ok(body))
|
||||
.chain(closures.into_iter().flat_map(|x| for_closure(db, x))),
|
||||
)
|
||||
}
|
||||
Err(e) => Box::new(iter::once(Err(e))),
|
||||
}
|
||||
}
|
||||
match db.mir_body(def) {
|
||||
Ok(body) => {
|
||||
let closures = body.closures.clone();
|
||||
Box::new(
|
||||
iter::once(Ok(body)).chain(closures.into_iter().flat_map(|x| for_closure(db, x))),
|
||||
)
|
||||
}
|
||||
Err(e) => Box::new(iter::once(Err(e))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrowck_query(
|
||||
db: &dyn HirDatabase,
|
||||
def: DefWithBodyId,
|
||||
) -> Result<Arc<BorrowckResult>, MirLowerError> {
|
||||
) -> Result<Arc<[BorrowckResult]>, MirLowerError> {
|
||||
let _p = profile::span("borrowck_query");
|
||||
let body = db.mir_body(def)?;
|
||||
let r = BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body };
|
||||
Ok(Arc::new(r))
|
||||
let r = all_mir_bodies(db, def)
|
||||
.map(|body| {
|
||||
let body = body?;
|
||||
Ok(BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body })
|
||||
})
|
||||
.collect::<Result<Vec<_>, MirLowerError>>()?;
|
||||
Ok(r.into())
|
||||
}
|
||||
|
||||
fn is_place_direct(lvalue: &Place) -> bool {
|
||||
@ -60,7 +94,7 @@ fn place_case(lvalue: &Place) -> ProjectionCase {
|
||||
ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::Field(_)
|
||||
| ProjectionElem::TupleField(_)
|
||||
| ProjectionElem::TupleOrClosureField(_)
|
||||
| ProjectionElem::Index(_) => {
|
||||
is_part_of = true;
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ use crate::{
|
||||
mapping::from_chalk,
|
||||
method_resolution::{is_dyn_method, lookup_impl_method},
|
||||
traits::FnTrait,
|
||||
CallableDefId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, Substitution,
|
||||
TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
|
||||
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -92,6 +92,7 @@ pub struct Evaluator<'a> {
|
||||
enum Address {
|
||||
Stack(usize),
|
||||
Heap(usize),
|
||||
Invalid(usize),
|
||||
}
|
||||
|
||||
use Address::*;
|
||||
@ -169,8 +170,10 @@ impl Address {
|
||||
fn from_usize(x: usize) -> Self {
|
||||
if x > usize::MAX / 2 {
|
||||
Stack(x - usize::MAX / 2)
|
||||
} else if x > usize::MAX / 4 {
|
||||
Heap(x - usize::MAX / 4)
|
||||
} else {
|
||||
Heap(x)
|
||||
Invalid(x)
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +184,8 @@ impl Address {
|
||||
fn to_usize(&self) -> usize {
|
||||
let as_num = match self {
|
||||
Stack(x) => *x + usize::MAX / 2,
|
||||
Heap(x) => *x,
|
||||
Heap(x) => *x + usize::MAX / 4,
|
||||
Invalid(x) => *x,
|
||||
};
|
||||
as_num
|
||||
}
|
||||
@ -190,6 +194,7 @@ impl Address {
|
||||
match self {
|
||||
Stack(x) => Stack(f(*x)),
|
||||
Heap(x) => Heap(f(*x)),
|
||||
Invalid(x) => Invalid(f(*x)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,6 +214,7 @@ pub enum MirEvalError {
|
||||
UndefinedBehavior(&'static str),
|
||||
Panic(String),
|
||||
MirLowerError(FunctionId, MirLowerError),
|
||||
MirLowerErrorForClosure(ClosureId, MirLowerError),
|
||||
TypeIsUnsized(Ty, &'static str),
|
||||
NotSupported(String),
|
||||
InvalidConst(Const),
|
||||
@ -238,6 +244,9 @@ impl std::fmt::Debug for MirEvalError {
|
||||
Self::MirLowerError(arg0, arg1) => {
|
||||
f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
|
||||
}
|
||||
Self::MirLowerErrorForClosure(arg0, arg1) => {
|
||||
f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
|
||||
}
|
||||
Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
|
||||
Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
|
||||
Self::InvalidConst(arg0) => {
|
||||
@ -355,16 +364,15 @@ impl Evaluator<'_> {
|
||||
self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?;
|
||||
let mut metadata = None; // locals are always sized
|
||||
for proj in &p.projection {
|
||||
let prev_ty = ty.clone();
|
||||
ty = proj.projected_ty(ty, self.db, |c, f| {
|
||||
let (def, _) = self.db.lookup_intern_closure(c.into());
|
||||
let infer = self.db.infer(def);
|
||||
let (captures, _) = infer.closure_info(&c);
|
||||
captures.get(f).expect("broken closure field").ty.clone()
|
||||
});
|
||||
match proj {
|
||||
ProjectionElem::Deref => {
|
||||
ty = match &ty.data(Interner).kind {
|
||||
TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
|
||||
_ => {
|
||||
return Err(MirEvalError::TypeError(
|
||||
"Overloaded deref in MIR is disallowed",
|
||||
))
|
||||
}
|
||||
};
|
||||
metadata = if self.size_of(&ty, locals)?.is_none() {
|
||||
Some(Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() })
|
||||
} else {
|
||||
@ -377,78 +385,41 @@ impl Evaluator<'_> {
|
||||
let offset =
|
||||
from_bytes!(usize, self.read_memory(locals.ptr[*op], self.ptr_size())?);
|
||||
metadata = None; // Result of index is always sized
|
||||
match &ty.data(Interner).kind {
|
||||
TyKind::Ref(_, _, inner) => match &inner.data(Interner).kind {
|
||||
TyKind::Slice(inner) => {
|
||||
ty = inner.clone();
|
||||
let ty_size = self.size_of_sized(
|
||||
&ty,
|
||||
locals,
|
||||
"slice inner type should be sized",
|
||||
)?;
|
||||
let value = self.read_memory(addr, self.ptr_size() * 2)?;
|
||||
addr = Address::from_bytes(&value[0..8])?.offset(ty_size * offset);
|
||||
}
|
||||
x => not_supported!("MIR index for ref type {x:?}"),
|
||||
},
|
||||
TyKind::Array(inner, _) | TyKind::Slice(inner) => {
|
||||
ty = inner.clone();
|
||||
let ty_size = self.size_of_sized(
|
||||
&ty,
|
||||
locals,
|
||||
"array inner type should be sized",
|
||||
)?;
|
||||
addr = addr.offset(ty_size * offset);
|
||||
}
|
||||
x => not_supported!("MIR index for type {x:?}"),
|
||||
}
|
||||
let ty_size =
|
||||
self.size_of_sized(&ty, locals, "array inner type should be sized")?;
|
||||
addr = addr.offset(ty_size * offset);
|
||||
}
|
||||
&ProjectionElem::TupleOrClosureField(f) => {
|
||||
let layout = self.layout(&prev_ty)?;
|
||||
let offset = layout.fields.offset(f).bytes_usize();
|
||||
addr = addr.offset(offset);
|
||||
metadata = None; // tuple field is always sized
|
||||
}
|
||||
ProjectionElem::Field(f) => {
|
||||
let layout = self.layout(&prev_ty)?;
|
||||
let variant_layout = match &layout.variants {
|
||||
Variants::Single { .. } => &layout,
|
||||
Variants::Multiple { variants, .. } => {
|
||||
&variants[match f.parent {
|
||||
hir_def::VariantId::EnumVariantId(x) => {
|
||||
RustcEnumVariantIdx(x.local_id)
|
||||
}
|
||||
_ => {
|
||||
return Err(MirEvalError::TypeError(
|
||||
"Multivariant layout only happens for enums",
|
||||
))
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
let offset = variant_layout
|
||||
.fields
|
||||
.offset(u32::from(f.local_id.into_raw()) as usize)
|
||||
.bytes_usize();
|
||||
addr = addr.offset(offset);
|
||||
// FIXME: support structs with unsized fields
|
||||
metadata = None;
|
||||
}
|
||||
&ProjectionElem::TupleField(f) => match &ty.data(Interner).kind {
|
||||
TyKind::Tuple(_, subst) => {
|
||||
let layout = self.layout(&ty)?;
|
||||
ty = subst
|
||||
.as_slice(Interner)
|
||||
.get(f)
|
||||
.ok_or(MirEvalError::TypeError("not enough tuple fields"))?
|
||||
.assert_ty_ref(Interner)
|
||||
.clone();
|
||||
let offset = layout.fields.offset(f).bytes_usize();
|
||||
addr = addr.offset(offset);
|
||||
metadata = None; // tuple field is always sized
|
||||
}
|
||||
_ => return Err(MirEvalError::TypeError("Only tuple has tuple fields")),
|
||||
},
|
||||
ProjectionElem::Field(f) => match &ty.data(Interner).kind {
|
||||
TyKind::Adt(adt, subst) => {
|
||||
let layout = self.layout_adt(adt.0, subst.clone())?;
|
||||
let variant_layout = match &layout.variants {
|
||||
Variants::Single { .. } => &layout,
|
||||
Variants::Multiple { variants, .. } => {
|
||||
&variants[match f.parent {
|
||||
hir_def::VariantId::EnumVariantId(x) => {
|
||||
RustcEnumVariantIdx(x.local_id)
|
||||
}
|
||||
_ => {
|
||||
return Err(MirEvalError::TypeError(
|
||||
"Multivariant layout only happens for enums",
|
||||
))
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
ty = self.db.field_types(f.parent)[f.local_id]
|
||||
.clone()
|
||||
.substitute(Interner, subst);
|
||||
let offset = variant_layout
|
||||
.fields
|
||||
.offset(u32::from(f.local_id.into_raw()) as usize)
|
||||
.bytes_usize();
|
||||
addr = addr.offset(offset);
|
||||
// FIXME: support structs with unsized fields
|
||||
metadata = None;
|
||||
}
|
||||
_ => return Err(MirEvalError::TypeError("Only adt has fields")),
|
||||
},
|
||||
ProjectionElem::ConstantIndex { .. } => {
|
||||
not_supported!("constant index")
|
||||
}
|
||||
@ -845,6 +816,15 @@ impl Evaluator<'_> {
|
||||
values.iter().copied(),
|
||||
)?)
|
||||
}
|
||||
AggregateKind::Closure(ty) => {
|
||||
let layout = self.layout(&ty)?;
|
||||
Owned(self.make_by_layout(
|
||||
layout.size.bytes_usize(),
|
||||
&layout,
|
||||
None,
|
||||
values.iter().copied(),
|
||||
)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
Rvalue::Cast(kind, operand, target_ty) => match kind {
|
||||
@ -1065,6 +1045,9 @@ impl Evaluator<'_> {
|
||||
let (mem, pos) = match addr {
|
||||
Stack(x) => (&self.stack, x),
|
||||
Heap(x) => (&self.heap, x),
|
||||
Invalid(_) => {
|
||||
return Err(MirEvalError::UndefinedBehavior("read invalid memory address"))
|
||||
}
|
||||
};
|
||||
mem.get(pos..pos + size).ok_or(MirEvalError::UndefinedBehavior("out of bound memory read"))
|
||||
}
|
||||
@ -1073,6 +1056,9 @@ impl Evaluator<'_> {
|
||||
let (mem, pos) = match addr {
|
||||
Stack(x) => (&mut self.stack, x),
|
||||
Heap(x) => (&mut self.heap, x),
|
||||
Invalid(_) => {
|
||||
return Err(MirEvalError::UndefinedBehavior("write invalid memory address"))
|
||||
}
|
||||
};
|
||||
mem.get_mut(pos..pos + r.len())
|
||||
.ok_or(MirEvalError::UndefinedBehavior("out of bound memory write"))?
|
||||
@ -1394,6 +1380,25 @@ impl Evaluator<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_closure(
|
||||
&mut self,
|
||||
closure: ClosureId,
|
||||
closure_data: Interval,
|
||||
generic_args: &Substitution,
|
||||
destination: Interval,
|
||||
args: &[IntervalAndTy],
|
||||
) -> Result<()> {
|
||||
let mir_body = self
|
||||
.db
|
||||
.mir_body_for_closure(closure)
|
||||
.map_err(|x| MirEvalError::MirLowerErrorForClosure(closure, x))?;
|
||||
let arg_bytes = iter::once(Ok(closure_data.get(self)?.to_owned()))
|
||||
.chain(args.iter().map(|x| Ok(x.get(&self)?.to_owned())))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter(), generic_args.clone())?;
|
||||
destination.write_from_bytes(self, &bytes)
|
||||
}
|
||||
|
||||
fn exec_fn_def(
|
||||
&mut self,
|
||||
def: FnDefId,
|
||||
@ -1546,6 +1551,9 @@ impl Evaluator<'_> {
|
||||
TyKind::Function(_) => {
|
||||
self.exec_fn_pointer(func_data, destination, &args[1..], locals)?;
|
||||
}
|
||||
TyKind::Closure(closure, subst) => {
|
||||
self.exec_closure(*closure, func_data, subst, destination, &args[1..])?;
|
||||
}
|
||||
x => not_supported!("Call FnTrait methods with type {x:?}"),
|
||||
}
|
||||
Ok(())
|
||||
|
@ -4,9 +4,9 @@ use std::{iter, mem, sync::Arc};
|
||||
|
||||
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
||||
use hir_def::{
|
||||
adt::{StructKind, VariantData},
|
||||
body::Body,
|
||||
expr::{
|
||||
data::adt::{StructKind, VariantData},
|
||||
hir::{
|
||||
Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
|
||||
RecordFieldPat, RecordLitField,
|
||||
},
|
||||
@ -21,9 +21,16 @@ use la_arena::ArenaMap;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{
|
||||
consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
|
||||
inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime,
|
||||
utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
|
||||
consteval::ConstEvalError,
|
||||
db::HirDatabase,
|
||||
display::HirDisplay,
|
||||
infer::{CaptureKind, CapturedItem, TypeMismatch},
|
||||
inhabitedness::is_ty_uninhabited_from,
|
||||
layout::layout_of_ty,
|
||||
mapping::ToChalk,
|
||||
static_lifetime,
|
||||
utils::generics,
|
||||
Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
@ -47,7 +54,7 @@ struct MirLowerCtx<'a> {
|
||||
current_loop_blocks: Option<LoopBlocks>,
|
||||
// FIXME: we should resolve labels in HIR lowering and always work with label id here, not
|
||||
// with raw names.
|
||||
labeled_loop_blocks: FxHashMap<Name, LoopBlocks>,
|
||||
labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>,
|
||||
discr_temp: Option<Place>,
|
||||
db: &'a dyn HirDatabase,
|
||||
body: &'a Body,
|
||||
@ -74,10 +81,12 @@ pub enum MirLowerError {
|
||||
BreakWithoutLoop,
|
||||
Loop,
|
||||
/// Something that should never happen and is definitely a bug, but we don't want to panic if it happened
|
||||
ImplementationError(&'static str),
|
||||
ImplementationError(String),
|
||||
LangItemNotFound(LangItem),
|
||||
MutatingRvalue,
|
||||
UnresolvedLabel,
|
||||
UnresolvedUpvar(Place),
|
||||
UnaccessableLocal,
|
||||
}
|
||||
|
||||
macro_rules! not_supported {
|
||||
@ -88,8 +97,8 @@ macro_rules! not_supported {
|
||||
|
||||
macro_rules! implementation_error {
|
||||
($x: expr) => {{
|
||||
::stdx::never!("MIR lower implementation bug: {}", $x);
|
||||
return Err(MirLowerError::ImplementationError($x));
|
||||
::stdx::never!("MIR lower implementation bug: {}", format!($x));
|
||||
return Err(MirLowerError::ImplementationError(format!($x)));
|
||||
}};
|
||||
}
|
||||
|
||||
@ -116,7 +125,44 @@ impl MirLowerError {
|
||||
|
||||
type Result<T> = std::result::Result<T, MirLowerError>;
|
||||
|
||||
impl MirLowerCtx<'_> {
|
||||
impl<'ctx> MirLowerCtx<'ctx> {
|
||||
fn new(
|
||||
db: &'ctx dyn HirDatabase,
|
||||
owner: DefWithBodyId,
|
||||
body: &'ctx Body,
|
||||
infer: &'ctx InferenceResult,
|
||||
) -> Self {
|
||||
let mut basic_blocks = Arena::new();
|
||||
let start_block = basic_blocks.alloc(BasicBlock {
|
||||
statements: vec![],
|
||||
terminator: None,
|
||||
is_cleanup: false,
|
||||
});
|
||||
let locals = Arena::new();
|
||||
let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
|
||||
let mir = MirBody {
|
||||
basic_blocks,
|
||||
locals,
|
||||
start_block,
|
||||
binding_locals,
|
||||
param_locals: vec![],
|
||||
owner,
|
||||
arg_count: body.params.len(),
|
||||
closures: vec![],
|
||||
};
|
||||
let ctx = MirLowerCtx {
|
||||
result: mir,
|
||||
db,
|
||||
infer,
|
||||
body,
|
||||
owner,
|
||||
current_loop_blocks: None,
|
||||
labeled_loop_blocks: Default::default(),
|
||||
discr_temp: None,
|
||||
};
|
||||
ctx
|
||||
}
|
||||
|
||||
fn temp(&mut self, ty: Ty) -> Result<LocalId> {
|
||||
if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
|
||||
implementation_error!("unsized temporaries");
|
||||
@ -268,7 +314,7 @@ impl MirLowerCtx<'_> {
|
||||
self.push_assignment(
|
||||
current,
|
||||
place,
|
||||
Operand::Copy(self.result.binding_locals[pat_id].into()).into(),
|
||||
Operand::Copy(self.binding_local(pat_id)?.into()).into(),
|
||||
expr_id.into(),
|
||||
);
|
||||
Ok(Some(current))
|
||||
@ -579,19 +625,19 @@ impl MirLowerCtx<'_> {
|
||||
Ok(None)
|
||||
}
|
||||
},
|
||||
Expr::Break { expr, label } => {
|
||||
&Expr::Break { expr, label } => {
|
||||
if let Some(expr) = expr {
|
||||
let loop_data = match label {
|
||||
Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
|
||||
Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?,
|
||||
None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?,
|
||||
};
|
||||
let Some(c) = self.lower_expr_to_place(*expr, loop_data.place.clone(), current)? else {
|
||||
let Some(c) = self.lower_expr_to_place(expr, loop_data.place.clone(), current)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
current = c;
|
||||
}
|
||||
let end = match label {
|
||||
Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?.end.expect("We always generate end for labeled loops"),
|
||||
Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?.end.expect("We always generate end for labeled loops"),
|
||||
None => self.current_loop_end()?,
|
||||
};
|
||||
self.set_goto(current, end);
|
||||
@ -713,20 +759,20 @@ impl MirLowerCtx<'_> {
|
||||
Ok(Some(current))
|
||||
}
|
||||
Expr::Box { .. } => not_supported!("box expression"),
|
||||
Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::expr::UnaryOp::Deref, .. } => {
|
||||
Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => {
|
||||
let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
|
||||
Ok(Some(current))
|
||||
}
|
||||
Expr::UnaryOp { expr, op: op @ (hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg) } => {
|
||||
Expr::UnaryOp { expr, op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg) } => {
|
||||
let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
let operation = match op {
|
||||
hir_def::expr::UnaryOp::Not => UnOp::Not,
|
||||
hir_def::expr::UnaryOp::Neg => UnOp::Neg,
|
||||
hir_def::hir::UnaryOp::Not => UnOp::Not,
|
||||
hir_def::hir::UnaryOp::Neg => UnOp::Neg,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.push_assignment(
|
||||
@ -739,7 +785,7 @@ impl MirLowerCtx<'_> {
|
||||
},
|
||||
Expr::BinaryOp { lhs, rhs, op } => {
|
||||
let op = op.ok_or(MirLowerError::IncompleteExpr)?;
|
||||
if let hir_def::expr::BinaryOp::Assignment { op } = op {
|
||||
if let hir_def::hir::BinaryOp::Assignment { op } = op {
|
||||
if op.is_some() {
|
||||
not_supported!("assignment with arith op (like +=)");
|
||||
}
|
||||
@ -765,13 +811,13 @@ impl MirLowerCtx<'_> {
|
||||
place,
|
||||
Rvalue::CheckedBinaryOp(
|
||||
match op {
|
||||
hir_def::expr::BinaryOp::LogicOp(op) => match op {
|
||||
hir_def::expr::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
|
||||
hir_def::expr::LogicOp::Or => BinOp::BitOr,
|
||||
hir_def::hir::BinaryOp::LogicOp(op) => match op {
|
||||
hir_def::hir::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
|
||||
hir_def::hir::LogicOp::Or => BinOp::BitOr,
|
||||
},
|
||||
hir_def::expr::BinaryOp::ArithOp(op) => BinOp::from(op),
|
||||
hir_def::expr::BinaryOp::CmpOp(op) => BinOp::from(op),
|
||||
hir_def::expr::BinaryOp::Assignment { .. } => unreachable!(), // handled above
|
||||
hir_def::hir::BinaryOp::ArithOp(op) => BinOp::from(op),
|
||||
hir_def::hir::BinaryOp::CmpOp(op) => BinOp::from(op),
|
||||
hir_def::hir::BinaryOp::Assignment { .. } => unreachable!(), // handled above
|
||||
},
|
||||
lhs_op,
|
||||
rhs_op,
|
||||
@ -823,7 +869,51 @@ impl MirLowerCtx<'_> {
|
||||
);
|
||||
Ok(Some(current))
|
||||
},
|
||||
Expr::Closure { .. } => not_supported!("closure"),
|
||||
Expr::Closure { .. } => {
|
||||
let ty = self.expr_ty(expr_id);
|
||||
let TyKind::Closure(id, _) = ty.kind(Interner) else {
|
||||
not_supported!("closure with non closure type");
|
||||
};
|
||||
self.result.closures.push(*id);
|
||||
let (captures, _) = self.infer.closure_info(id);
|
||||
let mut operands = vec![];
|
||||
for capture in captures.iter() {
|
||||
let p = Place {
|
||||
local: self.binding_local(capture.place.local)?,
|
||||
projection: capture.place.projections.clone().into_iter().map(|x| {
|
||||
match x {
|
||||
ProjectionElem::Deref => ProjectionElem::Deref,
|
||||
ProjectionElem::Field(x) => ProjectionElem::Field(x),
|
||||
ProjectionElem::TupleOrClosureField(x) => ProjectionElem::TupleOrClosureField(x),
|
||||
ProjectionElem::ConstantIndex { offset, min_length, from_end } => ProjectionElem::ConstantIndex { offset, min_length, from_end },
|
||||
ProjectionElem::Subslice { from, to, from_end } => ProjectionElem::Subslice { from, to, from_end },
|
||||
ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x),
|
||||
ProjectionElem::Index(x) => match x { },
|
||||
}
|
||||
}).collect(),
|
||||
};
|
||||
match &capture.kind {
|
||||
CaptureKind::ByRef(bk) => {
|
||||
let tmp: Place = self.temp(capture.ty.clone())?.into();
|
||||
self.push_assignment(
|
||||
current,
|
||||
tmp.clone(),
|
||||
Rvalue::Ref(bk.clone(), p),
|
||||
expr_id.into(),
|
||||
);
|
||||
operands.push(Operand::Move(tmp));
|
||||
},
|
||||
CaptureKind::ByValue => operands.push(Operand::Move(p)),
|
||||
}
|
||||
}
|
||||
self.push_assignment(
|
||||
current,
|
||||
place,
|
||||
Rvalue::Aggregate(AggregateKind::Closure(ty), operands),
|
||||
expr_id.into(),
|
||||
);
|
||||
Ok(Some(current))
|
||||
},
|
||||
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||
let Some(values) = exprs
|
||||
.iter()
|
||||
@ -893,7 +983,7 @@ impl MirLowerCtx<'_> {
|
||||
let index = name
|
||||
.as_tuple_index()
|
||||
.ok_or(MirLowerError::TypeError("named field on tuple"))?;
|
||||
place.projection.push(ProjectionElem::TupleField(index))
|
||||
place.projection.push(ProjectionElem::TupleOrClosureField(index))
|
||||
} else {
|
||||
let field =
|
||||
self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?;
|
||||
@ -910,7 +1000,7 @@ impl MirLowerCtx<'_> {
|
||||
.size
|
||||
.bytes_usize();
|
||||
let bytes = match l {
|
||||
hir_def::expr::Literal::String(b) => {
|
||||
hir_def::hir::Literal::String(b) => {
|
||||
let b = b.as_bytes();
|
||||
let mut data = vec![];
|
||||
data.extend(0usize.to_le_bytes());
|
||||
@ -919,7 +1009,7 @@ impl MirLowerCtx<'_> {
|
||||
mm.insert(0, b.to_vec());
|
||||
return Ok(Operand::from_concrete_const(data, mm, ty));
|
||||
}
|
||||
hir_def::expr::Literal::ByteString(b) => {
|
||||
hir_def::hir::Literal::ByteString(b) => {
|
||||
let mut data = vec![];
|
||||
data.extend(0usize.to_le_bytes());
|
||||
data.extend(b.len().to_le_bytes());
|
||||
@ -927,11 +1017,11 @@ impl MirLowerCtx<'_> {
|
||||
mm.insert(0, b.to_vec());
|
||||
return Ok(Operand::from_concrete_const(data, mm, ty));
|
||||
}
|
||||
hir_def::expr::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
|
||||
hir_def::expr::Literal::Bool(b) => vec![*b as u8],
|
||||
hir_def::expr::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
|
||||
hir_def::expr::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
|
||||
hir_def::expr::Literal::Float(f, _) => match size {
|
||||
hir_def::hir::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
|
||||
hir_def::hir::Literal::Bool(b) => vec![*b as u8],
|
||||
hir_def::hir::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
|
||||
hir_def::hir::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
|
||||
hir_def::hir::Literal::Float(f, _) => match size {
|
||||
8 => f.into_f64().to_le_bytes().into(),
|
||||
4 => f.into_f32().to_le_bytes().into(),
|
||||
_ => {
|
||||
@ -1119,19 +1209,18 @@ impl MirLowerCtx<'_> {
|
||||
// bad as we may emit end (unneccessary unreachable block) for unterminating loop, but
|
||||
// it should not affect correctness.
|
||||
self.current_loop_end()?;
|
||||
self.labeled_loop_blocks.insert(
|
||||
self.body.labels[label].name.clone(),
|
||||
self.current_loop_blocks.as_ref().unwrap().clone(),
|
||||
)
|
||||
self.labeled_loop_blocks
|
||||
.insert(label, self.current_loop_blocks.as_ref().unwrap().clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.set_goto(prev_block, begin);
|
||||
f(self, begin)?;
|
||||
let my = mem::replace(&mut self.current_loop_blocks, prev)
|
||||
.ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?;
|
||||
let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
|
||||
MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
|
||||
)?;
|
||||
if let Some(prev) = prev_label {
|
||||
self.labeled_loop_blocks.insert(self.body.labels[label.unwrap()].name.clone(), prev);
|
||||
self.labeled_loop_blocks.insert(label.unwrap(), prev);
|
||||
}
|
||||
Ok(my.end)
|
||||
}
|
||||
@ -1161,7 +1250,9 @@ impl MirLowerCtx<'_> {
|
||||
let r = match self
|
||||
.current_loop_blocks
|
||||
.as_mut()
|
||||
.ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
|
||||
.ok_or(MirLowerError::ImplementationError(
|
||||
"Current loop access out of loop".to_string(),
|
||||
))?
|
||||
.end
|
||||
{
|
||||
Some(x) => x,
|
||||
@ -1169,7 +1260,9 @@ impl MirLowerCtx<'_> {
|
||||
let s = self.new_basic_block();
|
||||
self.current_loop_blocks
|
||||
.as_mut()
|
||||
.ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
|
||||
.ok_or(MirLowerError::ImplementationError(
|
||||
"Current loop access out of loop".to_string(),
|
||||
))?
|
||||
.end = Some(s);
|
||||
s
|
||||
}
|
||||
@ -1183,7 +1276,7 @@ impl MirLowerCtx<'_> {
|
||||
|
||||
/// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in
|
||||
/// the appropriated places.
|
||||
fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) {
|
||||
fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> {
|
||||
// Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break
|
||||
// and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in
|
||||
// the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
|
||||
@ -1208,9 +1301,10 @@ impl MirLowerCtx<'_> {
|
||||
.copied()
|
||||
.map(MirSpan::PatId)
|
||||
.unwrap_or(MirSpan::Unknown);
|
||||
let l = self.result.binding_locals[b];
|
||||
let l = self.binding_local(b)?;
|
||||
self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
|
||||
self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
|
||||
@ -1220,14 +1314,14 @@ impl MirLowerCtx<'_> {
|
||||
|
||||
fn lower_block_to_place(
|
||||
&mut self,
|
||||
statements: &[hir_def::expr::Statement],
|
||||
statements: &[hir_def::hir::Statement],
|
||||
mut current: BasicBlockId,
|
||||
tail: Option<ExprId>,
|
||||
place: Place,
|
||||
) -> Result<Option<Idx<BasicBlock>>> {
|
||||
for statement in statements.iter() {
|
||||
match statement {
|
||||
hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
|
||||
hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
|
||||
if let Some(expr_id) = initializer {
|
||||
let else_block;
|
||||
let Some((init_place, c)) =
|
||||
@ -1258,12 +1352,18 @@ impl MirLowerCtx<'_> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut err = None;
|
||||
self.body.walk_bindings_in_pat(*pat, |b| {
|
||||
self.push_storage_live(b, current);
|
||||
if let Err(e) = self.push_storage_live(b, current) {
|
||||
err = Some(e);
|
||||
}
|
||||
});
|
||||
if let Some(e) = err {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
|
||||
hir_def::hir::Statement::Expr { expr, has_semi: _ } => {
|
||||
let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
@ -1276,6 +1376,67 @@ impl MirLowerCtx<'_> {
|
||||
None => Ok(Some(current)),
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_params_and_bindings(
|
||||
&mut self,
|
||||
params: impl Iterator<Item = (PatId, Ty)> + Clone,
|
||||
pick_binding: impl Fn(BindingId) -> bool,
|
||||
) -> Result<BasicBlockId> {
|
||||
let base_param_count = self.result.param_locals.len();
|
||||
self.result.param_locals.extend(params.clone().map(|(x, ty)| {
|
||||
let local_id = self.result.locals.alloc(Local { ty });
|
||||
if let Pat::Bind { id, subpat: None } = self.body[x] {
|
||||
if matches!(
|
||||
self.body.bindings[id].mode,
|
||||
BindingAnnotation::Unannotated | BindingAnnotation::Mutable
|
||||
) {
|
||||
self.result.binding_locals.insert(id, local_id);
|
||||
}
|
||||
}
|
||||
local_id
|
||||
}));
|
||||
// and then rest of bindings
|
||||
for (id, _) in self.body.bindings.iter() {
|
||||
if !pick_binding(id) {
|
||||
continue;
|
||||
}
|
||||
if !self.result.binding_locals.contains_idx(id) {
|
||||
self.result
|
||||
.binding_locals
|
||||
.insert(id, self.result.locals.alloc(Local { ty: self.infer[id].clone() }));
|
||||
}
|
||||
}
|
||||
let mut current = self.result.start_block;
|
||||
for ((param, _), local) in
|
||||
params.zip(self.result.param_locals.clone().into_iter().skip(base_param_count))
|
||||
{
|
||||
if let Pat::Bind { id, .. } = self.body[param] {
|
||||
if local == self.binding_local(id)? {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let r = self.pattern_match(
|
||||
current,
|
||||
None,
|
||||
local.into(),
|
||||
self.result.locals[local].ty.clone(),
|
||||
param,
|
||||
BindingAnnotation::Unannotated,
|
||||
)?;
|
||||
if let Some(b) = r.1 {
|
||||
self.set_terminator(b, Terminator::Unreachable);
|
||||
}
|
||||
current = r.0;
|
||||
}
|
||||
Ok(current)
|
||||
}
|
||||
|
||||
fn binding_local(&self, b: BindingId) -> Result<LocalId> {
|
||||
match self.result.binding_locals.get(b) {
|
||||
Some(x) => Ok(*x),
|
||||
None => Err(MirLowerError::UnaccessableLocal),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
|
||||
@ -1299,6 +1460,87 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn mir_body_for_closure_query(
|
||||
db: &dyn HirDatabase,
|
||||
closure: ClosureId,
|
||||
) -> Result<Arc<MirBody>> {
|
||||
let (owner, expr) = db.lookup_intern_closure(closure.into());
|
||||
let body = db.body(owner);
|
||||
let infer = db.infer(owner);
|
||||
let Expr::Closure { args, body: root, .. } = &body[expr] else {
|
||||
implementation_error!("closure expression is not closure");
|
||||
};
|
||||
let TyKind::Closure(_, substs) = &infer[expr].kind(Interner) else {
|
||||
implementation_error!("closure expression is not closure");
|
||||
};
|
||||
let (captures, _) = infer.closure_info(&closure);
|
||||
let mut ctx = MirLowerCtx::new(db, owner, &body, &infer);
|
||||
ctx.result.arg_count = args.len() + 1;
|
||||
// 0 is return local
|
||||
ctx.result.locals.alloc(Local { ty: infer[*root].clone() });
|
||||
ctx.result.locals.alloc(Local { ty: infer[expr].clone() });
|
||||
let Some(sig) = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db) else {
|
||||
implementation_error!("closure has not callable sig");
|
||||
};
|
||||
let current = ctx.lower_params_and_bindings(
|
||||
args.iter().zip(sig.params().iter()).map(|(x, y)| (*x, y.clone())),
|
||||
|_| true,
|
||||
)?;
|
||||
if let Some(b) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
|
||||
ctx.set_terminator(b, Terminator::Return);
|
||||
}
|
||||
let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
|
||||
for (i, capture) in captures.iter().enumerate() {
|
||||
let local = ctx.binding_local(capture.place.local)?;
|
||||
upvar_map.entry(local).or_default().push((capture, i));
|
||||
}
|
||||
let mut err = None;
|
||||
let closure_local = ctx.result.locals.iter().nth(1).unwrap().0;
|
||||
ctx.result.walk_places(|p| {
|
||||
if let Some(x) = upvar_map.get(&p.local) {
|
||||
let r = x.iter().find(|x| {
|
||||
if p.projection.len() < x.0.place.projections.len() {
|
||||
return false;
|
||||
}
|
||||
for (x, y) in p.projection.iter().zip(x.0.place.projections.iter()) {
|
||||
match (x, y) {
|
||||
(ProjectionElem::Deref, ProjectionElem::Deref) => (),
|
||||
(ProjectionElem::Field(x), ProjectionElem::Field(y)) if x == y => (),
|
||||
(
|
||||
ProjectionElem::TupleOrClosureField(x),
|
||||
ProjectionElem::TupleOrClosureField(y),
|
||||
) if x == y => (),
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
match r {
|
||||
Some(x) => {
|
||||
p.local = closure_local;
|
||||
let prev_projs =
|
||||
mem::replace(&mut p.projection, vec![PlaceElem::TupleOrClosureField(x.1)]);
|
||||
if x.0.kind != CaptureKind::ByValue {
|
||||
p.projection.push(ProjectionElem::Deref);
|
||||
}
|
||||
p.projection.extend(prev_projs.into_iter().skip(x.0.place.projections.len()));
|
||||
}
|
||||
None => err = Some(p.clone()),
|
||||
}
|
||||
}
|
||||
});
|
||||
ctx.result.binding_locals = ctx
|
||||
.result
|
||||
.binding_locals
|
||||
.into_iter()
|
||||
.filter(|x| ctx.body[x.0].owner == Some(expr))
|
||||
.collect();
|
||||
if let Some(err) = err {
|
||||
return Err(MirLowerError::UnresolvedUpvar(err));
|
||||
}
|
||||
Ok(Arc::new(ctx.result))
|
||||
}
|
||||
|
||||
pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
|
||||
let _p = profile::span("mir_body_query").detail(|| match def {
|
||||
DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(),
|
||||
@ -1336,86 +1578,29 @@ pub fn lower_to_mir(
|
||||
if let Some((_, x)) = infer.type_mismatches().next() {
|
||||
return Err(MirLowerError::TypeMismatch(x.clone()));
|
||||
}
|
||||
let mut basic_blocks = Arena::new();
|
||||
let start_block =
|
||||
basic_blocks.alloc(BasicBlock { statements: vec![], terminator: None, is_cleanup: false });
|
||||
let mut locals = Arena::new();
|
||||
let mut ctx = MirLowerCtx::new(db, owner, body, infer);
|
||||
// 0 is return local
|
||||
locals.alloc(Local { ty: infer[root_expr].clone() });
|
||||
let mut binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
|
||||
ctx.result.locals.alloc(Local { ty: infer[root_expr].clone() });
|
||||
let binding_picker = |b: BindingId| {
|
||||
if root_expr == body.body_expr {
|
||||
body[b].owner.is_none()
|
||||
} else {
|
||||
body[b].owner == Some(root_expr)
|
||||
}
|
||||
};
|
||||
// 1 to param_len is for params
|
||||
let param_locals: Vec<LocalId> = if let DefWithBodyId::FunctionId(fid) = owner {
|
||||
let current = if let DefWithBodyId::FunctionId(fid) = owner {
|
||||
let substs = TyBuilder::placeholder_subst(db, fid);
|
||||
let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs);
|
||||
body.params
|
||||
.iter()
|
||||
.zip(callable_sig.params().iter())
|
||||
.map(|(&x, ty)| {
|
||||
let local_id = locals.alloc(Local { ty: ty.clone() });
|
||||
if let Pat::Bind { id, subpat: None } = body[x] {
|
||||
if matches!(
|
||||
body.bindings[id].mode,
|
||||
BindingAnnotation::Unannotated | BindingAnnotation::Mutable
|
||||
) {
|
||||
binding_locals.insert(id, local_id);
|
||||
}
|
||||
}
|
||||
local_id
|
||||
})
|
||||
.collect()
|
||||
ctx.lower_params_and_bindings(
|
||||
body.params.iter().zip(callable_sig.params().iter()).map(|(x, y)| (*x, y.clone())),
|
||||
binding_picker,
|
||||
)?
|
||||
} else {
|
||||
if !body.params.is_empty() {
|
||||
return Err(MirLowerError::TypeError("Unexpected parameter for non function body"));
|
||||
}
|
||||
vec![]
|
||||
ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
|
||||
};
|
||||
// and then rest of bindings
|
||||
for (id, _) in body.bindings.iter() {
|
||||
if !binding_locals.contains_idx(id) {
|
||||
binding_locals.insert(id, locals.alloc(Local { ty: infer[id].clone() }));
|
||||
}
|
||||
}
|
||||
let mir = MirBody {
|
||||
basic_blocks,
|
||||
locals,
|
||||
start_block,
|
||||
binding_locals,
|
||||
param_locals,
|
||||
owner,
|
||||
arg_count: body.params.len(),
|
||||
};
|
||||
let mut ctx = MirLowerCtx {
|
||||
result: mir,
|
||||
db,
|
||||
infer,
|
||||
body,
|
||||
owner,
|
||||
current_loop_blocks: None,
|
||||
labeled_loop_blocks: Default::default(),
|
||||
discr_temp: None,
|
||||
};
|
||||
let mut current = start_block;
|
||||
for (¶m, local) in body.params.iter().zip(ctx.result.param_locals.clone().into_iter()) {
|
||||
if let Pat::Bind { id, .. } = body[param] {
|
||||
if local == ctx.result.binding_locals[id] {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let r = ctx.pattern_match(
|
||||
current,
|
||||
None,
|
||||
local.into(),
|
||||
ctx.result.locals[local].ty.clone(),
|
||||
param,
|
||||
BindingAnnotation::Unannotated,
|
||||
)?;
|
||||
if let Some(b) = r.1 {
|
||||
ctx.set_terminator(b, Terminator::Unreachable);
|
||||
}
|
||||
current = r.0;
|
||||
}
|
||||
if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
|
||||
ctx.result.basic_blocks[b].terminator = Some(Terminator::Return);
|
||||
ctx.set_terminator(b, Terminator::Return);
|
||||
}
|
||||
Ok(ctx.result)
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ impl MirLowerCtx<'_> {
|
||||
}
|
||||
}
|
||||
Expr::UnaryOp { expr, op } => match op {
|
||||
hir_def::expr::UnaryOp::Deref => {
|
||||
hir_def::hir::UnaryOp::Deref => {
|
||||
if !matches!(
|
||||
self.expr_ty(*expr).kind(Interner),
|
||||
TyKind::Ref(..) | TyKind::Raw(..)
|
||||
|
@ -1,5 +1,7 @@
|
||||
//! MIR lowering for patterns
|
||||
|
||||
use crate::utils::pattern_matching_dereference_count;
|
||||
|
||||
use super::*;
|
||||
|
||||
macro_rules! not_supported {
|
||||
@ -52,7 +54,7 @@ impl MirLowerCtx<'_> {
|
||||
args,
|
||||
*ellipsis,
|
||||
subst.iter(Interner).enumerate().map(|(i, x)| {
|
||||
(PlaceElem::TupleField(i), x.assert_ty_ref(Interner).clone())
|
||||
(PlaceElem::TupleOrClosureField(i), x.assert_ty_ref(Interner).clone())
|
||||
}),
|
||||
&cond_place,
|
||||
binding_mode,
|
||||
@ -142,7 +144,7 @@ impl MirLowerCtx<'_> {
|
||||
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
|
||||
binding_mode = mode;
|
||||
}
|
||||
self.push_storage_live(*id, current);
|
||||
self.push_storage_live(*id, current)?;
|
||||
self.push_assignment(
|
||||
current,
|
||||
target_place.into(),
|
||||
@ -387,13 +389,6 @@ fn pattern_matching_dereference(
|
||||
binding_mode: &mut BindingAnnotation,
|
||||
cond_place: &mut Place,
|
||||
) {
|
||||
while let Some((ty, _, mu)) = cond_ty.as_reference() {
|
||||
if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
|
||||
*binding_mode = BindingAnnotation::RefMut;
|
||||
} else {
|
||||
*binding_mode = BindingAnnotation::Ref;
|
||||
}
|
||||
*cond_ty = ty.clone();
|
||||
cond_place.projection.push(ProjectionElem::Deref);
|
||||
}
|
||||
let cnt = pattern_matching_dereference_count(cond_ty, binding_mode);
|
||||
cond_place.projection.extend((0..cnt).map(|_| ProjectionElem::Deref));
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
//! A pretty-printer for MIR.
|
||||
|
||||
use std::fmt::{Debug, Display, Write};
|
||||
use std::{
|
||||
fmt::{Debug, Display, Write},
|
||||
mem,
|
||||
};
|
||||
|
||||
use hir_def::{body::Body, expr::BindingId};
|
||||
use hir_def::{body::Body, hir::BindingId};
|
||||
use hir_expand::name::Name;
|
||||
use la_arena::ArenaMap;
|
||||
|
||||
@ -20,7 +23,7 @@ impl MirBody {
|
||||
pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
|
||||
let hir_body = db.body(self.owner);
|
||||
let mut ctx = MirPrettyCtx::new(self, &hir_body, db);
|
||||
ctx.for_body();
|
||||
ctx.for_body(ctx.body.owner);
|
||||
ctx.result
|
||||
}
|
||||
|
||||
@ -42,7 +45,7 @@ struct MirPrettyCtx<'a> {
|
||||
hir_body: &'a Body,
|
||||
db: &'a dyn HirDatabase,
|
||||
result: String,
|
||||
ident: String,
|
||||
indent: String,
|
||||
local_to_binding: ArenaMap<LocalId, BindingId>,
|
||||
}
|
||||
|
||||
@ -88,22 +91,43 @@ impl Display for LocalName {
|
||||
}
|
||||
|
||||
impl<'a> MirPrettyCtx<'a> {
|
||||
fn for_body(&mut self) {
|
||||
wln!(self, "// {:?}", self.body.owner);
|
||||
fn for_body(&mut self, name: impl Debug) {
|
||||
wln!(self, "// {:?}", name);
|
||||
self.with_block(|this| {
|
||||
this.locals();
|
||||
wln!(this);
|
||||
this.blocks();
|
||||
});
|
||||
for &closure in &self.body.closures {
|
||||
let body = match self.db.mir_body_for_closure(closure) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
wln!(self, "// error in {closure:?}: {e:?}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let result = mem::take(&mut self.result);
|
||||
let indent = mem::take(&mut self.indent);
|
||||
let mut ctx = MirPrettyCtx {
|
||||
body: &body,
|
||||
local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(),
|
||||
result,
|
||||
indent,
|
||||
..*self
|
||||
};
|
||||
ctx.for_body(closure);
|
||||
self.result = ctx.result;
|
||||
self.indent = ctx.indent;
|
||||
}
|
||||
}
|
||||
|
||||
fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) {
|
||||
self.ident += " ";
|
||||
self.indent += " ";
|
||||
wln!(self, "{{");
|
||||
f(self);
|
||||
for _ in 0..4 {
|
||||
self.result.pop();
|
||||
self.ident.pop();
|
||||
self.indent.pop();
|
||||
}
|
||||
wln!(self, "}}");
|
||||
}
|
||||
@ -114,7 +138,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
body,
|
||||
db,
|
||||
result: String::new(),
|
||||
ident: String::new(),
|
||||
indent: String::new(),
|
||||
local_to_binding,
|
||||
hir_body,
|
||||
}
|
||||
@ -122,7 +146,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
|
||||
fn write_line(&mut self) {
|
||||
self.result.push('\n');
|
||||
self.result += &self.ident;
|
||||
self.result += &self.indent;
|
||||
}
|
||||
|
||||
fn write(&mut self, line: &str) {
|
||||
@ -247,7 +271,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
ProjectionElem::TupleField(x) => {
|
||||
ProjectionElem::TupleOrClosureField(x) => {
|
||||
f(this, local, head);
|
||||
w!(this, ".{}", x);
|
||||
}
|
||||
@ -302,6 +326,11 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
self.operand_list(x);
|
||||
w!(self, ")");
|
||||
}
|
||||
Rvalue::Aggregate(AggregateKind::Closure(_), x) => {
|
||||
w!(self, "Closure(");
|
||||
self.operand_list(x);
|
||||
w!(self, ")");
|
||||
}
|
||||
Rvalue::Aggregate(AggregateKind::Union(_, _), x) => {
|
||||
w!(self, "Union(");
|
||||
self.operand_list(x);
|
||||
|
@ -17,7 +17,7 @@ use expect_test::Expect;
|
||||
use hir_def::{
|
||||
body::{Body, BodySourceMap, SyntheticSyntax},
|
||||
db::{DefDatabase, InternDatabase},
|
||||
expr::{ExprId, PatId},
|
||||
hir::{ExprId, PatId},
|
||||
item_scope::ItemScope,
|
||||
nameres::DefMap,
|
||||
src::HasSource,
|
||||
@ -198,8 +198,8 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
||||
|
||||
for (expr_or_pat, mismatch) in inference_result.type_mismatches() {
|
||||
let Some(node) = (match expr_or_pat {
|
||||
hir_def::expr::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
|
||||
hir_def::expr::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
|
||||
hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
|
||||
hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
|
||||
}) else { continue; };
|
||||
let range = node.as_ref().original_file_range(&db);
|
||||
let actual = format!(
|
||||
|
@ -575,7 +575,7 @@ fn two_closures_lub() {
|
||||
fn foo(c: i32) {
|
||||
let add = |a: i32, b: i32| a + b;
|
||||
let sub = |a, b| a - b;
|
||||
//^^^^^^^^^^^^ |i32, i32| -> i32
|
||||
//^^^^^^^^^^^^ impl Fn(i32, i32) -> i32
|
||||
if c > 42 { add } else { sub };
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fn(i32, i32) -> i32
|
||||
}
|
||||
@ -875,6 +875,16 @@ fn test() {
|
||||
fn adjust_index() {
|
||||
check_no_mismatches(
|
||||
r"
|
||||
//- minicore: index, slice, coerce_unsized
|
||||
fn test() {
|
||||
let x = [1, 2, 3];
|
||||
x[2] = 6;
|
||||
// ^ adjustments: Borrow(Ref(Mut))
|
||||
}
|
||||
",
|
||||
);
|
||||
check_no_mismatches(
|
||||
r"
|
||||
//- minicore: index
|
||||
struct Struct;
|
||||
impl core::ops::Index<usize> for Struct {
|
||||
@ -902,3 +912,32 @@ fn test() {
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_14443_dyn_coercion_block_impls() {
|
||||
check_no_mismatches(
|
||||
r#"
|
||||
//- minicore: coerce_unsized
|
||||
trait T {}
|
||||
|
||||
fn dyn_t(d: &dyn T) {}
|
||||
|
||||
fn main() {
|
||||
struct A;
|
||||
impl T for A {}
|
||||
|
||||
let a = A;
|
||||
|
||||
let b = {
|
||||
struct B;
|
||||
impl T for B {}
|
||||
|
||||
B
|
||||
};
|
||||
|
||||
dyn_t(&a);
|
||||
dyn_t(&b);
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ fn expr_macro_def_expanded_in_various_places() {
|
||||
100..119 'for _ ...!() {}': ()
|
||||
104..105 '_': {unknown}
|
||||
117..119 '{}': ()
|
||||
124..134 '|| spam!()': || -> isize
|
||||
124..134 '|| spam!()': impl Fn() -> isize
|
||||
140..156 'while ...!() {}': ()
|
||||
154..156 '{}': ()
|
||||
161..174 'break spam!()': !
|
||||
@ -279,7 +279,7 @@ fn expr_macro_rules_expanded_in_various_places() {
|
||||
114..133 'for _ ...!() {}': ()
|
||||
118..119 '_': {unknown}
|
||||
131..133 '{}': ()
|
||||
138..148 '|| spam!()': || -> isize
|
||||
138..148 '|| spam!()': impl Fn() -> isize
|
||||
154..170 'while ...!() {}': ()
|
||||
168..170 '{}': ()
|
||||
175..188 'break spam!()': !
|
||||
|
@ -388,6 +388,24 @@ mod bar_test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_trait_method_multiple_mutable_reference() {
|
||||
check_types(
|
||||
r#"
|
||||
trait Trait {
|
||||
fn method(&mut self) -> i32 { 5 }
|
||||
}
|
||||
struct S;
|
||||
impl Trait for &mut &mut S {}
|
||||
fn test() {
|
||||
let s = &mut &mut &mut S;
|
||||
s.method();
|
||||
//^^^^^^^^^^ i32
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_trait_method_generic_1() {
|
||||
// the trait implementation is intentionally incomplete -- it shouldn't matter
|
||||
@ -1722,7 +1740,7 @@ fn test() {
|
||||
Foo.foo();
|
||||
//^^^ adjustments: Borrow(Ref(Not))
|
||||
(&Foo).foo();
|
||||
// ^^^^ adjustments: ,
|
||||
// ^^^^ adjustments: Deref(None), Borrow(Ref(Not))
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
@ -70,8 +70,8 @@ fn infer_pattern() {
|
||||
228..233 '&true': &bool
|
||||
229..233 'true': bool
|
||||
234..236 '{}': ()
|
||||
246..252 'lambda': |u64, u64, i32| -> i32
|
||||
255..287 '|a: u6...b; c }': |u64, u64, i32| -> i32
|
||||
246..252 'lambda': impl Fn(u64, u64, i32) -> i32
|
||||
255..287 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32
|
||||
256..257 'a': u64
|
||||
264..265 'b': u64
|
||||
267..268 'c': i32
|
||||
@ -476,7 +476,7 @@ fn infer_adt_pattern() {
|
||||
183..184 'x': usize
|
||||
190..191 'x': usize
|
||||
201..205 'E::B': E
|
||||
209..212 'foo': {unknown}
|
||||
209..212 'foo': bool
|
||||
216..217 '1': usize
|
||||
227..231 'E::B': E
|
||||
235..237 '10': usize
|
||||
@ -677,25 +677,25 @@ fn test() {
|
||||
51..58 'loop {}': !
|
||||
56..58 '{}': ()
|
||||
72..171 '{ ... x); }': ()
|
||||
78..81 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32
|
||||
78..81 'foo': fn foo<&(i32, &str), i32, impl Fn(&(i32, &str)) -> i32>(&(i32, &str), impl Fn(&(i32, &str)) -> i32) -> i32
|
||||
78..105 'foo(&(...y)| x)': i32
|
||||
82..91 '&(1, "a")': &(i32, &str)
|
||||
83..91 '(1, "a")': (i32, &str)
|
||||
84..85 '1': i32
|
||||
87..90 '"a"': &str
|
||||
93..104 '|&(x, y)| x': |&(i32, &str)| -> i32
|
||||
93..104 '|&(x, y)| x': impl Fn(&(i32, &str)) -> i32
|
||||
94..101 '&(x, y)': &(i32, &str)
|
||||
95..101 '(x, y)': (i32, &str)
|
||||
96..97 'x': i32
|
||||
99..100 'y': &str
|
||||
103..104 'x': i32
|
||||
142..145 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32
|
||||
142..145 'foo': fn foo<&(i32, &str), &i32, impl Fn(&(i32, &str)) -> &i32>(&(i32, &str), impl Fn(&(i32, &str)) -> &i32) -> &i32
|
||||
142..168 'foo(&(...y)| x)': &i32
|
||||
146..155 '&(1, "a")': &(i32, &str)
|
||||
147..155 '(1, "a")': (i32, &str)
|
||||
148..149 '1': i32
|
||||
151..154 '"a"': &str
|
||||
157..167 '|(x, y)| x': |&(i32, &str)| -> &i32
|
||||
157..167 '|(x, y)| x': impl Fn(&(i32, &str)) -> &i32
|
||||
158..164 '(x, y)': (i32, &str)
|
||||
159..160 'x': &i32
|
||||
162..163 'y': &&str
|
||||
|
@ -270,7 +270,7 @@ fn infer_std_crash_5() {
|
||||
61..320 '{ ... }': ()
|
||||
75..79 'name': &{unknown}
|
||||
82..166 'if doe... }': &{unknown}
|
||||
85..98 'doesnt_matter': {unknown}
|
||||
85..98 'doesnt_matter': bool
|
||||
99..128 '{ ... }': &{unknown}
|
||||
113..118 'first': &{unknown}
|
||||
134..166 '{ ... }': &{unknown}
|
||||
@ -279,7 +279,7 @@ fn infer_std_crash_5() {
|
||||
181..188 'content': &{unknown}
|
||||
191..313 'if ICE... }': &{unknown}
|
||||
194..231 'ICE_RE..._VALUE': {unknown}
|
||||
194..247 'ICE_RE...&name)': {unknown}
|
||||
194..247 'ICE_RE...&name)': bool
|
||||
241..246 '&name': &&{unknown}
|
||||
242..246 'name': &{unknown}
|
||||
248..276 '{ ... }': &{unknown}
|
||||
@ -805,19 +805,19 @@ fn issue_4966() {
|
||||
225..229 'iter': T
|
||||
244..246 '{}': Vec<A>
|
||||
258..402 '{ ...r(); }': ()
|
||||
268..273 'inner': Map<|&f64| -> f64>
|
||||
276..300 'Map { ... 0.0 }': Map<|&f64| -> f64>
|
||||
285..298 '|_: &f64| 0.0': |&f64| -> f64
|
||||
268..273 'inner': Map<impl Fn(&f64) -> f64>
|
||||
276..300 'Map { ... 0.0 }': Map<impl Fn(&f64) -> f64>
|
||||
285..298 '|_: &f64| 0.0': impl Fn(&f64) -> f64
|
||||
286..287 '_': &f64
|
||||
295..298 '0.0': f64
|
||||
311..317 'repeat': Repeat<Map<|&f64| -> f64>>
|
||||
320..345 'Repeat...nner }': Repeat<Map<|&f64| -> f64>>
|
||||
338..343 'inner': Map<|&f64| -> f64>
|
||||
356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
|
||||
362..371 'from_iter': fn from_iter<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>, Repeat<Map<|&f64| -> f64>>>(Repeat<Map<|&f64| -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
|
||||
362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
|
||||
372..378 'repeat': Repeat<Map<|&f64| -> f64>>
|
||||
386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
|
||||
311..317 'repeat': Repeat<Map<impl Fn(&f64) -> f64>>
|
||||
320..345 'Repeat...nner }': Repeat<Map<impl Fn(&f64) -> f64>>
|
||||
338..343 'inner': Map<impl Fn(&f64) -> f64>
|
||||
356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
|
||||
362..371 'from_iter': fn from_iter<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>, Repeat<Map<impl Fn(&f64) -> f64>>>(Repeat<Map<impl Fn(&f64) -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
|
||||
362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
|
||||
372..378 'repeat': Repeat<Map<impl Fn(&f64) -> f64>>
|
||||
386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
|
||||
386..399 'vec.foo_bar()': {unknown}
|
||||
"#]],
|
||||
);
|
||||
@ -852,7 +852,7 @@ fn main() {
|
||||
123..126 'S()': S<i32>
|
||||
132..133 's': S<i32>
|
||||
132..144 's.g(|_x| {})': ()
|
||||
136..143 '|_x| {}': |&i32| -> ()
|
||||
136..143 '|_x| {}': impl Fn(&i32)
|
||||
137..139 '_x': &i32
|
||||
141..143 '{}': ()
|
||||
150..151 's': S<i32>
|
||||
@ -1067,6 +1067,23 @@ fn parse_arule() {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_closure() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: fn, option
|
||||
|
||||
fn map<T, U>(o: Option<T>, f: impl FnOnce(T) -> U) -> Option<U> { loop {} }
|
||||
|
||||
fn test() {
|
||||
let o = Some(Some(2));
|
||||
map(o, |s| map(s, |x| x));
|
||||
// ^ i32
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_expected_type_closure() {
|
||||
check_types(
|
||||
@ -1759,13 +1776,14 @@ const C: usize = 2 + 2;
|
||||
|
||||
#[test]
|
||||
fn regression_14456() {
|
||||
check_no_mismatches(
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: future
|
||||
async fn x() {}
|
||||
fn f() {
|
||||
let fut = x();
|
||||
let t = [0u8; 2 + 2];
|
||||
let t = [0u8; { let a = 2 + 2; a }];
|
||||
//^ [u8; 4]
|
||||
}
|
||||
"#,
|
||||
);
|
||||
@ -1802,3 +1820,21 @@ where
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_ergonomics_with_binding_modes_interaction() {
|
||||
check_types(
|
||||
r"
|
||||
enum E { A }
|
||||
fn foo() {
|
||||
match &E::A {
|
||||
b @ (x @ E::A | x) => {
|
||||
b;
|
||||
//^ &E
|
||||
x;
|
||||
//^ &E
|
||||
}
|
||||
}
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
@ -1906,8 +1906,8 @@ fn closure_return() {
|
||||
"#,
|
||||
expect![[r#"
|
||||
16..58 '{ ...; }; }': u32
|
||||
26..27 'x': || -> usize
|
||||
30..55 '|| -> ...n 1; }': || -> usize
|
||||
26..27 'x': impl Fn() -> usize
|
||||
30..55 '|| -> ...n 1; }': impl Fn() -> usize
|
||||
42..55 '{ return 1; }': usize
|
||||
44..52 'return 1': !
|
||||
51..52 '1': usize
|
||||
@ -1925,8 +1925,8 @@ fn closure_return_unit() {
|
||||
"#,
|
||||
expect![[r#"
|
||||
16..47 '{ ...; }; }': u32
|
||||
26..27 'x': || -> ()
|
||||
30..44 '|| { return; }': || -> ()
|
||||
26..27 'x': impl Fn()
|
||||
30..44 '|| { return; }': impl Fn()
|
||||
33..44 '{ return; }': ()
|
||||
35..41 'return': !
|
||||
"#]],
|
||||
@ -1943,8 +1943,8 @@ fn closure_return_inferred() {
|
||||
"#,
|
||||
expect![[r#"
|
||||
16..46 '{ ..." }; }': u32
|
||||
26..27 'x': || -> &str
|
||||
30..43 '|| { "test" }': || -> &str
|
||||
26..27 'x': impl Fn() -> &str
|
||||
30..43 '|| { "test" }': impl Fn() -> &str
|
||||
33..43 '{ "test" }': &str
|
||||
35..41 '"test"': &str
|
||||
"#]],
|
||||
@ -2050,7 +2050,7 @@ fn fn_pointer_return() {
|
||||
47..120 '{ ...hod; }': ()
|
||||
57..63 'vtable': Vtable
|
||||
66..90 'Vtable...| {} }': Vtable
|
||||
83..88 '|| {}': || -> ()
|
||||
83..88 '|| {}': impl Fn()
|
||||
86..88 '{}': ()
|
||||
100..101 'm': fn()
|
||||
104..110 'vtable': Vtable
|
||||
@ -2142,9 +2142,9 @@ fn main() {
|
||||
149..151 'Ok': Ok<(), ()>(()) -> Result<(), ()>
|
||||
149..155 'Ok(())': Result<(), ()>
|
||||
152..154 '()': ()
|
||||
167..171 'test': fn test<(), (), || -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(|| -> impl Future<Output = Result<(), ()>>)
|
||||
167..171 'test': fn test<(), (), impl Fn() -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(impl Fn() -> impl Future<Output = Result<(), ()>>)
|
||||
167..228 'test(|... })': ()
|
||||
172..227 '|| asy... }': || -> impl Future<Output = Result<(), ()>>
|
||||
172..227 '|| asy... }': impl Fn() -> impl Future<Output = Result<(), ()>>
|
||||
175..227 'async ... }': impl Future<Output = Result<(), ()>>
|
||||
191..205 'return Err(())': !
|
||||
198..201 'Err': Err<(), ()>(()) -> Result<(), ()>
|
||||
@ -2270,8 +2270,8 @@ fn infer_labelled_break_with_val() {
|
||||
"#,
|
||||
expect![[r#"
|
||||
9..335 '{ ... }; }': ()
|
||||
19..21 '_x': || -> bool
|
||||
24..332 '|| 'ou... }': || -> bool
|
||||
19..21 '_x': impl Fn() -> bool
|
||||
24..332 '|| 'ou... }': impl Fn() -> bool
|
||||
27..332 ''outer... }': bool
|
||||
40..332 '{ ... }': ()
|
||||
54..59 'inner': i8
|
||||
@ -2695,6 +2695,179 @@ impl B for Astruct {}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capture_kinds_simple() {
|
||||
check_types(
|
||||
r#"
|
||||
struct S;
|
||||
|
||||
impl S {
|
||||
fn read(&self) -> &S { self }
|
||||
fn write(&mut self) -> &mut S { self }
|
||||
fn consume(self) -> S { self }
|
||||
}
|
||||
|
||||
fn f() {
|
||||
let x = S;
|
||||
let c1 = || x.read();
|
||||
//^^ impl Fn() -> &S
|
||||
let c2 = || x.write();
|
||||
//^^ impl FnMut() -> &mut S
|
||||
let c3 = || x.consume();
|
||||
//^^ impl FnOnce() -> S
|
||||
let c3 = || x.consume().consume().consume();
|
||||
//^^ impl FnOnce() -> S
|
||||
let c3 = || x.consume().write().read();
|
||||
//^^ impl FnOnce() -> &S
|
||||
let x = &mut x;
|
||||
let c1 = || x.write();
|
||||
//^^ impl FnMut() -> &mut S
|
||||
let x = S;
|
||||
let c1 = || { let ref t = x; t };
|
||||
//^^ impl Fn() -> &S
|
||||
let c2 = || { let ref mut t = x; t };
|
||||
//^^ impl FnMut() -> &mut S
|
||||
let c3 = || { let t = x; t };
|
||||
//^^ impl FnOnce() -> S
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capture_kinds_closure() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: copy, fn
|
||||
fn f() {
|
||||
let mut x = 2;
|
||||
x = 5;
|
||||
let mut c1 = || { x = 3; x };
|
||||
//^^^^^^ impl FnMut() -> i32
|
||||
let mut c2 = || { c1() };
|
||||
//^^^^^^ impl FnMut() -> i32
|
||||
let mut c1 = || { x };
|
||||
//^^^^^^ impl Fn() -> i32
|
||||
let mut c2 = || { c1() };
|
||||
//^^^^^^ impl Fn() -> i32
|
||||
struct X;
|
||||
let x = X;
|
||||
let mut c1 = || { x };
|
||||
//^^^^^^ impl FnOnce() -> X
|
||||
let mut c2 = || { c1() };
|
||||
//^^^^^^ impl FnOnce() -> X
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capture_kinds_overloaded_deref() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: fn, deref_mut
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
struct Foo;
|
||||
impl Deref for Foo {
|
||||
type Target = (i32, u8);
|
||||
fn deref(&self) -> &(i32, u8) {
|
||||
&(5, 2)
|
||||
}
|
||||
}
|
||||
impl DerefMut for Foo {
|
||||
fn deref_mut(&mut self) -> &mut (i32, u8) {
|
||||
&mut (5, 2)
|
||||
}
|
||||
}
|
||||
fn test() {
|
||||
let mut x = Foo;
|
||||
let c1 = || *x;
|
||||
//^^ impl Fn() -> (i32, u8)
|
||||
let c2 = || { *x = (2, 5); };
|
||||
//^^ impl FnMut()
|
||||
let c3 = || { x.1 };
|
||||
//^^ impl Fn() -> u8
|
||||
let c4 = || { x.1 = 6; };
|
||||
//^^ impl FnMut()
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capture_kinds_with_copy_types() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: copy, clone, derive
|
||||
#[derive(Clone, Copy)]
|
||||
struct Copy;
|
||||
struct NotCopy;
|
||||
#[derive(Clone, Copy)]
|
||||
struct Generic<T>(T);
|
||||
|
||||
trait Tr {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
impl Tr for Copy {
|
||||
type Assoc = NotCopy;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct AssocGeneric<T: Tr>(T::Assoc);
|
||||
|
||||
fn f() {
|
||||
let a = Copy;
|
||||
let b = NotCopy;
|
||||
let c = Generic(Copy);
|
||||
let d = Generic(NotCopy);
|
||||
let e: AssocGeneric<Copy> = AssocGeneric(NotCopy);
|
||||
let c1 = || a;
|
||||
//^^ impl Fn() -> Copy
|
||||
let c2 = || b;
|
||||
//^^ impl FnOnce() -> NotCopy
|
||||
let c3 = || c;
|
||||
//^^ impl Fn() -> Generic<Copy>
|
||||
let c3 = || d;
|
||||
//^^ impl FnOnce() -> Generic<NotCopy>
|
||||
let c3 = || e;
|
||||
//^^ impl FnOnce() -> AssocGeneric<Copy>
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_macro_should_work_for_associated_type() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: copy, clone, derive
|
||||
#[derive(Clone)]
|
||||
struct X;
|
||||
#[derive(Clone)]
|
||||
struct Y;
|
||||
|
||||
trait Tr {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
impl Tr for X {
|
||||
type Assoc = Y;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AssocGeneric<T: Tr>(T::Assoc);
|
||||
|
||||
fn f() {
|
||||
let e: AssocGeneric<X> = AssocGeneric(Y);
|
||||
let e_clone = e.clone();
|
||||
//^^^^^^^ AssocGeneric<X>
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cfgd_out_assoc_items() {
|
||||
check_types(
|
||||
@ -3291,6 +3464,19 @@ fn f<T>(t: Ark<T>) {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn const_dependent_on_local() {
|
||||
check_types(
|
||||
r#"
|
||||
fn main() {
|
||||
let s = 5;
|
||||
let t = [2; s];
|
||||
//^ [i32; _]
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_14275() {
|
||||
// FIXME: evaluate const generic
|
||||
|
@ -90,7 +90,7 @@ fn infer_async_closure() {
|
||||
async fn test() {
|
||||
let f = async move |x: i32| x + 42;
|
||||
f;
|
||||
// ^ |i32| -> impl Future<Output = i32>
|
||||
// ^ impl Fn(i32) -> impl Future<Output = i32>
|
||||
let a = f(4);
|
||||
a;
|
||||
// ^ impl Future<Output = i32>
|
||||
@ -99,7 +99,7 @@ async fn test() {
|
||||
// ^ i32
|
||||
let f = async move || 42;
|
||||
f;
|
||||
// ^ || -> impl Future<Output = i32>
|
||||
// ^ impl Fn() -> impl Future<Output = i32>
|
||||
let a = f();
|
||||
a;
|
||||
// ^ impl Future<Output = i32>
|
||||
@ -116,7 +116,7 @@ async fn test() {
|
||||
};
|
||||
let _: Option<u64> = c().await;
|
||||
c;
|
||||
// ^ || -> impl Future<Output = Option<u64>>
|
||||
// ^ impl Fn() -> impl Future<Output = Option<u64>>
|
||||
}
|
||||
"#,
|
||||
);
|
||||
@ -550,7 +550,7 @@ fn test() -> u64 {
|
||||
53..54 'a': S
|
||||
57..58 'S': S(fn(u32) -> u64) -> S
|
||||
57..74 'S(|i| ...s u64)': S
|
||||
59..73 '|i| 2*i as u64': |u32| -> u64
|
||||
59..73 '|i| 2*i as u64': impl Fn(u32) -> u64
|
||||
60..61 'i': u32
|
||||
63..64 '2': u64
|
||||
63..73 '2*i as u64': u64
|
||||
@ -1333,9 +1333,9 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
134..165 '{ ...(C)) }': (|&str, T| -> (), Bar<u8>)
|
||||
140..163 '(|inpu...ar(C))': (|&str, T| -> (), Bar<u8>)
|
||||
141..154 '|input, t| {}': |&str, T| -> ()
|
||||
134..165 '{ ...(C)) }': (impl Fn(&str, T), Bar<u8>)
|
||||
140..163 '(|inpu...ar(C))': (impl Fn(&str, T), Bar<u8>)
|
||||
141..154 '|input, t| {}': impl Fn(&str, T)
|
||||
142..147 'input': &str
|
||||
149..150 't': T
|
||||
152..154 '{}': ()
|
||||
@ -1506,8 +1506,8 @@ fn main() {
|
||||
71..105 '{ ...()); }': ()
|
||||
77..78 'f': fn f(&dyn Fn(S))
|
||||
77..102 'f(&|nu...foo())': ()
|
||||
79..101 '&|numb....foo()': &|S| -> ()
|
||||
80..101 '|numbe....foo()': |S| -> ()
|
||||
79..101 '&|numb....foo()': &impl Fn(S)
|
||||
80..101 '|numbe....foo()': impl Fn(S)
|
||||
81..87 'number': S
|
||||
89..95 'number': S
|
||||
89..101 'number.foo()': ()
|
||||
@ -1912,13 +1912,13 @@ fn test() {
|
||||
131..132 'f': F
|
||||
151..153 '{}': Lazy<T, F>
|
||||
251..497 '{ ...o(); }': ()
|
||||
261..266 'lazy1': Lazy<Foo, || -> Foo>
|
||||
283..292 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
|
||||
283..300 'Lazy::...| Foo)': Lazy<Foo, || -> Foo>
|
||||
293..299 '|| Foo': || -> Foo
|
||||
261..266 'lazy1': Lazy<Foo, impl Fn() -> Foo>
|
||||
283..292 'Lazy::new': fn new<Foo, impl Fn() -> Foo>(impl Fn() -> Foo) -> Lazy<Foo, impl Fn() -> Foo>
|
||||
283..300 'Lazy::...| Foo)': Lazy<Foo, impl Fn() -> Foo>
|
||||
293..299 '|| Foo': impl Fn() -> Foo
|
||||
296..299 'Foo': Foo
|
||||
310..312 'r1': usize
|
||||
315..320 'lazy1': Lazy<Foo, || -> Foo>
|
||||
315..320 'lazy1': Lazy<Foo, impl Fn() -> Foo>
|
||||
315..326 'lazy1.foo()': usize
|
||||
368..383 'make_foo_fn_ptr': fn() -> Foo
|
||||
399..410 'make_foo_fn': fn make_foo_fn() -> Foo
|
||||
@ -1963,20 +1963,20 @@ fn test() {
|
||||
163..167 '1u32': u32
|
||||
174..175 'x': Option<u32>
|
||||
174..190 'x.map(...v + 1)': Option<u32>
|
||||
180..189 '|v| v + 1': |u32| -> u32
|
||||
180..189 '|v| v + 1': impl Fn(u32) -> u32
|
||||
181..182 'v': u32
|
||||
184..185 'v': u32
|
||||
184..189 'v + 1': u32
|
||||
188..189 '1': u32
|
||||
196..197 'x': Option<u32>
|
||||
196..212 'x.map(... 1u64)': Option<u64>
|
||||
202..211 '|_v| 1u64': |u32| -> u64
|
||||
202..211 '|_v| 1u64': impl Fn(u32) -> u64
|
||||
203..205 '_v': u32
|
||||
207..211 '1u64': u64
|
||||
222..223 'y': Option<i64>
|
||||
239..240 'x': Option<u32>
|
||||
239..252 'x.map(|_v| 1)': Option<i64>
|
||||
245..251 '|_v| 1': |u32| -> i64
|
||||
245..251 '|_v| 1': impl Fn(u32) -> i64
|
||||
246..248 '_v': u32
|
||||
250..251 '1': i64
|
||||
"#]],
|
||||
@ -2005,11 +2005,11 @@ fn test<F: FnOnce(u32) -> u64>(f: F) {
|
||||
//^^^^ u64
|
||||
let g = |v| v + 1;
|
||||
//^^^^^ u64
|
||||
//^^^^^^^^^ |u64| -> u64
|
||||
//^^^^^^^^^ impl Fn(u64) -> u64
|
||||
g(1u64);
|
||||
//^^^^^^^ u64
|
||||
let h = |v| 1u128 + v;
|
||||
//^^^^^^^^^^^^^ |u128| -> u128
|
||||
//^^^^^^^^^^^^^ impl Fn(u128) -> u128
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
@ -2062,17 +2062,17 @@ fn test() {
|
||||
312..314 '{}': ()
|
||||
330..489 '{ ... S); }': ()
|
||||
340..342 'x1': u64
|
||||
345..349 'foo1': fn foo1<S, u64, |S| -> u64>(S, |S| -> u64) -> u64
|
||||
345..349 'foo1': fn foo1<S, u64, impl Fn(S) -> u64>(S, impl Fn(S) -> u64) -> u64
|
||||
345..368 'foo1(S...hod())': u64
|
||||
350..351 'S': S
|
||||
353..367 '|s| s.method()': |S| -> u64
|
||||
353..367 '|s| s.method()': impl Fn(S) -> u64
|
||||
354..355 's': S
|
||||
357..358 's': S
|
||||
357..367 's.method()': u64
|
||||
378..380 'x2': u64
|
||||
383..387 'foo2': fn foo2<S, u64, |S| -> u64>(|S| -> u64, S) -> u64
|
||||
383..387 'foo2': fn foo2<S, u64, impl Fn(S) -> u64>(impl Fn(S) -> u64, S) -> u64
|
||||
383..406 'foo2(|...(), S)': u64
|
||||
388..402 '|s| s.method()': |S| -> u64
|
||||
388..402 '|s| s.method()': impl Fn(S) -> u64
|
||||
389..390 's': S
|
||||
392..393 's': S
|
||||
392..402 's.method()': u64
|
||||
@ -2081,14 +2081,14 @@ fn test() {
|
||||
421..422 'S': S
|
||||
421..446 'S.foo1...hod())': u64
|
||||
428..429 'S': S
|
||||
431..445 '|s| s.method()': |S| -> u64
|
||||
431..445 '|s| s.method()': impl Fn(S) -> u64
|
||||
432..433 's': S
|
||||
435..436 's': S
|
||||
435..445 's.method()': u64
|
||||
456..458 'x4': u64
|
||||
461..462 'S': S
|
||||
461..486 'S.foo2...(), S)': u64
|
||||
468..482 '|s| s.method()': |S| -> u64
|
||||
468..482 '|s| s.method()': impl Fn(S) -> u64
|
||||
469..470 's': S
|
||||
472..473 's': S
|
||||
472..482 's.method()': u64
|
||||
@ -2562,9 +2562,9 @@ fn main() {
|
||||
72..74 '_v': F
|
||||
117..120 '{ }': ()
|
||||
132..163 '{ ... }); }': ()
|
||||
138..148 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ())
|
||||
138..148 'f::<(), _>': fn f<(), impl Fn(&())>(impl Fn(&()))
|
||||
138..160 'f::<()... z; })': ()
|
||||
149..159 '|z| { z; }': |&()| -> ()
|
||||
149..159 '|z| { z; }': impl Fn(&())
|
||||
150..151 'z': &()
|
||||
153..159 '{ z; }': ()
|
||||
155..156 'z': &()
|
||||
@ -2721,9 +2721,9 @@ fn main() {
|
||||
983..998 'Vec::<i32>::new': fn new<i32>() -> Vec<i32>
|
||||
983..1000 'Vec::<...:new()': Vec<i32>
|
||||
983..1012 'Vec::<...iter()': IntoIter<i32>
|
||||
983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>>
|
||||
983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, impl Fn(i32) -> Option<u32>>
|
||||
983..1101 'Vec::<... y; })': ()
|
||||
1029..1074 '|x| if...None }': |i32| -> Option<u32>
|
||||
1029..1074 '|x| if...None }': impl Fn(i32) -> Option<u32>
|
||||
1030..1031 'x': i32
|
||||
1033..1074 'if x >...None }': Option<u32>
|
||||
1036..1037 'x': i32
|
||||
@ -2736,7 +2736,7 @@ fn main() {
|
||||
1049..1057 'x as u32': u32
|
||||
1066..1074 '{ None }': Option<u32>
|
||||
1068..1072 'None': Option<u32>
|
||||
1090..1100 '|y| { y; }': |u32| -> ()
|
||||
1090..1100 '|y| { y; }': impl Fn(u32)
|
||||
1091..1092 'y': u32
|
||||
1094..1100 '{ y; }': ()
|
||||
1096..1097 'y': u32
|
||||
@ -2979,13 +2979,13 @@ fn foo() {
|
||||
52..126 '{ ...)(s) }': ()
|
||||
62..63 's': Option<i32>
|
||||
66..78 'Option::None': Option<i32>
|
||||
88..89 'f': |Option<i32>| -> ()
|
||||
92..111 '|x: Op...2>| {}': |Option<i32>| -> ()
|
||||
88..89 'f': impl Fn(Option<i32>)
|
||||
92..111 '|x: Op...2>| {}': impl Fn(Option<i32>)
|
||||
93..94 'x': Option<i32>
|
||||
109..111 '{}': ()
|
||||
117..124 '(&f)(s)': ()
|
||||
118..120 '&f': &|Option<i32>| -> ()
|
||||
119..120 'f': |Option<i32>| -> ()
|
||||
118..120 '&f': &impl Fn(Option<i32>)
|
||||
119..120 'f': impl Fn(Option<i32>)
|
||||
122..123 's': Option<i32>
|
||||
"#]],
|
||||
);
|
||||
@ -3072,15 +3072,15 @@ fn foo() {
|
||||
228..229 's': Option<i32>
|
||||
232..236 'None': Option<i32>
|
||||
246..247 'f': Box<dyn FnOnce(&Option<i32>)>
|
||||
281..294 'box (|ps| {})': Box<|&Option<i32>| -> ()>
|
||||
286..293 '|ps| {}': |&Option<i32>| -> ()
|
||||
281..294 'box (|ps| {})': Box<impl Fn(&Option<i32>)>
|
||||
286..293 '|ps| {}': impl Fn(&Option<i32>)
|
||||
287..289 'ps': &Option<i32>
|
||||
291..293 '{}': ()
|
||||
300..301 'f': Box<dyn FnOnce(&Option<i32>)>
|
||||
300..305 'f(&s)': ()
|
||||
302..304 '&s': &Option<i32>
|
||||
303..304 's': Option<i32>
|
||||
281..294: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|&Option<i32>| -> ()>
|
||||
281..294: expected Box<dyn FnOnce(&Option<i32>)>, got Box<impl Fn(&Option<i32>)>
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
@ -3811,6 +3811,35 @@ fn f() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_14443_trait_solve() {
|
||||
check_no_mismatches(
|
||||
r#"
|
||||
trait T {
|
||||
fn f(&self) {}
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
struct A;
|
||||
impl T for A {}
|
||||
|
||||
let a = A;
|
||||
|
||||
let b = {
|
||||
struct B;
|
||||
impl T for B {}
|
||||
|
||||
B
|
||||
};
|
||||
|
||||
a.f();
|
||||
b.f();
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn associated_type_sized_bounds() {
|
||||
check_infer(
|
||||
@ -4286,3 +4315,63 @@ impl Trait for () {
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_macro_bounds() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: clone, derive
|
||||
#[derive(Clone)]
|
||||
struct Copy;
|
||||
struct NotCopy;
|
||||
#[derive(Clone)]
|
||||
struct Generic<T>(T);
|
||||
trait Tr {
|
||||
type Assoc;
|
||||
}
|
||||
impl Tr for Copy {
|
||||
type Assoc = NotCopy;
|
||||
}
|
||||
#[derive(Clone)]
|
||||
struct AssocGeneric<T: Tr>(T::Assoc);
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AssocGeneric2<T: Tr>(<T as Tr>::Assoc);
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AssocGeneric3<T: Tr>(Generic<T::Assoc>);
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Vec<T>();
|
||||
|
||||
#[derive(Clone)]
|
||||
struct R1(Vec<R2>);
|
||||
#[derive(Clone)]
|
||||
struct R2(R1);
|
||||
|
||||
fn f() {
|
||||
let x = (&Copy).clone();
|
||||
//^ Copy
|
||||
let x = (&NotCopy).clone();
|
||||
//^ &NotCopy
|
||||
let x = (&Generic(Copy)).clone();
|
||||
//^ Generic<Copy>
|
||||
let x = (&Generic(NotCopy)).clone();
|
||||
//^ &Generic<NotCopy>
|
||||
let x: &AssocGeneric<Copy> = &AssocGeneric(NotCopy);
|
||||
let x = x.clone();
|
||||
//^ &AssocGeneric<Copy>
|
||||
let x: &AssocGeneric2<Copy> = &AssocGeneric2(NotCopy);
|
||||
let x = x.clone();
|
||||
//^ &AssocGeneric2<Copy>
|
||||
let x: &AssocGeneric3<Copy> = &AssocGeneric3(Generic(NotCopy));
|
||||
let x = x.clone();
|
||||
//^ &AssocGeneric3<Copy>
|
||||
let x = (&R1(Vec())).clone();
|
||||
//^ R1
|
||||
let x = (&R2(R1(Vec()))).clone();
|
||||
//^ R2
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use std::{env::var, sync::Arc};
|
||||
|
||||
use chalk_ir::GoalData;
|
||||
use chalk_recursive::Cache;
|
||||
use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver};
|
||||
use chalk_solve::{logging_db::LoggingRustIrDatabase, rust_ir, Solver};
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_def::{
|
||||
@ -177,8 +177,10 @@ fn is_chalk_print() -> bool {
|
||||
std::env::var("CHALK_PRINT").is_ok()
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum FnTrait {
|
||||
// Warning: Order is important. If something implements `x` it should also implement
|
||||
// `y` if `y <= x`.
|
||||
FnOnce,
|
||||
FnMut,
|
||||
Fn,
|
||||
@ -193,6 +195,14 @@ impl FnTrait {
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn to_chalk_ir(self) -> rust_ir::ClosureKind {
|
||||
match self {
|
||||
FnTrait::FnOnce => rust_ir::ClosureKind::FnOnce,
|
||||
FnTrait::FnMut => rust_ir::ClosureKind::FnMut,
|
||||
FnTrait::Fn => rust_ir::ClosureKind::Fn,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn method_name(self) -> Name {
|
||||
match self {
|
||||
FnTrait::FnOnce => name!(call_once),
|
||||
|
@ -4,7 +4,7 @@
|
||||
use std::iter;
|
||||
|
||||
use base_db::CrateId;
|
||||
use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex};
|
||||
use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex, Mutability};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
db::DefDatabase,
|
||||
@ -12,6 +12,7 @@ use hir_def::{
|
||||
GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
|
||||
WherePredicateTypeTarget,
|
||||
},
|
||||
hir::BindingAnnotation,
|
||||
lang_item::LangItem,
|
||||
resolver::{HasResolver, TypeNs},
|
||||
type_ref::{TraitBoundModifier, TypeRef},
|
||||
@ -24,7 +25,8 @@ use rustc_hash::FxHashSet;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, WhereClause,
|
||||
db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyExt,
|
||||
WhereClause,
|
||||
};
|
||||
|
||||
pub(crate) fn fn_traits(
|
||||
@ -352,3 +354,20 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn pattern_matching_dereference_count(
|
||||
cond_ty: &mut Ty,
|
||||
binding_mode: &mut BindingAnnotation,
|
||||
) -> usize {
|
||||
let mut r = 0;
|
||||
while let Some((ty, _, mu)) = cond_ty.as_reference() {
|
||||
if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
|
||||
*binding_mode = BindingAnnotation::RefMut;
|
||||
} else {
|
||||
*binding_mode = BindingAnnotation::Ref;
|
||||
}
|
||||
*cond_ty = ty.clone();
|
||||
r += 1;
|
||||
}
|
||||
r
|
||||
}
|
||||
|
@ -50,7 +50,9 @@ diagnostics![
|
||||
PrivateField,
|
||||
ReplaceFilterMapNextWithFindMap,
|
||||
TypeMismatch,
|
||||
UndeclaredLabel,
|
||||
UnimplementedBuiltinMacro,
|
||||
UnreachableLabel,
|
||||
UnresolvedExternCrate,
|
||||
UnresolvedField,
|
||||
UnresolvedImport,
|
||||
@ -61,6 +63,13 @@ diagnostics![
|
||||
UnusedMut,
|
||||
];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BreakOutsideOfLoop {
|
||||
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||
pub is_break: bool,
|
||||
pub bad_value_break: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnresolvedModule {
|
||||
pub decl: InFile<AstPtr<ast::Module>>,
|
||||
@ -84,6 +93,17 @@ pub struct UnresolvedMacroCall {
|
||||
pub path: ModPath,
|
||||
pub is_bang: bool,
|
||||
}
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct UnreachableLabel {
|
||||
pub node: InFile<AstPtr<ast::Lifetime>>,
|
||||
pub name: Name,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct UndeclaredLabel {
|
||||
pub node: InFile<AstPtr<ast::Lifetime>>,
|
||||
pub name: Name,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct InactiveCode {
|
||||
@ -166,13 +186,6 @@ pub struct PrivateField {
|
||||
pub field: Field,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BreakOutsideOfLoop {
|
||||
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||
pub is_break: bool,
|
||||
pub bad_value_break: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MissingUnsafe {
|
||||
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! HirDisplay implementations for various hir types.
|
||||
use hir_def::{
|
||||
adt::VariantData,
|
||||
data::adt::VariantData,
|
||||
generics::{
|
||||
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
|
||||
},
|
||||
@ -8,6 +8,7 @@ use hir_def::{
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
AdtId, GenericDefId,
|
||||
};
|
||||
use hir_expand::name;
|
||||
use hir_ty::{
|
||||
display::{
|
||||
write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
|
||||
@ -76,22 +77,22 @@ impl HirDisplay for Function {
|
||||
};
|
||||
|
||||
let mut first = true;
|
||||
for (name, type_ref) in &data.params {
|
||||
// FIXME: Use resolved `param.ty` once we no longer discard lifetimes
|
||||
for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)) {
|
||||
let local = param.as_local(db).map(|it| it.name(db));
|
||||
if !first {
|
||||
f.write_str(", ")?;
|
||||
} else {
|
||||
first = false;
|
||||
if data.has_self_param() {
|
||||
if local == Some(name!(self)) {
|
||||
write_self_param(type_ref, f)?;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
match name {
|
||||
match local {
|
||||
Some(name) => write!(f, "{name}: ")?,
|
||||
None => f.write_str("_: ")?,
|
||||
}
|
||||
// FIXME: Use resolved `param.ty` or raw `type_ref`?
|
||||
// The former will ignore lifetime arguments currently.
|
||||
type_ref.hir_fmt(f)?;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
//! are splitting the hir.
|
||||
|
||||
use hir_def::{
|
||||
expr::{BindingId, LabelId},
|
||||
hir::{BindingId, LabelId},
|
||||
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
|
||||
ModuleDefId, VariantId,
|
||||
};
|
||||
|
@ -39,10 +39,10 @@ use arrayvec::ArrayVec;
|
||||
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
adt::VariantData,
|
||||
body::{BodyDiagnostic, SyntheticSyntax},
|
||||
expr::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
|
||||
data::adt::VariantData,
|
||||
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
|
||||
hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
|
||||
item_tree::ItemTreeNode,
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
layout::{Layout, LayoutError, ReprOptions},
|
||||
@ -88,9 +88,10 @@ pub use crate::{
|
||||
AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
|
||||
IncorrectCase, InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount,
|
||||
MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem,
|
||||
PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
|
||||
UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
|
||||
UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
|
||||
PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UndeclaredLabel,
|
||||
UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
|
||||
UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
|
||||
UnresolvedProcMacro, UnusedMut,
|
||||
},
|
||||
has_source::HasSource,
|
||||
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
|
||||
@ -108,9 +109,8 @@ pub use crate::{
|
||||
pub use {
|
||||
cfg::{CfgAtom, CfgExpr, CfgOptions},
|
||||
hir_def::{
|
||||
adt::StructKind,
|
||||
attr::{Attrs, AttrsWithOwner, Documentation},
|
||||
builtin_attr::AttributeTemplate,
|
||||
attr::{builtin::AttributeTemplate, Attrs, AttrsWithOwner, Documentation},
|
||||
data::adt::StructKind,
|
||||
find_path::PrefixKind,
|
||||
import_map,
|
||||
nameres::ModuleSource,
|
||||
@ -129,7 +129,7 @@ pub use {
|
||||
ExpandResult, HirFileId, InFile, MacroFile, Origin,
|
||||
},
|
||||
hir_ty::{
|
||||
display::{HirDisplay, HirDisplayError, HirWrite},
|
||||
display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
|
||||
mir::MirEvalError,
|
||||
PointerCast, Safety,
|
||||
},
|
||||
@ -1393,6 +1393,12 @@ impl DefWithBody {
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
BodyDiagnostic::UnreachableLabel { node, name } => {
|
||||
acc.push(UnreachableLabel { node: node.clone(), name: name.clone() }.into())
|
||||
}
|
||||
BodyDiagnostic::UndeclaredLabel { node, name } => {
|
||||
acc.push(UndeclaredLabel { node: node.clone(), name: name.clone() }.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1405,14 +1411,6 @@ impl DefWithBody {
|
||||
let field = source_map.field_syntax(expr);
|
||||
acc.push(NoSuchField { field }.into())
|
||||
}
|
||||
&hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
|
||||
expr,
|
||||
is_break,
|
||||
bad_value_break,
|
||||
} => {
|
||||
let expr = expr_syntax(expr);
|
||||
acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
|
||||
}
|
||||
&hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
|
||||
acc.push(
|
||||
MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found }
|
||||
@ -1484,6 +1482,14 @@ impl DefWithBody {
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
&hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
|
||||
expr,
|
||||
is_break,
|
||||
bad_value_break,
|
||||
} => {
|
||||
let expr = expr_syntax(expr);
|
||||
acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
for (pat_or_expr, mismatch) in infer.type_mismatches() {
|
||||
@ -1524,35 +1530,44 @@ impl DefWithBody {
|
||||
|
||||
let hir_body = db.body(self.into());
|
||||
|
||||
if let Ok(borrowck_result) = db.borrowck(self.into()) {
|
||||
let mir_body = &borrowck_result.mir_body;
|
||||
let mol = &borrowck_result.mutability_of_locals;
|
||||
for (binding_id, _) in hir_body.bindings.iter() {
|
||||
let need_mut = &mol[mir_body.binding_locals[binding_id]];
|
||||
let local = Local { parent: self.into(), binding_id };
|
||||
match (need_mut, local.is_mut(db)) {
|
||||
(mir::MutabilityReason::Mut { .. }, true)
|
||||
| (mir::MutabilityReason::Not, false) => (),
|
||||
(mir::MutabilityReason::Mut { spans }, false) => {
|
||||
for span in spans {
|
||||
let span: InFile<SyntaxNodePtr> = match span {
|
||||
mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
|
||||
Ok(s) => s.map(|x| x.into()),
|
||||
Err(_) => continue,
|
||||
},
|
||||
mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
|
||||
Ok(s) => s.map(|x| match x {
|
||||
Either::Left(e) => e.into(),
|
||||
Either::Right(e) => e.into(),
|
||||
}),
|
||||
Err(_) => continue,
|
||||
},
|
||||
mir::MirSpan::Unknown => continue,
|
||||
};
|
||||
acc.push(NeedMut { local, span }.into());
|
||||
if let Ok(borrowck_results) = db.borrowck(self.into()) {
|
||||
for borrowck_result in borrowck_results.iter() {
|
||||
let mir_body = &borrowck_result.mir_body;
|
||||
let mol = &borrowck_result.mutability_of_locals;
|
||||
for (binding_id, _) in hir_body.bindings.iter() {
|
||||
let Some(&local) = mir_body.binding_locals.get(binding_id) else {
|
||||
continue;
|
||||
};
|
||||
let need_mut = &mol[local];
|
||||
let local = Local { parent: self.into(), binding_id };
|
||||
match (need_mut, local.is_mut(db)) {
|
||||
(mir::MutabilityReason::Mut { .. }, true)
|
||||
| (mir::MutabilityReason::Not, false) => (),
|
||||
(mir::MutabilityReason::Mut { spans }, false) => {
|
||||
for span in spans {
|
||||
let span: InFile<SyntaxNodePtr> = match span {
|
||||
mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
|
||||
Ok(s) => s.map(|x| x.into()),
|
||||
Err(_) => continue,
|
||||
},
|
||||
mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
|
||||
Ok(s) => s.map(|x| match x {
|
||||
Either::Left(e) => e.into(),
|
||||
Either::Right(e) => e.into(),
|
||||
}),
|
||||
Err(_) => continue,
|
||||
},
|
||||
mir::MirSpan::Unknown => continue,
|
||||
};
|
||||
acc.push(NeedMut { local, span }.into());
|
||||
}
|
||||
}
|
||||
(mir::MutabilityReason::Not, true) => {
|
||||
if !infer.mutated_bindings_in_closure.contains(&binding_id) {
|
||||
acc.push(UnusedMut { local }.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
(mir::MutabilityReason::Not, true) => acc.push(UnusedMut { local }.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1838,7 +1853,7 @@ impl Param {
|
||||
}
|
||||
|
||||
pub fn name(&self, db: &dyn HirDatabase) -> Option<Name> {
|
||||
db.function_data(self.func.id).params[self.idx].0.clone()
|
||||
Some(self.as_local(db)?.name(db))
|
||||
}
|
||||
|
||||
pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
|
||||
@ -1879,7 +1894,7 @@ impl SelfParam {
|
||||
func_data
|
||||
.params
|
||||
.first()
|
||||
.map(|(_, param)| match &**param {
|
||||
.map(|param| match &**param {
|
||||
TypeRef::Reference(.., mutability) => match mutability {
|
||||
hir_def::type_ref::Mutability::Shared => Access::Shared,
|
||||
hir_def::type_ref::Mutability::Mut => Access::Exclusive,
|
||||
@ -2690,9 +2705,7 @@ impl BuiltinAttr {
|
||||
}
|
||||
|
||||
fn builtin(name: &str) -> Option<Self> {
|
||||
hir_def::builtin_attr::INERT_ATTRIBUTES
|
||||
.iter()
|
||||
.position(|tool| tool.name == name)
|
||||
hir_def::attr::builtin::find_builtin_attr_idx(name)
|
||||
.map(|idx| BuiltinAttr { krate: None, idx: idx as u32 })
|
||||
}
|
||||
|
||||
@ -2700,14 +2713,14 @@ impl BuiltinAttr {
|
||||
// FIXME: Return a `Name` here
|
||||
match self.krate {
|
||||
Some(krate) => db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(),
|
||||
None => SmolStr::new(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].name),
|
||||
None => SmolStr::new(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn template(&self, _: &dyn HirDatabase) -> Option<AttributeTemplate> {
|
||||
match self.krate {
|
||||
Some(_) => None,
|
||||
None => Some(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].template),
|
||||
None => Some(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].template),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2730,7 +2743,7 @@ impl ToolModule {
|
||||
}
|
||||
|
||||
fn builtin(name: &str) -> Option<Self> {
|
||||
hir_def::builtin_attr::TOOL_MODULES
|
||||
hir_def::attr::builtin::TOOL_MODULES
|
||||
.iter()
|
||||
.position(|&tool| tool == name)
|
||||
.map(|idx| ToolModule { krate: None, idx: idx as u32 })
|
||||
@ -2740,7 +2753,7 @@ impl ToolModule {
|
||||
// FIXME: Return a `Name` here
|
||||
match self.krate {
|
||||
Some(krate) => db.crate_def_map(krate).registered_tools()[self.idx as usize].clone(),
|
||||
None => SmolStr::new(hir_def::builtin_attr::TOOL_MODULES[self.idx as usize]),
|
||||
None => SmolStr::new(hir_def::attr::builtin::TOOL_MODULES[self.idx as usize]),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3379,7 +3392,12 @@ impl Type {
|
||||
}
|
||||
|
||||
pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
|
||||
let mut the_ty = &self.ty;
|
||||
let callee = match self.ty.kind(Interner) {
|
||||
TyKind::Ref(_, _, ty) if ty.as_closure().is_some() => {
|
||||
the_ty = ty;
|
||||
Callee::Closure(ty.as_closure().unwrap())
|
||||
}
|
||||
TyKind::Closure(id, _) => Callee::Closure(*id),
|
||||
TyKind::Function(_) => Callee::FnPtr,
|
||||
TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?),
|
||||
@ -3394,7 +3412,7 @@ impl Type {
|
||||
}
|
||||
};
|
||||
|
||||
let sig = self.ty.callable_sig(db)?;
|
||||
let sig = the_ty.callable_sig(db)?;
|
||||
Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false })
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use base_db::{FileId, FileRange};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
body,
|
||||
expr::Expr,
|
||||
hir::Expr,
|
||||
macro_id_to_def_id,
|
||||
resolver::{self, HasResolver, Resolver, TypeNs},
|
||||
type_ref::Mutability,
|
||||
@ -1065,7 +1065,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
|
||||
fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
|
||||
let analyze = self.analyze(ty.syntax())?;
|
||||
let ctx = body::LowerCtx::new(self.db.upcast(), analyze.file_id);
|
||||
let ctx = body::LowerCtx::with_file_id(self.db.upcast(), analyze.file_id);
|
||||
let ty = hir_ty::TyLoweringContext::new(self.db, &analyze.resolver)
|
||||
.lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone()));
|
||||
Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
|
||||
@ -1672,7 +1672,7 @@ impl<'a> SemanticsScope<'a> {
|
||||
/// Resolve a path as-if it was written at the given scope. This is
|
||||
/// necessary a heuristic, as it doesn't take hygiene into account.
|
||||
pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
|
||||
let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id);
|
||||
let ctx = body::LowerCtx::with_file_id(self.db.upcast(), self.file_id);
|
||||
let path = Path::from_src(path.clone(), &ctx)?;
|
||||
resolve_hir_path(self.db, &self.resolver, &path)
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
//! expression, an item definition.
|
||||
//!
|
||||
//! Knowing only the syntax gives us relatively little info. For example,
|
||||
//! looking at the syntax of the function we can realise that it is a part of an
|
||||
//! looking at the syntax of the function we can realize that it is a part of an
|
||||
//! `impl` block, but we won't be able to tell what trait function the current
|
||||
//! function overrides, and whether it does that correctly. For that, we need to
|
||||
//! go from [`ast::Fn`] to [`crate::Function`], and that's exactly what this
|
||||
@ -88,9 +88,11 @@
|
||||
use base_db::FileId;
|
||||
use hir_def::{
|
||||
child_by_source::ChildBySource,
|
||||
dyn_map::DynMap,
|
||||
expr::{BindingId, LabelId},
|
||||
keys::{self, Key},
|
||||
dyn_map::{
|
||||
keys::{self, Key},
|
||||
DynMap,
|
||||
},
|
||||
hir::{BindingId, LabelId},
|
||||
AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId,
|
||||
GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId,
|
||||
TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
|
||||
|
@ -17,7 +17,7 @@ use hir_def::{
|
||||
scope::{ExprScopes, ScopeId},
|
||||
Body, BodySourceMap,
|
||||
},
|
||||
expr::{ExprId, Pat, PatId},
|
||||
hir::{ExprId, Pat, PatId},
|
||||
lang_item::LangItem,
|
||||
macro_id_to_def_id,
|
||||
path::{ModPath, Path, PathKind},
|
||||
@ -463,7 +463,7 @@ impl SourceAnalyzer {
|
||||
db: &dyn HirDatabase,
|
||||
macro_call: InFile<&ast::MacroCall>,
|
||||
) -> Option<Macro> {
|
||||
let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id);
|
||||
let ctx = body::LowerCtx::with_file_id(db.upcast(), macro_call.file_id);
|
||||
let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
|
||||
self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()?).map(|it| it.into())
|
||||
}
|
||||
|
@ -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 = ¶m_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");
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -70,6 +70,11 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
|
||||
}
|
||||
|
||||
let node = ctx.covering_element();
|
||||
if matches!(node.kind(), T!['{'] | T!['}'] | T!['('] | T![')'] | T!['['] | T![']']) {
|
||||
cov_mark::hit!(extract_function_in_braces_is_not_applicable);
|
||||
return None;
|
||||
}
|
||||
|
||||
if node.kind() == COMMENT {
|
||||
cov_mark::hit!(extract_function_in_comment_is_not_applicable);
|
||||
return None;
|
||||
@ -5800,4 +5805,40 @@ fn $0fun_name() -> ControlFlow<()> {
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_left_curly_is_not_applicable() {
|
||||
cov_mark::check!(extract_function_in_braces_is_not_applicable);
|
||||
check_assist_not_applicable(extract_function, r"fn foo() { $0}$0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_right_curly_is_not_applicable() {
|
||||
cov_mark::check!(extract_function_in_braces_is_not_applicable);
|
||||
check_assist_not_applicable(extract_function, r"fn foo() $0{$0 }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_left_paren_is_not_applicable() {
|
||||
cov_mark::check!(extract_function_in_braces_is_not_applicable);
|
||||
check_assist_not_applicable(extract_function, r"fn foo( $0)$0 { }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_right_paren_is_not_applicable() {
|
||||
cov_mark::check!(extract_function_in_braces_is_not_applicable);
|
||||
check_assist_not_applicable(extract_function, r"fn foo $0($0 ) { }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_left_brack_is_not_applicable() {
|
||||
cov_mark::check!(extract_function_in_braces_is_not_applicable);
|
||||
check_assist_not_applicable(extract_function, r"fn foo(arr: &mut [i32$0]$0) {}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_right_brack_is_not_applicable() {
|
||||
cov_mark::check!(extract_function_in_braces_is_not_applicable);
|
||||
check_assist_not_applicable(extract_function, r"fn foo(arr: &mut $0[$0i32]) {}");
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use hir::TypeInfo;
|
||||
use stdx::format_to;
|
||||
use syntax::{
|
||||
ast::{self, AstNode},
|
||||
@ -46,21 +47,24 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
|
||||
.take_while(|it| ctx.selection_trimmed().contains_range(it.text_range()))
|
||||
.find_map(valid_target_expr)?;
|
||||
|
||||
if let Some(ty_info) = ctx.sema.type_of_expr(&to_extract) {
|
||||
if ty_info.adjusted().is_unit() {
|
||||
return None;
|
||||
}
|
||||
let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted);
|
||||
if matches!(&ty, Some(ty_info) if ty_info.is_unit()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let reference_modifier = match get_receiver_type(ctx, &to_extract) {
|
||||
let parent = to_extract.syntax().parent().and_then(ast::Expr::cast);
|
||||
let needs_adjust = parent
|
||||
.as_ref()
|
||||
.map_or(false, |it| matches!(it, ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_)));
|
||||
|
||||
let reference_modifier = match ty.filter(|_| needs_adjust) {
|
||||
Some(receiver_type) if receiver_type.is_mutable_reference() => "&mut ",
|
||||
Some(receiver_type) if receiver_type.is_reference() => "&",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
let parent_ref_expr = to_extract.syntax().parent().and_then(ast::RefExpr::cast);
|
||||
let var_modifier = match parent_ref_expr {
|
||||
Some(expr) if expr.mut_token().is_some() => "mut ",
|
||||
let var_modifier = match parent {
|
||||
Some(ast::Expr::RefExpr(expr)) if expr.mut_token().is_some() => "mut ",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
@ -164,22 +168,6 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_receiver_type(ctx: &AssistContext<'_>, expression: &ast::Expr) -> Option<hir::Type> {
|
||||
let receiver = get_receiver(expression.clone())?;
|
||||
Some(ctx.sema.type_of_expr(&receiver)?.original())
|
||||
}
|
||||
|
||||
/// In the expression `a.b.c.x()`, find `a`
|
||||
fn get_receiver(expression: ast::Expr) -> Option<ast::Expr> {
|
||||
match expression {
|
||||
ast::Expr::FieldExpr(field) if field.expr().is_some() => {
|
||||
let nested_expression = &field.expr()?;
|
||||
get_receiver(nested_expression.to_owned())
|
||||
}
|
||||
_ => Some(expression),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Anchor {
|
||||
Before(SyntaxNode),
|
||||
@ -944,6 +932,11 @@ struct S {
|
||||
vec: Vec<u8>
|
||||
}
|
||||
|
||||
struct Vec<T>;
|
||||
impl<T> Vec<T> {
|
||||
fn push(&mut self, _:usize) {}
|
||||
}
|
||||
|
||||
fn foo(s: &mut S) {
|
||||
$0s.vec$0.push(0);
|
||||
}"#,
|
||||
@ -952,6 +945,11 @@ struct S {
|
||||
vec: Vec<u8>
|
||||
}
|
||||
|
||||
struct Vec<T>;
|
||||
impl<T> Vec<T> {
|
||||
fn push(&mut self, _:usize) {}
|
||||
}
|
||||
|
||||
fn foo(s: &mut S) {
|
||||
let $0vec = &mut s.vec;
|
||||
vec.push(0);
|
||||
@ -973,6 +971,10 @@ struct X {
|
||||
struct S {
|
||||
vec: Vec<u8>
|
||||
}
|
||||
struct Vec<T>;
|
||||
impl<T> Vec<T> {
|
||||
fn push(&mut self, _:usize) {}
|
||||
}
|
||||
|
||||
fn foo(f: &mut Y) {
|
||||
$0f.field.field.vec$0.push(0);
|
||||
@ -987,6 +989,10 @@ struct X {
|
||||
struct S {
|
||||
vec: Vec<u8>
|
||||
}
|
||||
struct Vec<T>;
|
||||
impl<T> Vec<T> {
|
||||
fn push(&mut self, _:usize) {}
|
||||
}
|
||||
|
||||
fn foo(f: &mut Y) {
|
||||
let $0vec = &mut f.field.field.vec;
|
||||
@ -1123,7 +1129,7 @@ struct S {
|
||||
}
|
||||
|
||||
fn foo(s: S) {
|
||||
let $0x = s.sub;
|
||||
let $0x = &s.sub;
|
||||
x.do_thing();
|
||||
}"#,
|
||||
);
|
||||
@ -1189,7 +1195,7 @@ impl X {
|
||||
|
||||
fn foo() {
|
||||
let local = &mut S::new();
|
||||
let $0x = &mut local.sub;
|
||||
let $0x = &local.sub;
|
||||
x.do_thing();
|
||||
}"#,
|
||||
);
|
||||
|
@ -1910,7 +1910,6 @@ fn bar(new: fn) ${0:-> _} {
|
||||
|
||||
#[test]
|
||||
fn add_function_with_closure_arg() {
|
||||
// FIXME: The argument in `bar` is wrong.
|
||||
check_assist(
|
||||
generate_function,
|
||||
r"
|
||||
@ -1925,7 +1924,7 @@ fn foo() {
|
||||
bar(closure)
|
||||
}
|
||||
|
||||
fn bar(closure: _) {
|
||||
fn bar(closure: impl Fn(i64) -> i64) {
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
|
@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
// }
|
||||
//
|
||||
// struct Bar;
|
||||
// $0impl Foo for Bar {
|
||||
// $0impl Foo for Bar$0 {
|
||||
// const B: u8 = 17;
|
||||
// fn c() {}
|
||||
// type A = String;
|
||||
@ -45,6 +45,16 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||
let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
|
||||
let items = impl_ast.assoc_item_list()?;
|
||||
|
||||
// restrict the range
|
||||
// if cursor is in assoc_items, abort
|
||||
let assoc_range = items.syntax().text_range();
|
||||
let cursor_position = ctx.offset();
|
||||
if assoc_range.contains_inclusive(cursor_position) {
|
||||
cov_mark::hit!(not_applicable_editing_assoc_items);
|
||||
return None;
|
||||
}
|
||||
|
||||
let assoc_items = items.assoc_items().collect::<Vec<_>>();
|
||||
|
||||
let path = impl_ast
|
||||
@ -264,9 +274,9 @@ trait Bar {
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
impl Bar for Foo {
|
||||
$0impl Bar for Foo {
|
||||
type Fooo = ();
|
||||
type Foo = ();$0
|
||||
type Foo = ();
|
||||
}"#,
|
||||
r#"
|
||||
trait Bar {
|
||||
@ -281,4 +291,29 @@ impl Bar for Foo {
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_applicable_editing_assoc_items() {
|
||||
cov_mark::check!(not_applicable_editing_assoc_items);
|
||||
check_assist_not_applicable(
|
||||
reorder_impl_items,
|
||||
r#"
|
||||
trait Bar {
|
||||
type T;
|
||||
const C: ();
|
||||
fn a() {}
|
||||
fn z() {}
|
||||
fn b() {}
|
||||
}
|
||||
struct Foo;
|
||||
impl Bar for Foo {
|
||||
type T = ();$0
|
||||
const C: () = ();
|
||||
fn z() {}
|
||||
fn a() {}
|
||||
fn b() {}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -116,9 +116,11 @@ trait AddRewrite {
|
||||
new: Vec<T>,
|
||||
target: TextRange,
|
||||
) -> Option<()>;
|
||||
fn yeet() {}
|
||||
}
|
||||
|
||||
impl AddRewrite for Assists {
|
||||
fn yeet() {}
|
||||
fn add_rewrite<T: AstNode>(
|
||||
&mut self,
|
||||
label: &str,
|
||||
|
@ -122,6 +122,7 @@ mod handlers {
|
||||
mod convert_iter_for_each_to_for;
|
||||
mod convert_let_else_to_match;
|
||||
mod convert_match_to_let_else;
|
||||
mod convert_nested_function_to_closure;
|
||||
mod convert_tuple_struct_to_named_struct;
|
||||
mod convert_named_struct_to_tuple_struct;
|
||||
mod convert_to_guarded_return;
|
||||
@ -228,8 +229,9 @@ mod handlers {
|
||||
convert_iter_for_each_to_for::convert_iter_for_each_to_for,
|
||||
convert_iter_for_each_to_for::convert_for_loop_with_for_each,
|
||||
convert_let_else_to_match::convert_let_else_to_match,
|
||||
convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
|
||||
convert_match_to_let_else::convert_match_to_let_else,
|
||||
convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
|
||||
convert_nested_function_to_closure::convert_nested_function_to_closure,
|
||||
convert_to_guarded_return::convert_to_guarded_return,
|
||||
convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
|
||||
convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro,
|
||||
|
@ -494,6 +494,31 @@ impl Point {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_convert_nested_function_to_closure() {
|
||||
check_doc_test(
|
||||
"convert_nested_function_to_closure",
|
||||
r#####"
|
||||
fn main() {
|
||||
fn fo$0o(label: &str, number: u64) {
|
||||
println!("{}: {}", label, number);
|
||||
}
|
||||
|
||||
foo("Bar", 100);
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
let foo = |label: &str, number: u64| {
|
||||
println!("{}: {}", label, number);
|
||||
};
|
||||
|
||||
foo("Bar", 100);
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_convert_to_guarded_return() {
|
||||
check_doc_test(
|
||||
@ -2116,7 +2141,7 @@ trait Foo {
|
||||
}
|
||||
|
||||
struct Bar;
|
||||
$0impl Foo for Bar {
|
||||
$0impl Foo for Bar$0 {
|
||||
const B: u8 = 17;
|
||||
fn c() {}
|
||||
type A = String;
|
||||
|
@ -90,8 +90,6 @@ impl Assist {
|
||||
let comment_blocks = sourcegen::CommentBlock::extract("Assist", &text);
|
||||
|
||||
for block in comment_blocks {
|
||||
// FIXME: doesn't support blank lines yet, need to tweak
|
||||
// `extract_comment_blocks` for that.
|
||||
let id = block.id;
|
||||
assert!(
|
||||
id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
|
||||
|
@ -23,7 +23,7 @@ pub(crate) mod env_vars;
|
||||
|
||||
use std::iter;
|
||||
|
||||
use hir::{known, ScopeDef, Variant};
|
||||
use hir::{known, HasAttrs, ScopeDef, Variant};
|
||||
use ide_db::{imports::import_assets::LocatedImport, SymbolKind};
|
||||
use syntax::ast;
|
||||
|
||||
@ -173,6 +173,9 @@ impl Completions {
|
||||
resolution: hir::ScopeDef,
|
||||
doc_aliases: Vec<syntax::SmolStr>,
|
||||
) {
|
||||
if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.def_is_visible(&resolution) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
@ -198,6 +201,9 @@ impl Completions {
|
||||
local_name: hir::Name,
|
||||
resolution: hir::ScopeDef,
|
||||
) {
|
||||
if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.def_is_visible(&resolution) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
@ -220,6 +226,9 @@ impl Completions {
|
||||
path_ctx: &PathCompletionCtx,
|
||||
e: hir::Enum,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&e.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
e.variants(ctx.db)
|
||||
.into_iter()
|
||||
.for_each(|variant| self.add_enum_variant(ctx, path_ctx, variant, None));
|
||||
@ -233,6 +242,9 @@ impl Completions {
|
||||
local_name: hir::Name,
|
||||
doc_aliases: Vec<syntax::SmolStr>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&module.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
self.add_path_resolution(
|
||||
ctx,
|
||||
path_ctx,
|
||||
@ -249,6 +261,9 @@ impl Completions {
|
||||
mac: hir::Macro,
|
||||
local_name: hir::Name,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&mac.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&mac) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
@ -272,6 +287,9 @@ impl Completions {
|
||||
func: hir::Function,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&func) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
@ -296,6 +314,9 @@ impl Completions {
|
||||
receiver: Option<hir::Name>,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&func) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
@ -320,6 +341,9 @@ impl Completions {
|
||||
func: hir::Function,
|
||||
import: LocatedImport,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&func) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
@ -340,6 +364,9 @@ impl Completions {
|
||||
}
|
||||
|
||||
pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
|
||||
if !ctx.check_stability(Some(&konst.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&konst) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
@ -356,6 +383,9 @@ impl Completions {
|
||||
ctx: &CompletionContext<'_>,
|
||||
type_alias: hir::TypeAlias,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&type_alias) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
@ -372,6 +402,9 @@ impl Completions {
|
||||
ctx: &CompletionContext<'_>,
|
||||
type_alias: hir::TypeAlias,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
|
||||
}
|
||||
|
||||
@ -382,6 +415,9 @@ impl Completions {
|
||||
variant: hir::Variant,
|
||||
path: hir::ModPath,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
if let Some(builder) =
|
||||
render_variant_lit(RenderContext::new(ctx), path_ctx, None, variant, Some(path))
|
||||
{
|
||||
@ -396,6 +432,9 @@ impl Completions {
|
||||
variant: hir::Variant,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx {
|
||||
cov_mark::hit!(enum_variant_pattern_path);
|
||||
self.add_variant_pat(ctx, pat_ctx, Some(path_ctx), variant, local_name);
|
||||
@ -417,6 +456,9 @@ impl Completions {
|
||||
field: hir::Field,
|
||||
ty: &hir::Type,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&field.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&field) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
@ -441,6 +483,9 @@ impl Completions {
|
||||
path: Option<hir::ModPath>,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
if let Some(builder) =
|
||||
render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name)
|
||||
{
|
||||
@ -455,6 +500,9 @@ impl Completions {
|
||||
path: Option<hir::ModPath>,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&un.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
|
||||
self.add_opt(item);
|
||||
}
|
||||
@ -466,6 +514,8 @@ impl Completions {
|
||||
field: usize,
|
||||
ty: &hir::Type,
|
||||
) {
|
||||
// Only used for (unnamed) tuples, whose all fields *are* stable. No need to check
|
||||
// stability here.
|
||||
let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
|
||||
self.add(item);
|
||||
}
|
||||
@ -487,6 +537,9 @@ impl Completions {
|
||||
variant: hir::Variant,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
self.add_opt(render_variant_pat(
|
||||
RenderContext::new(ctx),
|
||||
pattern_ctx,
|
||||
@ -504,6 +557,9 @@ impl Completions {
|
||||
variant: hir::Variant,
|
||||
path: hir::ModPath,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let path = Some(&path);
|
||||
self.add_opt(render_variant_pat(
|
||||
RenderContext::new(ctx),
|
||||
@ -522,6 +578,9 @@ impl Completions {
|
||||
strukt: hir::Struct,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
|
||||
}
|
||||
}
|
||||
|
@ -172,6 +172,43 @@ fn foo(s: S) { s.$0 }
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_unstable_method_on_stable() {
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
fn foo(s: std::S) { s.$0 }
|
||||
//- /std.rs crate:std
|
||||
pub struct S;
|
||||
impl S {
|
||||
#[unstable]
|
||||
pub fn bar(&self) {}
|
||||
}
|
||||
"#,
|
||||
expect![""],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unstable_method_on_nightly() {
|
||||
check(
|
||||
r#"
|
||||
//- toolchain:nightly
|
||||
//- /main.rs crate:main deps:std
|
||||
fn foo(s: std::S) { s.$0 }
|
||||
//- /std.rs crate:std
|
||||
pub struct S;
|
||||
impl S {
|
||||
#[unstable]
|
||||
pub fn bar(&self) {}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
me bar() fn(&self)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_field_completion_self() {
|
||||
check(
|
||||
|
@ -37,9 +37,9 @@ pub(crate) fn complete_cargo_env_vars(
|
||||
guard_env_macro(expanded, &ctx.sema)?;
|
||||
let range = expanded.text_range_between_quotes()?;
|
||||
|
||||
CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| {
|
||||
CARGO_DEFINED_VARS.into_iter().for_each(|&(var, detail)| {
|
||||
let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
|
||||
item.detail(*detail);
|
||||
item.detail(detail);
|
||||
item.add_to(acc);
|
||||
});
|
||||
|
||||
|
@ -262,8 +262,10 @@ fn import_on_the_fly(
|
||||
.into_iter()
|
||||
.filter(ns_filter)
|
||||
.filter(|import| {
|
||||
let original_item = &import.original_item;
|
||||
!ctx.is_item_hidden(&import.item_to_import)
|
||||
&& !ctx.is_item_hidden(&import.original_item)
|
||||
&& !ctx.is_item_hidden(original_item)
|
||||
&& ctx.check_stability(original_item.attrs(ctx.db).as_deref())
|
||||
})
|
||||
.sorted_by_key(|located_import| {
|
||||
compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
|
||||
@ -302,8 +304,10 @@ fn import_on_the_fly_pat_(
|
||||
.into_iter()
|
||||
.filter(ns_filter)
|
||||
.filter(|import| {
|
||||
let original_item = &import.original_item;
|
||||
!ctx.is_item_hidden(&import.item_to_import)
|
||||
&& !ctx.is_item_hidden(&import.original_item)
|
||||
&& !ctx.is_item_hidden(original_item)
|
||||
&& ctx.check_stability(original_item.attrs(ctx.db).as_deref())
|
||||
})
|
||||
.sorted_by_key(|located_import| {
|
||||
compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
|
||||
|
@ -150,21 +150,24 @@ fn complete_trait_impl(
|
||||
impl_def: &ast::Impl,
|
||||
) {
|
||||
if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
|
||||
get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| {
|
||||
use self::ImplCompletionKind::*;
|
||||
match (item, kind) {
|
||||
(hir::AssocItem::Function(func), All | Fn) => {
|
||||
add_function_impl(acc, ctx, replacement_range, func, hir_impl)
|
||||
get_missing_assoc_items(&ctx.sema, impl_def)
|
||||
.into_iter()
|
||||
.filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db))))
|
||||
.for_each(|item| {
|
||||
use self::ImplCompletionKind::*;
|
||||
match (item, kind) {
|
||||
(hir::AssocItem::Function(func), All | Fn) => {
|
||||
add_function_impl(acc, ctx, replacement_range, func, hir_impl)
|
||||
}
|
||||
(hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
|
||||
add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
|
||||
}
|
||||
(hir::AssocItem::Const(const_), All | Const) => {
|
||||
add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
(hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
|
||||
add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
|
||||
}
|
||||
(hir::AssocItem::Const(const_), All | Const) => {
|
||||
add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,9 @@ pub(crate) fn complete_use_path(
|
||||
)
|
||||
};
|
||||
for (name, def) in module_scope {
|
||||
if !ctx.check_stability(def.attrs(ctx.db).as_deref()) {
|
||||
continue;
|
||||
}
|
||||
let is_name_already_imported = name
|
||||
.as_text()
|
||||
.map_or(false, |text| already_imported_names.contains(text.as_str()));
|
||||
|
@ -367,6 +367,8 @@ pub(crate) struct CompletionContext<'a> {
|
||||
pub(super) krate: hir::Crate,
|
||||
/// The module of the `scope`.
|
||||
pub(super) module: hir::Module,
|
||||
/// Whether nightly toolchain is used. Cached since this is looked up a lot.
|
||||
is_nightly: bool,
|
||||
|
||||
/// The expected name of what we are completing.
|
||||
/// This is usually the parameter name of the function argument we are completing.
|
||||
@ -386,7 +388,7 @@ pub(crate) struct CompletionContext<'a> {
|
||||
pub(super) depth_from_crate_root: usize,
|
||||
}
|
||||
|
||||
impl<'a> CompletionContext<'a> {
|
||||
impl CompletionContext<'_> {
|
||||
/// The range of the identifier that is being completed.
|
||||
pub(crate) fn source_range(&self) -> TextRange {
|
||||
let kind = self.original_token.kind();
|
||||
@ -459,6 +461,12 @@ impl<'a> CompletionContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether this item should be listed in regards to stability. Returns `true` if we should.
|
||||
pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool {
|
||||
let Some(attrs) = attrs else { return true; };
|
||||
!attrs.is_unstable() || self.is_nightly
|
||||
}
|
||||
|
||||
/// Whether the given trait is an operator trait or not.
|
||||
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
|
||||
match trait_.attrs(self.db).lang() {
|
||||
@ -632,6 +640,11 @@ impl<'a> CompletionContext<'a> {
|
||||
let krate = scope.krate();
|
||||
let module = scope.module();
|
||||
|
||||
let toolchain = db.crate_graph()[krate.into()].channel;
|
||||
// `toolchain == None` means we're in some detached files. Since we have no information on
|
||||
// the toolchain being used, let's just allow unstable items to be listed.
|
||||
let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
|
||||
|
||||
let mut locals = FxHashMap::default();
|
||||
scope.process_all_names(&mut |name, scope| {
|
||||
if let ScopeDef::Local(local) = scope {
|
||||
@ -651,6 +664,7 @@ impl<'a> CompletionContext<'a> {
|
||||
token,
|
||||
krate,
|
||||
module,
|
||||
is_nightly,
|
||||
expected_name,
|
||||
expected_type,
|
||||
qualifier_ctx,
|
||||
|
@ -23,6 +23,7 @@ mod type_pos;
|
||||
mod use_tree;
|
||||
mod visibility;
|
||||
|
||||
use expect_test::Expect;
|
||||
use hir::PrefixKind;
|
||||
use ide_db::{
|
||||
base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
|
||||
@ -215,6 +216,11 @@ pub(crate) fn check_edit_with_config(
|
||||
assert_eq_text!(&ra_fixture_after, &actual)
|
||||
}
|
||||
|
||||
fn check_empty(ra_fixture: &str, expect: Expect) {
|
||||
let actual = completion_list(ra_fixture);
|
||||
expect.assert_eq(&actual);
|
||||
}
|
||||
|
||||
pub(crate) fn get_all_items(
|
||||
config: CompletionConfig,
|
||||
code: &str,
|
||||
|
@ -1,18 +1,13 @@
|
||||
//! Completion tests for expressions.
|
||||
use expect_test::{expect, Expect};
|
||||
|
||||
use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
|
||||
use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
|
||||
|
||||
fn check(ra_fixture: &str, expect: Expect) {
|
||||
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
|
||||
expect.assert_eq(&actual)
|
||||
}
|
||||
|
||||
fn check_empty(ra_fixture: &str, expect: Expect) {
|
||||
let actual = completion_list(ra_fixture);
|
||||
expect.assert_eq(&actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complete_literal_struct_with_a_private_field() {
|
||||
// `FooDesc.bar` is private, the completion should not be triggered.
|
||||
@ -997,3 +992,105 @@ fn foo() { if foo {} el$0 { let x = 92; } }
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_no_unstable_item_on_stable() {
|
||||
check_empty(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
use std::*;
|
||||
fn main() {
|
||||
$0
|
||||
}
|
||||
//- /std.rs crate:std
|
||||
#[unstable]
|
||||
pub struct UnstableThisShouldNotBeListed;
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn main() fn()
|
||||
md std
|
||||
bt u32
|
||||
kw const
|
||||
kw crate::
|
||||
kw enum
|
||||
kw extern
|
||||
kw false
|
||||
kw fn
|
||||
kw for
|
||||
kw if
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
kw return
|
||||
kw self::
|
||||
kw static
|
||||
kw struct
|
||||
kw trait
|
||||
kw true
|
||||
kw type
|
||||
kw union
|
||||
kw unsafe
|
||||
kw use
|
||||
kw while
|
||||
kw while let
|
||||
sn macro_rules
|
||||
sn pd
|
||||
sn ppd
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_unstable_item_on_nightly() {
|
||||
check_empty(
|
||||
r#"
|
||||
//- toolchain:nightly
|
||||
//- /main.rs crate:main deps:std
|
||||
use std::*;
|
||||
fn main() {
|
||||
$0
|
||||
}
|
||||
//- /std.rs crate:std
|
||||
#[unstable]
|
||||
pub struct UnstableButWeAreOnNightlyAnyway;
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn main() fn()
|
||||
md std
|
||||
st UnstableButWeAreOnNightlyAnyway
|
||||
bt u32
|
||||
kw const
|
||||
kw crate::
|
||||
kw enum
|
||||
kw extern
|
||||
kw false
|
||||
kw fn
|
||||
kw for
|
||||
kw if
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
kw return
|
||||
kw self::
|
||||
kw static
|
||||
kw struct
|
||||
kw trait
|
||||
kw true
|
||||
kw type
|
||||
kw union
|
||||
kw unsafe
|
||||
kw use
|
||||
kw while
|
||||
kw while let
|
||||
sn macro_rules
|
||||
sn pd
|
||||
sn ppd
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user