Auto merge of #143613 - Enselic:panic-abort-uwtables, r=petrochenkov
Fix backtraces with `-C panic=abort` on linux; emit unwind tables by default
The linux backtrace unwinder relies on unwind tables to work properly, and generating and printing a backtrace is done by for example the default panic hook.
Begin emitting unwind tables by default again with `-C panic=abort` (see history below) so that backtraces work.
Closes https://github.com/rust-lang/rust/issues/81902 which is **regression-from-stable-to-stable**
Closes https://github.com/rust-lang/rust/issues/94815
### History
Backtraces with `-C panic=abort` used to work in Rust 1.22 but broke in Rust 1.23, because in 1.23 we stopped emitting unwind tables with `-C panic=abort` (see https://github.com/rust-lang/rust/pull/45031 and https://github.com/rust-lang/rust/issues/81902#issuecomment-3046487084).
In 1.45 a workaround in the form of `-C force-unwind-tables=yes` was added (see https://github.com/rust-lang/rust/pull/69984).
`-C panic=abort` was added in [Rust 1.10](https://blog.rust-lang.org/2016/07/07/Rust-1.10/#what-s-in-1-10-stable) and the motivation was binary size and compile time. But given how confusing that behavior has turned out to be, it is better to make binary size optimization opt-in with `-C force-unwind-tables=no` rather than default since the current default breaks backtraces.
Besides, if binary size is a primary concern, there are many other tricks that can be used that has a higher impact.
# Release Note Entry Draft:
## Compatibility Notes
* [Fix backtraces with `-C panic=abort` on Linux by generating unwind tables by default](https://github.com/rust-lang/rust/pull/143613). Build with `-C force-unwind-tables=no` to keep omitting unwind tables.
try-job: aarch64-apple
try-job: armhf-gnu
try-job: aarch64-msvc-1
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 7706524..0eb57a6 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -56,8 +56,8 @@
# Install a pinned rustc commit to avoid surprises
- name: Install Rust toolchain
run: |
- RUSTC_VERSION=`cat rust-version`
- rustup-toolchain-install-master ${RUSTC_VERSION} -c rust-src -c rustfmt
+ RUSTC_VERSION=$(cat rust-version)
+ rustup-toolchain-install-master ${RUSTC_VERSION} -c cargo -c rust-src -c rustfmt
rustup default ${RUSTC_VERSION}
# Emulate a nightly toolchain, because the toolchain installed above does not have "nightly"
@@ -98,9 +98,9 @@
run: |
rustup update --no-self-update stable
rustup default stable
- rustup component add --toolchain stable rust-src clippy
- # We always use a nightly rustfmt, regardless of channel, because we need
- # --file-lines.
+ rustup component add --toolchain stable rust-src clippy rustfmt
+ # We also install a nightly rustfmt, because we use `--file-lines` in
+ # a test.
rustup toolchain install nightly --profile minimal --component rustfmt
# https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json
- name: Install Rust Problem Matcher
diff --git a/Cargo.lock b/Cargo.lock
index 17dea1b..086f38f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -546,6 +546,12 @@
]
[[package]]
+name = "fixedbitset"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
+
+[[package]]
name = "flate2"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -775,6 +781,7 @@
"itertools",
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"oorandom",
+ "petgraph",
"project-model",
"query-group-macro",
"ra-ap-rustc_abi",
@@ -1327,9 +1334,9 @@
[[package]]
name = "memchr"
-version = "2.7.5"
+version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "memmap2"
@@ -1595,6 +1602,17 @@
]
[[package]]
+name = "petgraph"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca"
+dependencies = [
+ "fixedbitset",
+ "hashbrown 0.15.4",
+ "indexmap",
+]
+
+[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 0401367..d3a4e37 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -170,6 +170,7 @@
triomphe = { version = "0.1.14", default-features = false, features = ["std"] }
url = "2.5.4"
xshell = "0.2.7"
+petgraph = { version = "0.8.2", default-features = false }
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
dashmap = { version = "=6.1.0", features = ["raw-api", "inline"] }
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 301d4cc..e5c213c 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -15,10 +15,6 @@
#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_parse_format as rustc_parse_format;
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_abi;
-
-#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_abi as rustc_abi;
pub mod db;
diff --git a/crates/hir-expand/src/builtin/derive_macro.rs b/crates/hir-expand/src/builtin/derive_macro.rs
index 15e68ff..0fa412a 100644
--- a/crates/hir-expand/src/builtin/derive_macro.rs
+++ b/crates/hir-expand/src/builtin/derive_macro.rs
@@ -12,7 +12,7 @@
use crate::{
ExpandError, ExpandResult, MacroCallId,
- builtin::quote::{dollar_crate, quote},
+ builtin::quote::dollar_crate,
db::ExpandDatabase,
hygiene::span_with_def_site_ctxt,
name::{self, AsName, Name},
diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs
index ec34461..6fe63f2 100644
--- a/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/crates/hir-expand/src/builtin/fn_macro.rs
@@ -19,7 +19,7 @@
use crate::{
EditionedFileId, ExpandError, ExpandResult, Lookup as _, MacroCallId,
- builtin::quote::{WithDelimiter, dollar_crate, quote},
+ builtin::quote::{WithDelimiter, dollar_crate},
db::ExpandDatabase,
hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt},
name,
diff --git a/crates/hir-expand/src/builtin/quote.rs b/crates/hir-expand/src/builtin/quote.rs
index 70c38d4..84dd4a2 100644
--- a/crates/hir-expand/src/builtin/quote.rs
+++ b/crates/hir-expand/src/builtin/quote.rs
@@ -229,8 +229,6 @@
use span::{Edition, ROOT_ERASED_FILE_AST_ID, SpanAnchor, SyntaxContext};
use syntax::{TextRange, TextSize};
- use super::quote;
-
const DUMMY: tt::Span = tt::Span {
range: TextRange::empty(TextSize::new(0)),
anchor: SpanAnchor {
diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml
index 138d02e..4013d19 100644
--- a/crates/hir-ty/Cargo.toml
+++ b/crates/hir-ty/Cargo.toml
@@ -34,6 +34,7 @@
query-group.workspace = true
salsa.workspace = true
salsa-macros.workspace = true
+petgraph.workspace = true
ra-ap-rustc_abi.workspace = true
ra-ap-rustc_index.workspace = true
diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs
index fd60ffc..21a86d3 100644
--- a/crates/hir-ty/src/autoderef.rs
+++ b/crates/hir-ty/src/autoderef.rs
@@ -32,11 +32,11 @@
/// - the yielded types don't contain inference variables (but may contain `TyKind::Error`).
/// - a type won't be yielded more than once; in other words, the returned iterator will stop if it
/// detects a cycle in the deref chain.
-pub fn autoderef(
- db: &dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+pub fn autoderef<'db>(
+ db: &'db dyn HirDatabase,
+ env: Arc<TraitEnvironment<'db>>,
ty: crate::Canonical<crate::Ty>,
-) -> impl Iterator<Item = crate::Ty> {
+) -> impl Iterator<Item = crate::Ty> + use<> {
let mut table = InferenceTable::new(db, env);
let interner = table.interner;
let ty = table.instantiate_canonical(ty);
@@ -298,7 +298,7 @@
) -> Option<(Ty<'db>, PredicateObligations<'db>)> {
let mut ocx = ObligationCtxt::new(&table.infer_ctxt);
let Ok(normalized_ty) =
- ocx.structurally_normalize_ty(&ObligationCause::misc(), table.param_env, ty)
+ ocx.structurally_normalize_ty(&ObligationCause::misc(), table.trait_env.env, ty)
else {
// We shouldn't have errors here in the old solver, except for
// evaluate/fulfill mismatches, but that's not a reason for an ICE.
diff --git a/crates/hir-ty/src/builder.rs b/crates/hir-ty/src/builder.rs
index 3755175..5511587 100644
--- a/crates/hir-ty/src/builder.rs
+++ b/crates/hir-ty/src/builder.rs
@@ -3,17 +3,20 @@
use chalk_ir::{
AdtId, DebruijnIndex, Scalar,
cast::{Cast, CastTo, Caster},
- fold::TypeFoldable,
- interner::HasInterner,
};
use hir_def::{GenericDefId, GenericParamId, TraitId, TypeAliasId, builtin_type::BuiltinType};
use smallvec::SmallVec;
use crate::{
- Binders, BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy,
- Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind, consteval::unknown_const_as_generic,
- db::HirDatabase, error_lifetime, generics::generics, infer::unify::InferenceTable, primitive,
- to_assoc_type_id, to_chalk_trait_id,
+ BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy, Substitution,
+ TraitRef, Ty, TyDefId, TyExt, TyKind,
+ consteval::unknown_const_as_generic,
+ db::HirDatabase,
+ error_lifetime,
+ generics::generics,
+ infer::unify::InferenceTable,
+ next_solver::{DbInterner, EarlyBinder, mapping::ChalkToNextSolver},
+ primitive, to_assoc_type_id, to_chalk_trait_id,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -345,19 +348,20 @@
}
}
-impl<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>> TyBuilder<Binders<T>> {
- pub fn build(self) -> T {
+impl<'db, T: rustc_type_ir::TypeFoldable<DbInterner<'db>>> TyBuilder<EarlyBinder<'db, T>> {
+ pub fn build(self, interner: DbInterner<'db>) -> T {
let (b, subst) = self.build_internal();
- b.substitute(Interner, &subst)
+ let args: crate::next_solver::GenericArgs<'db> = subst.to_nextsolver(interner);
+ b.instantiate(interner, args)
}
}
-impl TyBuilder<Binders<Ty>> {
+impl<'db> TyBuilder<EarlyBinder<'db, crate::next_solver::Ty<'db>>> {
pub fn def_ty(
- db: &dyn HirDatabase,
+ db: &'db dyn HirDatabase,
def: TyDefId,
parent_subst: Option<Substitution>,
- ) -> TyBuilder<Binders<Ty>> {
+ ) -> TyBuilder<EarlyBinder<'db, crate::next_solver::Ty<'db>>> {
let poly_ty = db.ty(def);
let id: GenericDefId = match def {
TyDefId::BuiltinType(_) => {
@@ -370,7 +374,10 @@
TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_ty)
}
- pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> {
+ pub fn impl_self_ty(
+ db: &'db dyn HirDatabase,
+ def: hir_def::ImplId,
+ ) -> TyBuilder<EarlyBinder<'db, crate::next_solver::Ty<'db>>> {
TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def))
}
}
diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs
index 1faf9f6..6956a0a 100644
--- a/crates/hir-ty/src/chalk_ext.rs
+++ b/crates/hir-ty/src/chalk_ext.rs
@@ -15,8 +15,13 @@
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds,
ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy,
QuantifiedWhereClause, Substitution, ToChalk, TraitRef, Ty, TyBuilder, TyKind, TypeFlags,
- WhereClause, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
- from_placeholder_idx, generics::generics, to_chalk_trait_id, utils::ClosureSubst,
+ WhereClause,
+ db::HirDatabase,
+ from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
+ generics::generics,
+ next_solver::{DbInterner, mapping::NextSolverToChalk},
+ to_chalk_trait_id,
+ utils::ClosureSubst,
};
pub trait TyExt {
@@ -372,7 +377,10 @@
let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build();
let env = db.trait_environment_for_body(owner);
let goal = Canonical {
- value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
+ value: InEnvironment::new(
+ &env.env.to_chalk(DbInterner::new_with(db, Some(env.krate), env.block)),
+ trait_ref.cast(Interner),
+ ),
binders: CanonicalVarKinds::empty(Interner),
};
!db.trait_solve(crate_id, None, goal).no_solution()
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 0f2cc17..b2daed4 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -229,7 +229,7 @@
_: &dyn HirDatabase,
_: GeneralConstId,
_: Substitution,
- _: Option<Arc<TraitEnvironment>>,
+ _: Option<Arc<TraitEnvironment<'_>>>,
) -> Result<Const, ConstEvalError> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
@@ -252,7 +252,7 @@
db: &dyn HirDatabase,
def: GeneralConstId,
subst: Substitution,
- trait_env: Option<Arc<TraitEnvironment>>,
+ trait_env: Option<Arc<TraitEnvironment<'_>>>,
) -> Result<Const, ConstEvalError> {
let body = match def {
GeneralConstId::ConstId(c) => {
@@ -327,7 +327,7 @@
debruijn: DebruijnIndex,
) -> Const {
let db = ctx.db;
- let infer = ctx.clone().resolve_all();
+ let infer = ctx.fixme_resolve_all_clone();
fn has_closure(body: &Body, expr: ExprId) -> bool {
if matches!(body[expr], Expr::Closure { .. }) {
return true;
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 299b73a..1586846 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -36,12 +36,12 @@
error: impl FnOnce(ConstEvalError) -> bool,
) {
let (db, file_id) = TestDB::with_single_file(ra_fixture);
- match eval_goal(&db, file_id) {
+ salsa::attach(&db, || match eval_goal(&db, file_id) {
Ok(_) => panic!("Expected fail, but it succeeded"),
Err(e) => {
- assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, db))
+ assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, &db))
}
- }
+ })
}
#[track_caller]
@@ -79,36 +79,38 @@
check: impl FnOnce(&[u8], &MemoryMap<'_>),
) {
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
- let file_id = *file_ids.last().unwrap();
- let r = match eval_goal(&db, file_id) {
- Ok(t) => t,
- Err(e) => {
- let err = pretty_print_err(e, db);
- panic!("Error in evaluating goal: {err}");
- }
- };
- match &r.data(Interner).value {
- chalk_ir::ConstValue::Concrete(c) => match &c.interned {
- ConstScalar::Bytes(b, mm) => {
- check(b, mm);
+ salsa::attach(&db, || {
+ let file_id = *file_ids.last().unwrap();
+ let r = match eval_goal(&db, file_id) {
+ Ok(t) => t,
+ Err(e) => {
+ let err = pretty_print_err(e, &db);
+ panic!("Error in evaluating goal: {err}");
}
- x => panic!("Expected number but found {x:?}"),
- },
- _ => panic!("result of const eval wasn't a concrete const"),
- }
+ };
+ match &r.data(Interner).value {
+ chalk_ir::ConstValue::Concrete(c) => match &c.interned {
+ ConstScalar::Bytes(b, mm) => {
+ check(b, mm);
+ }
+ x => panic!("Expected number but found {x:?}"),
+ },
+ _ => panic!("result of const eval wasn't a concrete const"),
+ }
+ });
}
-fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
+fn pretty_print_err(e: ConstEvalError, db: &TestDB) -> String {
let mut err = String::new();
let span_formatter = |file, range| format!("{file:?} {range:?}");
let display_target =
- DisplayTarget::from_crate(&db, *db.all_crates().last().expect("no crate graph present"));
+ DisplayTarget::from_crate(db, *db.all_crates().last().expect("no crate graph present"));
match e {
ConstEvalError::MirLowerError(e) => {
- e.pretty_print(&mut err, &db, span_formatter, display_target)
+ e.pretty_print(&mut err, db, span_formatter, display_target)
}
ConstEvalError::MirEvalError(e) => {
- e.pretty_print(&mut err, &db, span_formatter, display_target)
+ e.pretty_print(&mut err, db, span_formatter, display_target)
}
}
.unwrap();
diff --git a/crates/hir-ty/src/consteval_nextsolver.rs b/crates/hir-ty/src/consteval_nextsolver.rs
index 6e07d3a..155f133 100644
--- a/crates/hir-ty/src/consteval_nextsolver.rs
+++ b/crates/hir-ty/src/consteval_nextsolver.rs
@@ -222,7 +222,7 @@
// and make this function private. See the fixme comment on `InferenceContext::resolve_all`.
pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'db>) -> Const<'db> {
let interner = DbInterner::new_with(ctx.db, None, None);
- let infer = ctx.clone().resolve_all();
+ let infer = ctx.fixme_resolve_all_clone();
fn has_closure(body: &Body, expr: ExprId) -> bool {
if matches!(body[expr], Expr::Closure { .. }) {
return true;
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 448fc4a..71fb3d4 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -16,8 +16,8 @@
use triomphe::Arc;
use crate::{
- Binders, Const, ImplTraitId, ImplTraits, InferenceResult, PolyFnSig, Substitution,
- TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, chalk_db,
+ Binders, Const, ImplTraitId, ImplTraits, InferenceResult, Substitution, TraitEnvironment, Ty,
+ TyDefId, ValueTyDefId, chalk_db,
consteval::ConstEvalError,
drop::DropGlue,
dyn_compatibility::DynCompatibilityViolation,
@@ -49,7 +49,7 @@
&self,
def: DefWithBodyId,
subst: Substitution,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'_>>,
) -> Result<Arc<MirBody>, MirLowerError>;
#[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)]
@@ -57,7 +57,7 @@
&self,
def: InternedClosureId,
subst: Substitution,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'_>>,
) -> Result<Arc<MirBody>, MirLowerError>;
#[salsa::invoke(crate::mir::borrowck_query)]
@@ -70,7 +70,7 @@
&self,
def: GeneralConstId,
subst: Substitution,
- trait_env: Option<Arc<TraitEnvironment>>,
+ trait_env: Option<Arc<TraitEnvironment<'_>>>,
) -> Result<Const, ConstEvalError>;
#[salsa::invoke(crate::consteval::const_eval_static_query)]
@@ -84,7 +84,7 @@
#[salsa::invoke(crate::method_resolution::lookup_impl_method_query)]
fn lookup_impl_method(
&self,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'_>>,
func: FunctionId,
fn_subst: Substitution,
) -> (FunctionId, Substitution);
@@ -97,7 +97,7 @@
&'db self,
def: AdtId,
args: crate::next_solver::GenericArgs<'db>,
- trait_env: Arc<TraitEnvironment>,
+ trait_env: Arc<TraitEnvironment<'db>>,
) -> Result<Arc<Layout>, LayoutError>;
#[salsa::invoke(crate::layout::layout_of_ty_query)]
@@ -105,7 +105,7 @@
fn layout_of_ty<'db>(
&'db self,
ty: crate::next_solver::Ty<'db>,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
) -> Result<Arc<Layout>, LayoutError>;
#[salsa::invoke(crate::layout::target_data_layout_query)]
@@ -114,55 +114,94 @@
#[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)]
fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option<DynCompatibilityViolation>;
- #[salsa::invoke(crate::lower::ty_query)]
+ #[salsa::invoke(crate::lower_nextsolver::ty_query)]
#[salsa::transparent]
- fn ty(&self, def: TyDefId) -> Binders<Ty>;
+ fn ty<'db>(
+ &'db self,
+ def: TyDefId,
+ ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>;
- #[salsa::invoke(crate::lower::type_for_type_alias_with_diagnostics_query)]
- #[salsa::cycle(cycle_result = crate::lower::type_for_type_alias_with_diagnostics_cycle_result)]
- fn type_for_type_alias_with_diagnostics(&self, def: TypeAliasId) -> (Binders<Ty>, Diagnostics);
+ #[salsa::invoke(crate::lower_nextsolver::type_for_type_alias_with_diagnostics_query)]
+ #[salsa::cycle(cycle_result = crate::lower_nextsolver::type_for_type_alias_with_diagnostics_cycle_result)]
+ fn type_for_type_alias_with_diagnostics<'db>(
+ &'db self,
+ def: TypeAliasId,
+ ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics);
/// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is
/// a `StructId` or `EnumVariantId` with a record constructor.
- #[salsa::invoke(crate::lower::value_ty_query)]
- fn value_ty(&self, def: ValueTyDefId) -> Option<Binders<Ty>>;
+ #[salsa::invoke(crate::lower_nextsolver::value_ty_query)]
+ fn value_ty<'db>(
+ &'db self,
+ def: ValueTyDefId,
+ ) -> Option<crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>>;
- #[salsa::invoke(crate::lower::impl_self_ty_with_diagnostics_query)]
- #[salsa::cycle(cycle_result = crate::lower::impl_self_ty_with_diagnostics_cycle_result)]
- fn impl_self_ty_with_diagnostics(&self, def: ImplId) -> (Binders<Ty>, Diagnostics);
+ #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_with_diagnostics_query)]
+ #[salsa::cycle(cycle_result = crate::lower_nextsolver::impl_self_ty_with_diagnostics_cycle_result)]
+ fn impl_self_ty_with_diagnostics<'db>(
+ &'db self,
+ def: ImplId,
+ ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics);
- #[salsa::invoke(crate::lower::impl_self_ty_query)]
+ #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_query)]
#[salsa::transparent]
- fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>;
+ fn impl_self_ty<'db>(
+ &'db self,
+ def: ImplId,
+ ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>;
// FIXME: Make this a non-interned query.
- #[salsa::invoke_interned(crate::lower::const_param_ty_with_diagnostics_query)]
- #[salsa::cycle(cycle_result = crate::lower::const_param_ty_with_diagnostics_cycle_result)]
- fn const_param_ty_with_diagnostics(&self, def: ConstParamId) -> (Ty, Diagnostics);
+ #[salsa::invoke_interned(crate::lower_nextsolver::const_param_ty_with_diagnostics_query)]
+ #[salsa::cycle(cycle_result = crate::lower_nextsolver::const_param_ty_with_diagnostics_cycle_result)]
+ fn const_param_ty_with_diagnostics<'db>(
+ &'db self,
+ def: ConstParamId,
+ ) -> (crate::next_solver::Ty<'db>, Diagnostics);
- #[salsa::invoke(crate::lower::const_param_ty_query)]
- #[salsa::transparent]
+ // FIXME: Make this a non-interned query.
+ #[salsa::invoke_interned(crate::lower::const_param_ty_query)]
+ #[salsa::cycle(cycle_result = crate::lower::const_param_ty_cycle_result)]
fn const_param_ty(&self, def: ConstParamId) -> Ty;
- #[salsa::invoke(crate::lower::impl_trait_with_diagnostics_query)]
- fn impl_trait_with_diagnostics(&self, def: ImplId) -> Option<(Binders<TraitRef>, Diagnostics)>;
+ #[salsa::invoke(crate::lower_nextsolver::impl_trait_with_diagnostics_query)]
+ fn impl_trait_with_diagnostics<'db>(
+ &'db self,
+ def: ImplId,
+ ) -> Option<(
+ crate::next_solver::EarlyBinder<'db, crate::next_solver::TraitRef<'db>>,
+ Diagnostics,
+ )>;
- #[salsa::invoke(crate::lower::impl_trait_query)]
+ #[salsa::invoke(crate::lower_nextsolver::impl_trait_query)]
#[salsa::transparent]
- fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
+ fn impl_trait<'db>(
+ &'db self,
+ def: ImplId,
+ ) -> Option<crate::next_solver::EarlyBinder<'db, crate::next_solver::TraitRef<'db>>>;
- #[salsa::invoke(crate::lower::field_types_with_diagnostics_query)]
- fn field_types_with_diagnostics(
- &self,
+ #[salsa::invoke(crate::lower_nextsolver::field_types_with_diagnostics_query)]
+ fn field_types_with_diagnostics<'db>(
+ &'db self,
var: VariantId,
- ) -> (Arc<ArenaMap<LocalFieldId, Binders<Ty>>>, Diagnostics);
+ ) -> (
+ Arc<
+ ArenaMap<
+ LocalFieldId,
+ crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>,
+ >,
+ >,
+ Diagnostics,
+ );
#[salsa::invoke(crate::lower::field_types_query)]
#[salsa::transparent]
fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>;
- #[salsa::invoke(crate::lower::callable_item_signature_query)]
- fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
+ #[salsa::invoke(crate::lower_nextsolver::callable_item_signature_query)]
+ fn callable_item_signature<'db>(
+ &'db self,
+ def: CallableDefId,
+ ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::PolyFnSig<'db>>;
#[salsa::invoke(crate::lower::return_type_impl_traits)]
fn return_type_impl_traits(&self, def: FunctionId) -> Option<Arc<Binders<ImplTraits>>>;
@@ -182,12 +221,28 @@
#[salsa::invoke(crate::lower::generic_predicates_query)]
fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates;
- #[salsa::invoke(crate::lower::trait_environment_for_body_query)]
- #[salsa::transparent]
- fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<TraitEnvironment>;
+ #[salsa::invoke(
+ crate::lower_nextsolver::generic_predicates_without_parent_with_diagnostics_query
+ )]
+ fn generic_predicates_without_parent_with_diagnostics<'db>(
+ &'db self,
+ def: GenericDefId,
+ ) -> (crate::lower_nextsolver::GenericPredicates<'db>, Diagnostics);
- #[salsa::invoke(crate::lower::trait_environment_query)]
- fn trait_environment(&self, def: GenericDefId) -> Arc<TraitEnvironment>;
+ #[salsa::invoke(crate::lower_nextsolver::generic_predicates_without_parent_query)]
+ #[salsa::transparent]
+ fn generic_predicates_without_parent<'db>(
+ &'db self,
+ def: GenericDefId,
+ ) -> crate::lower_nextsolver::GenericPredicates<'db>;
+
+ #[salsa::invoke(crate::lower_nextsolver::trait_environment_for_body_query)]
+ #[salsa::transparent]
+ fn trait_environment_for_body<'db>(&'db self, def: DefWithBodyId)
+ -> Arc<TraitEnvironment<'db>>;
+
+ #[salsa::invoke(crate::lower_nextsolver::trait_environment_query)]
+ fn trait_environment<'db>(&'db self, def: GenericDefId) -> Arc<TraitEnvironment<'db>>;
#[salsa::invoke(crate::lower::generic_defaults_with_diagnostics_query)]
#[salsa::cycle(cycle_result = crate::lower::generic_defaults_with_diagnostics_cycle_result)]
@@ -258,7 +313,7 @@
fn normalize_projection(
&self,
projection: crate::ProjectionTy,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'_>>,
) -> Ty;
#[salsa::invoke(crate::traits::trait_solve_query)]
@@ -272,87 +327,14 @@
#[salsa::invoke(crate::drop::has_drop_glue)]
#[salsa::cycle(cycle_result = crate::drop::has_drop_glue_cycle_result)]
- fn has_drop_glue(&self, ty: Ty, env: Arc<TraitEnvironment>) -> DropGlue;
+ fn has_drop_glue(&self, ty: Ty, env: Arc<TraitEnvironment<'_>>) -> DropGlue;
// next trait solver
- #[salsa::invoke(crate::lower_nextsolver::ty_query)]
- #[salsa::transparent]
- fn ty_ns<'db>(
- &'db self,
- def: TyDefId,
- ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>;
-
- /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is
- /// a `StructId` or `EnumVariantId` with a record constructor.
- #[salsa::invoke(crate::lower_nextsolver::value_ty_query)]
- fn value_ty_ns<'db>(
- &'db self,
- def: ValueTyDefId,
- ) -> Option<crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>>;
-
- #[salsa::invoke(crate::lower_nextsolver::type_for_type_alias_with_diagnostics_query)]
- #[salsa::cycle(cycle_result = crate::lower_nextsolver::type_for_type_alias_with_diagnostics_cycle_result)]
- fn type_for_type_alias_with_diagnostics_ns<'db>(
- &'db self,
- def: TypeAliasId,
- ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics);
-
- #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_with_diagnostics_query)]
- #[salsa::cycle(cycle_result = crate::lower_nextsolver::impl_self_ty_with_diagnostics_cycle_result)]
- fn impl_self_ty_with_diagnostics_ns<'db>(
- &'db self,
- def: ImplId,
- ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics);
-
- #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_query)]
- #[salsa::transparent]
- fn impl_self_ty_ns<'db>(
- &'db self,
- def: ImplId,
- ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>;
-
- // FIXME: Make this a non-interned query.
- #[salsa::invoke_interned(crate::lower_nextsolver::const_param_ty_with_diagnostics_query)]
- fn const_param_ty_with_diagnostics_ns<'db>(
- &'db self,
- def: ConstParamId,
- ) -> (crate::next_solver::Ty<'db>, Diagnostics);
-
#[salsa::invoke(crate::lower_nextsolver::const_param_ty_query)]
#[salsa::transparent]
fn const_param_ty_ns<'db>(&'db self, def: ConstParamId) -> crate::next_solver::Ty<'db>;
- #[salsa::invoke(crate::lower_nextsolver::impl_trait_with_diagnostics_query)]
- fn impl_trait_with_diagnostics_ns<'db>(
- &'db self,
- def: ImplId,
- ) -> Option<(
- crate::next_solver::EarlyBinder<'db, crate::next_solver::TraitRef<'db>>,
- Diagnostics,
- )>;
-
- #[salsa::invoke(crate::lower_nextsolver::impl_trait_query)]
- #[salsa::transparent]
- fn impl_trait_ns<'db>(
- &'db self,
- def: ImplId,
- ) -> Option<crate::next_solver::EarlyBinder<'db, crate::next_solver::TraitRef<'db>>>;
-
- #[salsa::invoke(crate::lower_nextsolver::field_types_with_diagnostics_query)]
- fn field_types_with_diagnostics_ns<'db>(
- &'db self,
- var: VariantId,
- ) -> (
- Arc<
- ArenaMap<
- LocalFieldId,
- crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>,
- >,
- >,
- Diagnostics,
- );
-
#[salsa::invoke(crate::lower_nextsolver::field_types_query)]
#[salsa::transparent]
fn field_types_ns<'db>(
@@ -362,12 +344,6 @@
ArenaMap<LocalFieldId, crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>>,
>;
- #[salsa::invoke(crate::lower_nextsolver::callable_item_signature_query)]
- fn callable_item_signature_ns<'db>(
- &'db self,
- def: CallableDefId,
- ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::PolyFnSig<'db>>;
-
#[salsa::invoke(crate::lower_nextsolver::return_type_impl_traits)]
fn return_type_impl_traits_ns<'db>(
&'db self,
@@ -394,21 +370,6 @@
&'db self,
def: GenericDefId,
) -> crate::lower_nextsolver::GenericPredicates<'db>;
-
- #[salsa::invoke(
- crate::lower_nextsolver::generic_predicates_without_parent_with_diagnostics_query
- )]
- fn generic_predicates_without_parent_with_diagnostics_ns<'db>(
- &'db self,
- def: GenericDefId,
- ) -> (crate::lower_nextsolver::GenericPredicates<'db>, Diagnostics);
-
- #[salsa::invoke(crate::lower_nextsolver::generic_predicates_without_parent_query)]
- #[salsa::transparent]
- fn generic_predicates_without_parent_ns<'db>(
- &'db self,
- def: GenericDefId,
- ) -> crate::lower_nextsolver::GenericPredicates<'db>;
}
#[test]
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 403ea05..d05814e 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -81,17 +81,17 @@
}
}
-struct ExprValidator {
+struct ExprValidator<'db> {
owner: DefWithBodyId,
body: Arc<Body>,
infer: Arc<InferenceResult>,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
diagnostics: Vec<BodyValidationDiagnostic>,
validate_lints: bool,
}
-impl ExprValidator {
- fn validate_body(&mut self, db: &dyn HirDatabase) {
+impl<'db> ExprValidator<'db> {
+ fn validate_body(&mut self, db: &'db dyn HirDatabase) {
let mut filter_map_next_checker = None;
// we'll pass &mut self while iterating over body.exprs, so they need to be disjoint
let body = Arc::clone(&self.body);
diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index 56fd12e..eb20d3c 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -70,7 +70,7 @@
body: DefWithBodyId,
pub(crate) db: &'db dyn HirDatabase,
exhaustive_patterns: bool,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
}
impl<'db> MatchCheckCtx<'db> {
@@ -78,7 +78,7 @@
module: ModuleId,
body: DefWithBodyId,
db: &'db dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
) -> Self {
let def_map = module.crate_def_map(db);
let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 3f04b72..3c78f5e 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -315,6 +315,22 @@
}
_ => (),
}
+
+ let mut peeled = *expr;
+ while let Expr::Field { expr: lhs, .. } = &self.body[peeled] {
+ if let Some(Either::Left(FieldId { parent: VariantId::UnionId(_), .. })) =
+ self.infer.field_resolution(peeled)
+ {
+ peeled = *lhs;
+ } else {
+ break;
+ }
+ }
+
+ // Walk the peeled expression (the LHS of the union field chain)
+ self.walk_expr(peeled);
+ // Return so we don't recurse directly onto the union field access(es)
+ return;
}
Expr::MethodCall { .. } => {
if let Some((func, _)) = self.infer.method_resolution(current) {
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 519e4b5..e11ce51 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -46,8 +46,8 @@
use stdx::never;
use triomphe::Arc;
-use crate::next_solver::infer::DbInternerInferExt;
use crate::next_solver::infer::traits::ObligationCause;
+use crate::next_solver::{infer::DbInternerInferExt, mapping::NextSolverToChalk};
use crate::{
AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const, ConstScalar,
ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData,
@@ -792,19 +792,16 @@
let trait_env = TraitEnvironment::empty(f.krate());
let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block);
let infcx = interner.infer_ctxt().build(rustc_type_ir::TypingMode::PostAnalysis);
- let ty = infcx
- .at(&ObligationCause::new(), trait_env.env.to_nextsolver(interner))
- .deeply_normalize(ty)
- .unwrap_or(ty);
+ let ty = infcx.at(&ObligationCause::new(), trait_env.env).deeply_normalize(ty).unwrap_or(ty);
render_const_scalar_inner(f, b, memory_map, ty, trait_env)
}
-fn render_const_scalar_inner(
+fn render_const_scalar_inner<'db>(
f: &mut HirFormatter<'_>,
b: &[u8],
memory_map: &MemoryMap<'_>,
- ty: crate::next_solver::Ty<'_>,
- trait_env: Arc<TraitEnvironment>,
+ ty: crate::next_solver::Ty<'db>,
+ trait_env: Arc<TraitEnvironment<'db>>,
) -> Result<(), HirDisplayError> {
use rustc_type_ir::TyKind;
match ty.kind() {
@@ -1068,11 +1065,11 @@
}
}
-fn render_variant_after_name(
+fn render_variant_after_name<'db>(
data: &VariantFields,
f: &mut HirFormatter<'_>,
field_types: &ArenaMap<LocalFieldId, Binders<Ty>>,
- trait_env: Arc<TraitEnvironment>,
+ trait_env: Arc<TraitEnvironment<'db>>,
layout: &Layout,
args: GenericArgs<'_>,
b: &[u8],
@@ -1301,7 +1298,9 @@
let def = def.0;
let sig = db
.callable_item_signature(def)
- .substitute(Interner, &convert_args_for_result(interner, args.as_slice()));
+ .instantiate(interner, args)
+ .skip_binder()
+ .to_chalk(interner);
if f.display_kind.is_source_code() {
// `FnDef` is anonymous and there's no surface syntax for it. Show it as a
diff --git a/crates/hir-ty/src/drop.rs b/crates/hir-ty/src/drop.rs
index f5c2f41..413f705 100644
--- a/crates/hir-ty/src/drop.rs
+++ b/crates/hir-ty/src/drop.rs
@@ -7,6 +7,8 @@
use stdx::never;
use triomphe::Arc;
+use crate::next_solver::DbInterner;
+use crate::next_solver::mapping::NextSolverToChalk;
use crate::{
AliasTy, Canonical, CanonicalVarKinds, ConcreteConst, ConstScalar, ConstValue, InEnvironment,
Interner, ProjectionTy, TraitEnvironment, Ty, TyBuilder, TyKind, db::HirDatabase,
@@ -43,7 +45,11 @@
HasDropGlue,
}
-pub(crate) fn has_drop_glue(db: &dyn HirDatabase, ty: Ty, env: Arc<TraitEnvironment>) -> DropGlue {
+pub(crate) fn has_drop_glue(
+ db: &dyn HirDatabase,
+ ty: Ty,
+ env: Arc<TraitEnvironment<'_>>,
+) -> DropGlue {
match ty.kind(Interner) {
TyKind::Adt(adt, subst) => {
if has_destructor(db, adt.0) {
@@ -165,7 +171,7 @@
fn projection_has_drop_glue(
db: &dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'_>>,
projection: ProjectionTy,
ty: Ty,
) -> DropGlue {
@@ -178,13 +184,16 @@
}
}
-fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc<TraitEnvironment>) -> bool {
+fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc<TraitEnvironment<'_>>) -> bool {
let Some(copy_trait) = LangItem::Copy.resolve_trait(db, env.krate) else {
return false;
};
let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(ty).build();
let goal = Canonical {
- value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
+ value: InEnvironment::new(
+ &env.env.to_chalk(DbInterner::new_with(db, Some(env.krate), env.block)),
+ trait_ref.cast(Interner),
+ ),
binders: CanonicalVarKinds::empty(Interner),
};
db.trait_solve(env.krate, env.block, goal).certain()
@@ -193,7 +202,7 @@
pub(crate) fn has_drop_glue_cycle_result(
_db: &dyn HirDatabase,
_ty: Ty,
- _env: Arc<TraitEnvironment>,
+ _env: Arc<TraitEnvironment<'_>>,
) -> DropGlue {
DropGlue::None
}
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs
index b87c998..b2406a0 100644
--- a/crates/hir-ty/src/dyn_compatibility.rs
+++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -329,7 +329,7 @@
cb(MethodViolationCode::AsyncFn)?;
}
- let sig = db.callable_item_signature_ns(func.into());
+ let sig = db.callable_item_signature(func.into());
if sig
.skip_binder()
.inputs()
@@ -364,7 +364,7 @@
cb(MethodViolationCode::UndispatchableReceiver)?;
}
- let predicates = &*db.generic_predicates_without_parent_ns(func.into());
+ let predicates = &*db.generic_predicates_without_parent(func.into());
for pred in predicates {
let pred = pred.kind().skip_binder();
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 0171197..0282b7a 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -19,6 +19,7 @@
mod coerce;
pub(crate) mod diagnostics;
mod expr;
+mod fallback;
mod mutability;
mod pat;
mod path;
@@ -53,16 +54,16 @@
use intern::sym;
use la_arena::{ArenaMap, Entry};
use rustc_hash::{FxHashMap, FxHashSet};
+use rustc_type_ir::inherent::Ty as _;
use stdx::{always, never};
use triomphe::Arc;
-use crate::db::InternedClosureId;
use crate::{
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, ImplTraitId, ImplTraitIdx,
IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId, ParamLoweringMode,
PathLoweringDiagnostic, ProjectionTy, Substitution, TargetFeatures, TraitEnvironment, Ty,
TyBuilder, TyExt,
- db::HirDatabase,
+ db::{HirDatabase, InternedClosureId},
fold_tys,
generics::Generics,
infer::{
@@ -75,6 +76,7 @@
mir::MirSpan,
next_solver::{
self, DbInterner,
+ infer::{DefineOpaqueTypes, traits::ObligationCause},
mapping::{ChalkToNextSolver, NextSolverToChalk},
},
static_lifetime, to_assoc_type_id,
@@ -138,6 +140,20 @@
ctx.infer_mut_body();
+ ctx.type_inference_fallback();
+
+ // Comment from rustc:
+ // Even though coercion casts provide type hints, we check casts after fallback for
+ // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
+ let cast_checks = std::mem::take(&mut ctx.deferred_cast_checks);
+ for mut cast in cast_checks.into_iter() {
+ if let Err(diag) = cast.check(&mut ctx) {
+ ctx.diagnostics.push(diag);
+ }
+ }
+
+ ctx.table.select_obligations_where_possible();
+
ctx.infer_closures();
Arc::new(ctx.resolve_all())
@@ -152,7 +168,7 @@
/// This is appropriate to use only after type-check: it assumes
/// that normalization will succeed, for example.
#[tracing::instrument(level = "debug", skip(db))]
-pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc<TraitEnvironment>, ty: Ty) -> Ty {
+pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc<TraitEnvironment<'_>>, ty: Ty) -> Ty {
// FIXME: TypeFlags::HAS_CT_PROJECTION is not implemented in chalk, so TypeFlags::HAS_PROJECTION only
// works for the type case, so we check array unconditionally. Remove the array part
// when the bug in chalk becomes fixed.
@@ -165,7 +181,6 @@
let ty_with_vars = table.normalize_associated_types_in(ty);
table.select_obligations_where_possible();
- table.propagate_diverging_flag();
table.resolve_completely(ty_with_vars)
}
@@ -632,6 +647,26 @@
pub fn binding_mode(&self, id: PatId) -> Option<BindingMode> {
self.binding_modes.get(id).copied()
}
+
+ // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please.
+ pub fn expression_types(&self) -> impl Iterator<Item = (ExprId, &Ty)> {
+ self.type_of_expr.iter()
+ }
+
+ // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please.
+ pub fn pattern_types(&self) -> impl Iterator<Item = (PatId, &Ty)> {
+ self.type_of_pat.iter()
+ }
+
+ // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please.
+ pub fn binding_types(&self) -> impl Iterator<Item = (BindingId, &Ty)> {
+ self.type_of_binding.iter()
+ }
+
+ // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please.
+ pub fn return_position_impl_trait_types(&self) -> impl Iterator<Item = (ImplTraitIdx, &Ty)> {
+ self.type_of_rpit.iter()
+ }
}
impl Index<ExprId> for InferenceResult {
@@ -666,6 +701,25 @@
}
}
+#[derive(Debug, Clone)]
+struct InternedStandardTypesNextSolver<'db> {
+ unit: crate::next_solver::Ty<'db>,
+ never: crate::next_solver::Ty<'db>,
+ i32: crate::next_solver::Ty<'db>,
+ f64: crate::next_solver::Ty<'db>,
+}
+
+impl<'db> InternedStandardTypesNextSolver<'db> {
+ fn new(interner: DbInterner<'db>) -> Self {
+ Self {
+ unit: crate::next_solver::Ty::new_unit(interner),
+ never: crate::next_solver::Ty::new(interner, crate::next_solver::TyKind::Never),
+ i32: crate::next_solver::Ty::new_int(interner, rustc_type_ir::IntTy::I32),
+ f64: crate::next_solver::Ty::new_float(interner, rustc_type_ir::FloatTy::F64),
+ }
+ }
+}
+
/// The inference context contains all information needed during type inference.
#[derive(Clone, Debug)]
pub(crate) struct InferenceContext<'db> {
@@ -698,6 +752,7 @@
resume_yield_tys: Option<(Ty, Ty)>,
diverges: Diverges,
breakables: Vec<BreakableContext<'db>>,
+ types: InternedStandardTypesNextSolver<'db>,
/// Whether we are inside the pattern of a destructuring assignment.
inside_assignment: bool,
@@ -778,11 +833,13 @@
resolver: Resolver<'db>,
) -> Self {
let trait_env = db.trait_environment_for_body(owner);
+ let table = unify::InferenceTable::new(db, trait_env);
InferenceContext {
+ types: InternedStandardTypesNextSolver::new(table.interner),
target_features: OnceCell::new(),
generics: OnceCell::new(),
result: InferenceResult::default(),
- table: unify::InferenceTable::new(db, trait_env),
+ table,
tuple_field_accesses_rev: Default::default(),
return_ty: TyKind::Error.intern(Interner), // set in collect_* calls
resume_yield_tys: None,
@@ -845,24 +902,33 @@
self.result.has_errors = true;
}
- // FIXME: This function should be private in module. It is currently only used in the consteval, since we need
- // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
- // used this function for another workaround, mention it here. If you really need this function and believe that
- // there is no problem in it being `pub(crate)`, remove this comment.
- pub(crate) fn resolve_all(mut self) -> InferenceResult {
- self.table.select_obligations_where_possible();
- self.table.fallback_if_possible();
+ /// Clones `self` and calls `resolve_all()` on it.
+ // FIXME: Remove this.
+ pub(crate) fn fixme_resolve_all_clone(&self) -> InferenceResult {
+ let mut ctx = self.clone();
+
+ ctx.type_inference_fallback();
// Comment from rustc:
// Even though coercion casts provide type hints, we check casts after fallback for
// backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
- let cast_checks = std::mem::take(&mut self.deferred_cast_checks);
+ let cast_checks = std::mem::take(&mut ctx.deferred_cast_checks);
for mut cast in cast_checks.into_iter() {
- if let Err(diag) = cast.check(&mut self) {
- self.diagnostics.push(diag);
+ if let Err(diag) = cast.check(&mut ctx) {
+ ctx.diagnostics.push(diag);
}
}
+ ctx.table.select_obligations_where_possible();
+
+ ctx.resolve_all()
+ }
+
+ // FIXME: This function should be private in module. It is currently only used in the consteval, since we need
+ // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
+ // used this function for another workaround, mention it here. If you really need this function and believe that
+ // there is no problem in it being `pub(crate)`, remove this comment.
+ pub(crate) fn resolve_all(self) -> InferenceResult {
let InferenceContext {
mut table, mut result, tuple_field_accesses_rev, diagnostics, ..
} = self;
@@ -894,11 +960,6 @@
diagnostics: _,
} = &mut result;
- // FIXME resolve obligations as well (use Guidance if necessary)
- table.select_obligations_where_possible();
-
- // make sure diverging type variables are marked as such
- table.propagate_diverging_flag();
for ty in type_of_expr.values_mut() {
*ty = table.resolve_completely(ty.clone());
*has_errors = *has_errors || ty.contains_unknown();
@@ -1653,6 +1714,22 @@
self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[])
}
+ fn demand_eqtype(
+ &mut self,
+ expected: crate::next_solver::Ty<'db>,
+ actual: crate::next_solver::Ty<'db>,
+ ) {
+ let result = self
+ .table
+ .infer_ctxt
+ .at(&ObligationCause::new(), self.table.trait_env.env)
+ .eq(DefineOpaqueTypes::Yes, expected, actual)
+ .map(|infer_ok| self.table.register_infer_ok(infer_ok));
+ if let Err(_err) = result {
+ // FIXME: Emit diagnostic.
+ }
+ }
+
fn resolve_associated_type_with_params(
&mut self,
inner_ty: Ty,
@@ -1708,6 +1785,7 @@
LifetimeElisionKind::Infer,
);
let mut path_ctx = ctx.at_path(path, node);
+ let interner = DbInterner::conjure();
let (resolution, unresolved) = if value_ns {
let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else {
return (self.err_ty(), None);
@@ -1717,15 +1795,27 @@
ValueNs::EnumVariantId(var) => {
let substs = path_ctx.substs_from_path(var.into(), true, false);
drop(ctx);
- let ty = self.db.ty(var.lookup(self.db).parent.into());
- let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
+ let args: crate::next_solver::GenericArgs<'_> =
+ substs.to_nextsolver(interner);
+ let ty = self
+ .db
+ .ty(var.lookup(self.db).parent.into())
+ .instantiate(interner, args)
+ .to_chalk(interner);
+ let ty = self.insert_type_vars(ty);
return (ty, Some(var.into()));
}
ValueNs::StructId(strukt) => {
let substs = path_ctx.substs_from_path(strukt.into(), true, false);
drop(ctx);
- let ty = self.db.ty(strukt.into());
- let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
+ let args: crate::next_solver::GenericArgs<'_> =
+ substs.to_nextsolver(interner);
+ let ty = self
+ .db
+ .ty(strukt.into())
+ .instantiate(interner, args)
+ .to_chalk(interner);
+ let ty = self.insert_type_vars(ty);
return (ty, Some(strukt.into()));
}
ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None),
@@ -1746,28 +1836,37 @@
TypeNs::AdtId(AdtId::StructId(strukt)) => {
let substs = path_ctx.substs_from_path(strukt.into(), true, false);
drop(ctx);
- let ty = self.db.ty(strukt.into());
- let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let ty = self.db.ty(strukt.into()).instantiate(interner, args).to_chalk(interner);
+ let ty = self.insert_type_vars(ty);
forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
}
TypeNs::AdtId(AdtId::UnionId(u)) => {
let substs = path_ctx.substs_from_path(u.into(), true, false);
drop(ctx);
- let ty = self.db.ty(u.into());
- let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let ty = self.db.ty(u.into()).instantiate(interner, args).to_chalk(interner);
+ let ty = self.insert_type_vars(ty);
forbid_unresolved_segments((ty, Some(u.into())), unresolved)
}
TypeNs::EnumVariantId(var) => {
let substs = path_ctx.substs_from_path(var.into(), true, false);
drop(ctx);
- let ty = self.db.ty(var.lookup(self.db).parent.into());
- let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let ty = self
+ .db
+ .ty(var.lookup(self.db).parent.into())
+ .instantiate(interner, args)
+ .to_chalk(interner);
+ let ty = self.insert_type_vars(ty);
forbid_unresolved_segments((ty, Some(var.into())), unresolved)
}
TypeNs::SelfType(impl_id) => {
let generics = crate::generics::generics(self.db, impl_id.into());
let substs = generics.placeholder_subst(self.db);
- let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let mut ty =
+ self.db.impl_self_ty(impl_id).instantiate(interner, args).to_chalk(interner);
let Some(remaining_idx) = unresolved else {
drop(ctx);
@@ -1844,8 +1943,10 @@
};
let substs = path_ctx.substs_from_path_segment(it.into(), true, None, false);
drop(ctx);
- let ty = self.db.ty(it.into());
- let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
+ let interner = DbInterner::conjure();
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let ty = self.db.ty(it.into()).instantiate(interner, args).to_chalk(interner);
+ let ty = self.insert_type_vars(ty);
self.resolve_variant_on_alias(ty, unresolved, mod_path)
}
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 1d5d8dd..4a57b2f 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -318,7 +318,7 @@
_ = self
.table
.infer_ctxt
- .at(&ObligationCause::new(), self.table.param_env)
+ .at(&ObligationCause::new(), self.table.trait_env.env)
.eq(DefineOpaqueTypes::Yes, inferred_fnptr_sig, generalized_fnptr_sig)
.map(|infer_ok| self.table.register_infer_ok(infer_ok));
@@ -703,7 +703,7 @@
let cause = ObligationCause::new();
let InferOk { value: (), obligations } = table
.infer_ctxt
- .at(&cause, table.param_env)
+ .at(&cause, table.trait_env.env)
.eq(DefineOpaqueTypes::Yes, expected_ty, supplied_ty)?;
all_obligations.extend(obligations);
}
@@ -711,7 +711,7 @@
let supplied_output_ty = supplied_sig.output();
let cause = ObligationCause::new();
let InferOk { value: (), obligations } =
- table.infer_ctxt.at(&cause, table.param_env).eq(
+ table.infer_ctxt.at(&cause, table.trait_env.env).eq(
DefineOpaqueTypes::Yes,
expected_sigs.liberated_sig.output(),
supplied_output_ty,
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 7930d8b..62ce00a 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -144,7 +144,7 @@
fn unify_raw(&mut self, a: Ty<'db>, b: Ty<'db>) -> InferResult<'db, Ty<'db>> {
debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub);
self.commit_if_ok(|this| {
- let at = this.infer_ctxt().at(&this.cause, this.table.param_env);
+ let at = this.infer_ctxt().at(&this.cause, this.table.trait_env.env);
let res = if this.use_lub {
at.lub(b, a)
@@ -210,9 +210,8 @@
// Coercing from `!` to any type is allowed:
if a.is_never() {
// If we're coercing into an inference var, mark it as possibly diverging.
- // FIXME: rustc does this differently.
- if let TyKind::Infer(rustc_type_ir::TyVar(b)) = b.kind() {
- self.table.set_diverging(b.as_u32().into(), chalk_ir::TyVariableKind::General);
+ if b.is_infer() {
+ self.table.set_diverging(b);
}
if self.coerce_never {
@@ -330,7 +329,7 @@
obligations.push(Obligation::new(
self.interner(),
self.cause.clone(),
- self.table.param_env,
+ self.table.trait_env.env,
Binder::dummy(PredicateKind::Coerce(CoercePredicate {
a: source_ty,
b: target_ty,
@@ -718,7 +717,7 @@
let mut queue: SmallVec<[PredicateObligation<'db>; 4]> = smallvec![Obligation::new(
self.interner(),
cause,
- self.table.param_env,
+ self.table.trait_env.env,
TraitRef::new(
self.interner(),
coerce_unsized_did.into(),
@@ -1114,8 +1113,12 @@
match self.table.commit_if_ok(|table| {
// We need to eagerly handle nested obligations due to lazy norm.
let mut ocx = ObligationCtxt::new(&table.infer_ctxt);
- let value =
- ocx.lub(&ObligationCause::new(), table.param_env, prev_ty, new_ty)?;
+ let value = ocx.lub(
+ &ObligationCause::new(),
+ table.trait_env.env,
+ prev_ty,
+ new_ty,
+ )?;
if ocx.select_where_possible().is_empty() {
Ok(InferOk { value, obligations: ocx.into_pending_obligations() })
} else {
@@ -1158,7 +1161,7 @@
let sig = self
.table
.infer_ctxt
- .at(&ObligationCause::new(), self.table.param_env)
+ .at(&ObligationCause::new(), self.table.trait_env.env)
.lub(a_sig, b_sig)
.map(|ok| self.table.register_infer_ok(ok))?;
@@ -1248,7 +1251,7 @@
.commit_if_ok(|table| {
table
.infer_ctxt
- .at(&ObligationCause::new(), table.param_env)
+ .at(&ObligationCause::new(), table.trait_env.env)
.lub(prev_ty, new_ty)
})
.unwrap_err())
@@ -1498,7 +1501,7 @@
assert!(expression_ty.is_unit(), "if let hack without unit type");
icx.table
.infer_ctxt
- .at(cause, icx.table.param_env)
+ .at(cause, icx.table.trait_env.env)
.eq(
// needed for tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs
DefineOpaqueTypes::Yes,
@@ -1564,9 +1567,9 @@
}
}
-pub fn could_coerce(
- db: &dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+pub fn could_coerce<'db>(
+ db: &'db dyn HirDatabase,
+ env: Arc<TraitEnvironment<'db>>,
tys: &crate::Canonical<(crate::Ty, crate::Ty)>,
) -> bool {
coerce(db, env, tys).is_ok()
@@ -1574,7 +1577,7 @@
fn coerce<'db>(
db: &'db dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
tys: &crate::Canonical<(crate::Ty, crate::Ty)>,
) -> Result<(Vec<Adjustment>, crate::Ty), TypeError<DbInterner<'db>>> {
let mut table = InferenceTable::new(db, env);
@@ -1609,16 +1612,21 @@
chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner),
} == Some(iv))
};
- let fallback = |iv, kind, default, binder| match kind {
- chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv)
- .map_or(default, |i| crate::BoundVar::new(binder, i).to_ty(Interner).cast(Interner)),
- chalk_ir::VariableKind::Lifetime => find_var(iv).map_or(default, |i| {
- crate::BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner)
- }),
- chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or(default, |i| {
- crate::BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner)
- }),
+ let fallback = |iv, kind, binder| match kind {
+ chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv).map_or_else(
+ || chalk_ir::TyKind::Error.intern(Interner).cast(Interner),
+ |i| crate::BoundVar::new(binder, i).to_ty(Interner).cast(Interner),
+ ),
+ chalk_ir::VariableKind::Lifetime => find_var(iv).map_or_else(
+ || crate::LifetimeData::Error.intern(Interner).cast(Interner),
+ |i| crate::BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner),
+ ),
+ chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or_else(
+ || crate::unknown_const(ty.clone()).cast(Interner),
+ |i| crate::BoundVar::new(binder, i).to_const(Interner, ty.clone()).cast(Interner),
+ ),
};
// FIXME also map the types in the adjustments
+ // FIXME: We don't fallback correctly since this is done on `InferenceContext` and we only have `InferenceTable`.
Ok((adjustments, table.resolve_with_fallback(ty.to_chalk(table.interner), &fallback)))
}
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index c5a51df..ddf632c 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -23,13 +23,13 @@
use tracing::debug;
use crate::autoderef::overloaded_deref_ty;
-use crate::next_solver::ErrorGuaranteed;
use crate::next_solver::infer::DefineOpaqueTypes;
use crate::next_solver::obligation_ctxt::ObligationCtxt;
+use crate::next_solver::{DbInterner, ErrorGuaranteed};
use crate::{
- Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext,
- DeclOrigin, IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar,
- Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, consteval,
+ Adjust, Adjustment, AdtId, AutoBorrow, CallableDefId, CallableSig, DeclContext, DeclOrigin,
+ IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, Substitution,
+ TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, consteval,
generics::generics,
infer::{
AllowTwoPhase, BreakableKind,
@@ -1481,7 +1481,10 @@
self.write_method_resolution(tgt_expr, func, subst.clone());
- let method_ty = self.db.value_ty(func.into()).unwrap().substitute(Interner, &subst);
+ let interner = DbInterner::new_with(self.db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = subst.to_nextsolver(interner);
+ let method_ty =
+ self.db.value_ty(func.into()).unwrap().instantiate(interner, args).to_chalk(interner);
self.register_obligations_for_call(&method_ty);
self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone()), ExprIsRead::Yes);
@@ -1662,7 +1665,6 @@
});
self.resolver.reset_to_guard(g);
if let Some(prev_env) = prev_env {
- self.table.param_env = prev_env.env.to_nextsolver(self.table.interner);
self.table.trait_env = prev_env;
}
@@ -1801,11 +1803,17 @@
self.write_expr_adj(receiver, adjustments.into_boxed_slice());
self.write_method_resolution(tgt_expr, func, substs.clone());
+ let interner = DbInterner::new_with(self.db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> =
+ substs.to_nextsolver(interner);
self.check_method_call(
tgt_expr,
&[],
- self.db.value_ty(func.into()).unwrap(),
- substs,
+ self.db
+ .value_ty(func.into())
+ .unwrap()
+ .instantiate(interner, args)
+ .to_chalk(interner),
ty,
expected,
)
@@ -1964,11 +1972,16 @@
let substs = self.substs_for_method_call(tgt_expr, func.into(), generic_args);
self.write_method_resolution(tgt_expr, func, substs.clone());
+ let interner = DbInterner::new_with(self.db, None, None);
+ let gen_args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
self.check_method_call(
tgt_expr,
args,
- self.db.value_ty(func.into()).expect("we have a function def"),
- substs,
+ self.db
+ .value_ty(func.into())
+ .expect("we have a function def")
+ .instantiate(interner, gen_args)
+ .to_chalk(interner),
ty,
expected,
)
@@ -2013,11 +2026,15 @@
let recovered = match assoc_func_with_same_name {
Some(f) => {
let substs = self.substs_for_method_call(tgt_expr, f.into(), generic_args);
+ let interner = DbInterner::new_with(self.db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> =
+ substs.to_nextsolver(interner);
let f = self
.db
.value_ty(f.into())
.expect("we have a function def")
- .substitute(Interner, &substs);
+ .instantiate(interner, args)
+ .to_chalk(interner);
let sig = f.callable_sig(self.db).expect("we have a function def");
Some((f, sig, true))
}
@@ -2057,12 +2074,10 @@
&mut self,
tgt_expr: ExprId,
args: &[ExprId],
- method_ty: Binders<Ty>,
- substs: Substitution,
+ method_ty: Ty,
receiver_ty: Ty,
expected: &Expectation,
) -> Ty {
- let method_ty = method_ty.substitute(Interner, &substs);
self.register_obligations_for_call(&method_ty);
let interner = self.table.interner;
let ((formal_receiver_ty, param_tys), ret_ty, is_varargs) =
@@ -2132,7 +2147,7 @@
let origin = ObligationCause::new();
ocx.sup(
&origin,
- self.table.param_env,
+ self.table.trait_env.env,
expected_output.to_nextsolver(interner),
formal_output,
)?;
@@ -2239,7 +2254,7 @@
let formal_ty_error = this
.table
.infer_ctxt
- .at(&ObligationCause::new(), this.table.param_env)
+ .at(&ObligationCause::new(), this.table.trait_env.env)
.eq(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty);
// If neither check failed, the types are compatible
diff --git a/crates/hir-ty/src/infer/fallback.rs b/crates/hir-ty/src/infer/fallback.rs
new file mode 100644
index 0000000..2022447
--- /dev/null
+++ b/crates/hir-ty/src/infer/fallback.rs
@@ -0,0 +1,439 @@
+//! Fallback of infer vars to `!` and `i32`/`f64`.
+
+use intern::sym;
+use petgraph::{
+ Graph,
+ visit::{Dfs, Walker},
+};
+use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
+use rustc_type_ir::{
+ TyVid,
+ inherent::{IntoKind, Ty as _},
+};
+use tracing::debug;
+
+use crate::{
+ infer::InferenceContext,
+ next_solver::{CoercePredicate, PredicateKind, SubtypePredicate, Ty, TyKind},
+};
+
+#[derive(Copy, Clone)]
+pub(crate) enum DivergingFallbackBehavior {
+ /// Always fallback to `()` (aka "always spontaneous decay")
+ ToUnit,
+ /// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken.
+ ContextDependent,
+ /// Always fallback to `!` (which should be equivalent to never falling back + not making
+ /// never-to-any coercions unless necessary)
+ ToNever,
+}
+
+impl<'db> InferenceContext<'db> {
+ pub(super) fn type_inference_fallback(&mut self) {
+ debug!(
+ "type-inference-fallback start obligations: {:#?}",
+ self.table.fulfillment_cx.pending_obligations()
+ );
+
+ // All type checking constraints were added, try to fallback unsolved variables.
+ self.table.select_obligations_where_possible();
+
+ debug!(
+ "type-inference-fallback post selection obligations: {:#?}",
+ self.table.fulfillment_cx.pending_obligations()
+ );
+
+ let fallback_occurred = self.fallback_types();
+
+ if !fallback_occurred {
+ return;
+ }
+
+ // We now see if we can make progress. This might cause us to
+ // unify inference variables for opaque types, since we may
+ // have unified some other type variables during the first
+ // phase of fallback. This means that we only replace
+ // inference variables with their underlying opaque types as a
+ // last resort.
+ //
+ // In code like this:
+ //
+ // ```rust
+ // type MyType = impl Copy;
+ // fn produce() -> MyType { true }
+ // fn bad_produce() -> MyType { panic!() }
+ // ```
+ //
+ // we want to unify the opaque inference variable in `bad_produce`
+ // with the diverging fallback for `panic!` (e.g. `()` or `!`).
+ // This will produce a nice error message about conflicting concrete
+ // types for `MyType`.
+ //
+ // If we had tried to fallback the opaque inference variable to `MyType`,
+ // we will generate a confusing type-check error that does not explicitly
+ // refer to opaque types.
+ self.table.select_obligations_where_possible();
+ }
+
+ fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior {
+ if self.krate().data(self.db).edition.at_least_2024() {
+ return DivergingFallbackBehavior::ToNever;
+ }
+
+ if self.resolver.def_map().is_unstable_feature_enabled(&sym::never_type_fallback) {
+ return DivergingFallbackBehavior::ContextDependent;
+ }
+
+ DivergingFallbackBehavior::ToUnit
+ }
+
+ fn fallback_types(&mut self) -> bool {
+ // Check if we have any unresolved variables. If not, no need for fallback.
+ let unresolved_variables = self.table.infer_ctxt.unresolved_variables();
+
+ if unresolved_variables.is_empty() {
+ return false;
+ }
+
+ let diverging_fallback_behavior = self.diverging_fallback_behavior();
+
+ let diverging_fallback =
+ self.calculate_diverging_fallback(&unresolved_variables, diverging_fallback_behavior);
+
+ // We do fallback in two passes, to try to generate
+ // better error messages.
+ // The first time, we do *not* replace opaque types.
+ let mut fallback_occurred = false;
+ for ty in unresolved_variables {
+ debug!("unsolved_variable = {:?}", ty);
+ fallback_occurred |= self.fallback_if_possible(ty, &diverging_fallback);
+ }
+
+ fallback_occurred
+ }
+
+ // Tries to apply a fallback to `ty` if it is an unsolved variable.
+ //
+ // - Unconstrained ints are replaced with `i32`.
+ //
+ // - Unconstrained floats are replaced with `f64`.
+ //
+ // - Non-numerics may get replaced with `()` or `!`, depending on
+ // how they were categorized by `calculate_diverging_fallback`
+ // (and the setting of `#![feature(never_type_fallback)]`).
+ //
+ // Fallback becomes very dubious if we have encountered
+ // type-checking errors. In that case, fallback to Error.
+ //
+ // Sets `FnCtxt::fallback_has_occurred` if fallback is performed
+ // during this call.
+ fn fallback_if_possible(
+ &mut self,
+ ty: Ty<'db>,
+ diverging_fallback: &FxHashMap<Ty<'db>, Ty<'db>>,
+ ) -> bool {
+ // Careful: we do NOT shallow-resolve `ty`. We know that `ty`
+ // is an unsolved variable, and we determine its fallback
+ // based solely on how it was created, not what other type
+ // variables it may have been unified with since then.
+ //
+ // The reason this matters is that other attempts at fallback
+ // may (in principle) conflict with this fallback, and we wish
+ // to generate a type error in that case. (However, this
+ // actually isn't true right now, because we're only using the
+ // builtin fallback rules. This would be true if we were using
+ // user-supplied fallbacks. But it's still useful to write the
+ // code to detect bugs.)
+ //
+ // (Note though that if we have a general type variable `?T`
+ // that is then unified with an integer type variable `?I`
+ // that ultimately never gets resolved to a special integral
+ // type, `?T` is not considered unsolved, but `?I` is. The
+ // same is true for float variables.)
+ let fallback = match ty.kind() {
+ TyKind::Infer(rustc_type_ir::IntVar(_)) => self.types.i32,
+ TyKind::Infer(rustc_type_ir::FloatVar(_)) => self.types.f64,
+ _ => match diverging_fallback.get(&ty) {
+ Some(&fallback_ty) => fallback_ty,
+ None => return false,
+ },
+ };
+ debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback);
+
+ self.demand_eqtype(ty, fallback);
+ true
+ }
+
+ /// The "diverging fallback" system is rather complicated. This is
+ /// a result of our need to balance 'do the right thing' with
+ /// backwards compatibility.
+ ///
+ /// "Diverging" type variables are variables created when we
+ /// coerce a `!` type into an unbound type variable `?X`. If they
+ /// never wind up being constrained, the "right and natural" thing
+ /// is that `?X` should "fallback" to `!`. This means that e.g. an
+ /// expression like `Some(return)` will ultimately wind up with a
+ /// type like `Option<!>` (presuming it is not assigned or
+ /// constrained to have some other type).
+ ///
+ /// However, the fallback used to be `()` (before the `!` type was
+ /// added). Moreover, there are cases where the `!` type 'leaks
+ /// out' from dead code into type variables that affect live
+ /// code. The most common case is something like this:
+ ///
+ /// ```rust
+ /// # fn foo() -> i32 { 4 }
+ /// match foo() {
+ /// 22 => Default::default(), // call this type `?D`
+ /// _ => return, // return has type `!`
+ /// } // call the type of this match `?M`
+ /// ```
+ ///
+ /// Here, coercing the type `!` into `?M` will create a diverging
+ /// type variable `?X` where `?X <: ?M`. We also have that `?D <:
+ /// ?M`. If `?M` winds up unconstrained, then `?X` will
+ /// fallback. If it falls back to `!`, then all the type variables
+ /// will wind up equal to `!` -- this includes the type `?D`
+ /// (since `!` doesn't implement `Default`, we wind up a "trait
+ /// not implemented" error in code like this). But since the
+ /// original fallback was `()`, this code used to compile with `?D
+ /// = ()`. This is somewhat surprising, since `Default::default()`
+ /// on its own would give an error because the types are
+ /// insufficiently constrained.
+ ///
+ /// Our solution to this dilemma is to modify diverging variables
+ /// so that they can *either* fallback to `!` (the default) or to
+ /// `()` (the backwards compatibility case). We decide which
+ /// fallback to use based on whether there is a coercion pattern
+ /// like this:
+ ///
+ /// ```ignore (not-rust)
+ /// ?Diverging -> ?V
+ /// ?NonDiverging -> ?V
+ /// ?V != ?NonDiverging
+ /// ```
+ ///
+ /// Here `?Diverging` represents some diverging type variable and
+ /// `?NonDiverging` represents some non-diverging type
+ /// variable. `?V` can be any type variable (diverging or not), so
+ /// long as it is not equal to `?NonDiverging`.
+ ///
+ /// Intuitively, what we are looking for is a case where a
+ /// "non-diverging" type variable (like `?M` in our example above)
+ /// is coerced *into* some variable `?V` that would otherwise
+ /// fallback to `!`. In that case, we make `?V` fallback to `!`,
+ /// along with anything that would flow into `?V`.
+ ///
+ /// The algorithm we use:
+ /// * Identify all variables that are coerced *into* by a
+ /// diverging variable. Do this by iterating over each
+ /// diverging, unsolved variable and finding all variables
+ /// reachable from there. Call that set `D`.
+ /// * Walk over all unsolved, non-diverging variables, and find
+ /// any variable that has an edge into `D`.
+ fn calculate_diverging_fallback(
+ &self,
+ unresolved_variables: &[Ty<'db>],
+ behavior: DivergingFallbackBehavior,
+ ) -> FxHashMap<Ty<'db>, Ty<'db>> {
+ debug!("calculate_diverging_fallback({:?})", unresolved_variables);
+
+ // Construct a coercion graph where an edge `A -> B` indicates
+ // a type variable is that is coerced
+ let coercion_graph = self.create_coercion_graph();
+
+ // Extract the unsolved type inference variable vids; note that some
+ // unsolved variables are integer/float variables and are excluded.
+ let unsolved_vids = unresolved_variables.iter().filter_map(|ty| ty.ty_vid());
+
+ // Compute the diverging root vids D -- that is, the root vid of
+ // those type variables that (a) are the target of a coercion from
+ // a `!` type and (b) have not yet been solved.
+ //
+ // These variables are the ones that are targets for fallback to
+ // either `!` or `()`.
+ let diverging_roots: FxHashSet<TyVid> = self
+ .table
+ .diverging_type_vars
+ .iter()
+ .map(|&ty| self.shallow_resolve(ty))
+ .filter_map(|ty| ty.ty_vid())
+ .map(|vid| self.table.infer_ctxt.root_var(vid))
+ .collect();
+ debug!(
+ "calculate_diverging_fallback: diverging_type_vars={:?}",
+ self.table.diverging_type_vars
+ );
+ debug!("calculate_diverging_fallback: diverging_roots={:?}", diverging_roots);
+
+ // Find all type variables that are reachable from a diverging
+ // type variable. These will typically default to `!`, unless
+ // we find later that they are *also* reachable from some
+ // other type variable outside this set.
+ let mut roots_reachable_from_diverging = Dfs::empty(&coercion_graph);
+ let mut diverging_vids = vec![];
+ let mut non_diverging_vids = vec![];
+ for unsolved_vid in unsolved_vids {
+ let root_vid = self.table.infer_ctxt.root_var(unsolved_vid);
+ debug!(
+ "calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}",
+ unsolved_vid,
+ root_vid,
+ diverging_roots.contains(&root_vid),
+ );
+ if diverging_roots.contains(&root_vid) {
+ diverging_vids.push(unsolved_vid);
+ roots_reachable_from_diverging.move_to(root_vid.as_u32().into());
+
+ // drain the iterator to visit all nodes reachable from this node
+ while roots_reachable_from_diverging.next(&coercion_graph).is_some() {}
+ } else {
+ non_diverging_vids.push(unsolved_vid);
+ }
+ }
+
+ debug!(
+ "calculate_diverging_fallback: roots_reachable_from_diverging={:?}",
+ roots_reachable_from_diverging,
+ );
+
+ // Find all type variables N0 that are not reachable from a
+ // diverging variable, and then compute the set reachable from
+ // N0, which we call N. These are the *non-diverging* type
+ // variables. (Note that this set consists of "root variables".)
+ let mut roots_reachable_from_non_diverging = Dfs::empty(&coercion_graph);
+ for &non_diverging_vid in &non_diverging_vids {
+ let root_vid = self.table.infer_ctxt.root_var(non_diverging_vid);
+ if roots_reachable_from_diverging.discovered.contains(root_vid.as_usize()) {
+ continue;
+ }
+ roots_reachable_from_non_diverging.move_to(root_vid.as_u32().into());
+ while roots_reachable_from_non_diverging.next(&coercion_graph).is_some() {}
+ }
+ debug!(
+ "calculate_diverging_fallback: roots_reachable_from_non_diverging={:?}",
+ roots_reachable_from_non_diverging,
+ );
+
+ debug!("obligations: {:#?}", self.table.fulfillment_cx.pending_obligations());
+
+ // For each diverging variable, figure out whether it can
+ // reach a member of N. If so, it falls back to `()`. Else
+ // `!`.
+ let mut diverging_fallback =
+ FxHashMap::with_capacity_and_hasher(diverging_vids.len(), FxBuildHasher);
+
+ for &diverging_vid in &diverging_vids {
+ let diverging_ty = Ty::new_var(self.table.interner, diverging_vid);
+ let root_vid = self.table.infer_ctxt.root_var(diverging_vid);
+ let can_reach_non_diverging = Dfs::new(&coercion_graph, root_vid.as_u32().into())
+ .iter(&coercion_graph)
+ .any(|n| roots_reachable_from_non_diverging.discovered.contains(n.index()));
+
+ let mut fallback_to = |ty| {
+ diverging_fallback.insert(diverging_ty, ty);
+ };
+
+ match behavior {
+ DivergingFallbackBehavior::ToUnit => {
+ debug!("fallback to () - legacy: {:?}", diverging_vid);
+ fallback_to(self.types.unit);
+ }
+ DivergingFallbackBehavior::ContextDependent => {
+ // FIXME: rustc does the following, but given this is only relevant when the unstable
+ // `never_type_fallback` feature is active, I chose to not port this.
+ // if found_infer_var_info.self_in_trait && found_infer_var_info.output {
+ // // This case falls back to () to ensure that the code pattern in
+ // // tests/ui/never_type/fallback-closure-ret.rs continues to
+ // // compile when never_type_fallback is enabled.
+ // //
+ // // This rule is not readily explainable from first principles,
+ // // but is rather intended as a patchwork fix to ensure code
+ // // which compiles before the stabilization of never type
+ // // fallback continues to work.
+ // //
+ // // Typically this pattern is encountered in a function taking a
+ // // closure as a parameter, where the return type of that closure
+ // // (checked by `relationship.output`) is expected to implement
+ // // some trait (checked by `relationship.self_in_trait`). This
+ // // can come up in non-closure cases too, so we do not limit this
+ // // rule to specifically `FnOnce`.
+ // //
+ // // When the closure's body is something like `panic!()`, the
+ // // return type would normally be inferred to `!`. However, it
+ // // needs to fall back to `()` in order to still compile, as the
+ // // trait is specifically implemented for `()` but not `!`.
+ // //
+ // // For details on the requirements for these relationships to be
+ // // set, see the relationship finding module in
+ // // compiler/rustc_trait_selection/src/traits/relationships.rs.
+ // debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
+ // fallback_to(self.types.unit);
+ // }
+ if can_reach_non_diverging {
+ debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
+ fallback_to(self.types.unit);
+ } else {
+ debug!("fallback to ! - all diverging: {:?}", diverging_vid);
+ fallback_to(self.types.never);
+ }
+ }
+ DivergingFallbackBehavior::ToNever => {
+ debug!(
+ "fallback to ! - `rustc_never_type_mode = \"fallback_to_never\")`: {:?}",
+ diverging_vid
+ );
+ fallback_to(self.types.never);
+ }
+ }
+ }
+
+ diverging_fallback
+ }
+
+ /// Returns a graph whose nodes are (unresolved) inference variables and where
+ /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`.
+ fn create_coercion_graph(&self) -> Graph<(), ()> {
+ let pending_obligations = self.table.fulfillment_cx.pending_obligations();
+ let pending_obligations_len = pending_obligations.len();
+ debug!("create_coercion_graph: pending_obligations={:?}", pending_obligations);
+ let coercion_edges = pending_obligations
+ .into_iter()
+ .filter_map(|obligation| {
+ // The predicates we are looking for look like `Coerce(?A -> ?B)`.
+ // They will have no bound variables.
+ obligation.predicate.kind().no_bound_vars()
+ })
+ .filter_map(|atom| {
+ // We consider both subtyping and coercion to imply 'flow' from
+ // some position in the code `a` to a different position `b`.
+ // This is then used to determine which variables interact with
+ // live code, and as such must fall back to `()` to preserve
+ // soundness.
+ //
+ // In practice currently the two ways that this happens is
+ // coercion and subtyping.
+ let (a, b) = match atom {
+ PredicateKind::Coerce(CoercePredicate { a, b }) => (a, b),
+ PredicateKind::Subtype(SubtypePredicate { a_is_expected: _, a, b }) => (a, b),
+ _ => return None,
+ };
+
+ let a_vid = self.root_vid(a)?;
+ let b_vid = self.root_vid(b)?;
+ Some((a_vid.as_u32(), b_vid.as_u32()))
+ });
+ let num_ty_vars = self.table.infer_ctxt.num_ty_vars();
+ let mut graph = Graph::with_capacity(num_ty_vars, pending_obligations_len);
+ for _ in 0..num_ty_vars {
+ graph.add_node(());
+ }
+ graph.extend_with_edges(coercion_edges);
+ graph
+ }
+
+ /// If `ty` is an unresolved type variable, returns its root vid.
+ fn root_vid(&self, ty: Ty<'db>) -> Option<TyVid> {
+ Some(self.table.infer_ctxt.root_var(self.shallow_resolve(ty).ty_vid()?))
+ }
+}
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 80f7324..733f3c2 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -17,7 +17,10 @@
generics::generics,
infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext,
method_resolution::{self, VisibleFromModule},
- next_solver::mapping::ChalkToNextSolver,
+ next_solver::{
+ DbInterner,
+ mapping::{ChalkToNextSolver, NextSolverToChalk},
+ },
to_chalk_trait_id,
};
@@ -36,7 +39,9 @@
self.add_required_obligations_for_value_path(generic_def, &substs);
- let ty = self.db.value_ty(value_def)?.substitute(Interner, &substs);
+ let interner = DbInterner::new_with(self.db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let ty = self.db.value_ty(value_def)?.instantiate(interner, args).to_chalk(interner);
let ty = self.process_remote_user_written_ty(ty);
Some(ty)
}
@@ -69,8 +74,11 @@
}
ValueNs::ImplSelf(impl_id) => {
let generics = crate::generics::generics(self.db, impl_id.into());
+ let interner = DbInterner::new_with(self.db, None, None);
let substs = generics.placeholder_subst(self.db);
- let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let ty =
+ self.db.impl_self_ty(impl_id).instantiate(interner, args).to_chalk(interner);
return if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() {
Some(ValuePathResolution::GenericDef(
struct_id.into(),
@@ -89,9 +97,9 @@
let generic_def = value_def.to_generic_def_id(self.db);
if let GenericDefId::StaticId(_) = generic_def {
+ let interner = DbInterner::new_with(self.db, None, None);
// `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type.
- let (ty, binders) = self.db.value_ty(value_def)?.into_value_and_skipped_binders();
- stdx::always!(binders.is_empty(Interner), "non-empty binders for non-generic def",);
+ let ty = self.db.value_ty(value_def)?.skip_binder().to_chalk(interner);
return Some(ValuePathResolution::NonGeneric(ty));
};
@@ -354,10 +362,13 @@
};
let substs = match container {
ItemContainerId::ImplId(impl_id) => {
+ let interner = DbInterner::new_with(self.db, None, None);
let impl_substs = TyBuilder::subst_for_def(self.db, impl_id, None)
.fill_with_inference_vars(&mut self.table)
.build();
- let impl_self_ty = self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs);
+ let args: crate::next_solver::GenericArgs<'_> = impl_substs.to_nextsolver(interner);
+ let impl_self_ty =
+ self.db.impl_self_ty(impl_id).instantiate(interner, args).to_chalk(interner);
self.unify(&impl_self_ty, &ty);
impl_substs
}
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 1687857..108cf5b 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -3,47 +3,42 @@
use std::fmt;
use chalk_ir::{
- CanonicalVarKind, FloatTy, IntTy, TyVariableKind, cast::Cast, fold::TypeFoldable,
- interner::HasInterner,
+ CanonicalVarKind, TyVariableKind, cast::Cast, fold::TypeFoldable, interner::HasInterner,
};
use either::Either;
use hir_def::{AdtId, lang_item::LangItem};
use hir_expand::name::Name;
use intern::sym;
use rustc_hash::{FxHashMap, FxHashSet};
-use rustc_type_ir::inherent::Ty as _;
use rustc_type_ir::{
- FloatVid, IntVid, TyVid, TypeVisitableExt,
- inherent::{IntoKind, Span, Term as _},
+ TyVid, TypeVisitableExt, UpcastFrom,
+ inherent::{IntoKind, Span, Term as _, Ty as _},
relate::{Relate, solver_relating::RelateExt},
- solve::{Certainty, GoalSource, NoSolution},
+ solve::{Certainty, GoalSource},
};
use smallvec::SmallVec;
use triomphe::Arc;
use super::{InferResult, InferenceContext, TypeError};
-use crate::next_solver::ErrorGuaranteed;
use crate::{
AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, GenericArg, GenericArgData,
- Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind,
- ProjectionTy, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
- VariableKind, WhereClause,
+ InferenceVar, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution, TraitEnvironment, Ty,
+ TyExt, TyKind, VariableKind,
consteval::unknown_const,
db::HirDatabase,
fold_generic_args, fold_tys_and_consts,
- next_solver::infer::InferOk,
next_solver::{
- self, ClauseKind, DbInterner, ParamEnv, Predicate, PredicateKind, SolverDefIds, Term,
+ self, ClauseKind, DbInterner, ErrorGuaranteed, Predicate, PredicateKind, SolverDefIds,
+ Term, TraitRef,
fulfill::FulfillmentCtxt,
infer::{
- DbInternerInferExt, InferCtxt,
+ DbInternerInferExt, InferCtxt, InferOk,
snapshot::CombinedSnapshot,
traits::{Obligation, ObligationCause},
},
inspect::{InspectConfig, InspectGoal, ProofTreeVisitor},
mapping::{ChalkToNextSolver, NextSolverToChalk},
},
- to_chalk_trait_id,
traits::{
FnTrait, NextTraitSolveResult, next_trait_solve_canonical_in_ctxt, next_trait_solve_in_ctxt,
},
@@ -125,7 +120,7 @@
/// unresolved goal `T = U`.
pub fn could_unify(
db: &dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'_>>,
tys: &Canonical<(Ty, Ty)>,
) -> bool {
unify(db, env, tys).is_some()
@@ -137,7 +132,7 @@
/// them. For example `Option<T>` and `Option<U>` do not unify as we cannot show that `T = U`
pub fn could_unify_deeply(
db: &dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'_>>,
tys: &Canonical<(Ty, Ty)>,
) -> bool {
let mut table = InferenceTable::new(db, env);
@@ -147,7 +142,6 @@
let ty1_with_vars = table.normalize_associated_types_in(ty1_with_vars);
let ty2_with_vars = table.normalize_associated_types_in(ty2_with_vars);
table.select_obligations_where_possible();
- table.propagate_diverging_flag();
let ty1_with_vars = table.resolve_completely(ty1_with_vars);
let ty2_with_vars = table.resolve_completely(ty2_with_vars);
table.unify_deeply(&ty1_with_vars, &ty2_with_vars)
@@ -155,7 +149,7 @@
pub(crate) fn unify(
db: &dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'_>>,
tys: &Canonical<(Ty, Ty)>,
) -> Option<Substitution> {
let mut table = InferenceTable::new(db, env);
@@ -174,13 +168,19 @@
GenericArgData::Const(c) => c.inference_var(Interner),
} == Some(iv))
};
- let fallback = |iv, kind, default, binder| match kind {
- chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv)
- .map_or(default, |i| BoundVar::new(binder, i).to_ty(Interner).cast(Interner)),
- chalk_ir::VariableKind::Lifetime => find_var(iv)
- .map_or(default, |i| BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner)),
- chalk_ir::VariableKind::Const(ty) => find_var(iv)
- .map_or(default, |i| BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner)),
+ let fallback = |iv, kind, binder| match kind {
+ chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv).map_or_else(
+ || TyKind::Error.intern(Interner).cast(Interner),
+ |i| BoundVar::new(binder, i).to_ty(Interner).cast(Interner),
+ ),
+ chalk_ir::VariableKind::Lifetime => find_var(iv).map_or_else(
+ || crate::error_lifetime().cast(Interner),
+ |i| BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner),
+ ),
+ chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or_else(
+ || crate::unknown_const(ty.clone()).cast(Interner),
+ |i| BoundVar::new(binder, i).to_const(Interner, ty.clone()).cast(Interner),
+ ),
};
Some(Substitution::from_iter(
Interner,
@@ -216,22 +216,20 @@
pub(crate) struct InferenceTable<'db> {
pub(crate) db: &'db dyn HirDatabase,
pub(crate) interner: DbInterner<'db>,
- pub(crate) trait_env: Arc<TraitEnvironment>,
- pub(crate) param_env: ParamEnv<'db>,
+ pub(crate) trait_env: Arc<TraitEnvironment<'db>>,
pub(crate) tait_coercion_table: Option<FxHashMap<OpaqueTyId, Ty>>,
pub(crate) infer_ctxt: InferCtxt<'db>,
- diverging_tys: FxHashSet<Ty>,
pub(super) fulfillment_cx: FulfillmentCtxt<'db>,
+ pub(super) diverging_type_vars: FxHashSet<crate::next_solver::Ty<'db>>,
}
pub(crate) struct InferenceTableSnapshot<'db> {
ctxt_snapshot: CombinedSnapshot,
obligations: FulfillmentCtxt<'db>,
- diverging_tys: FxHashSet<Ty>,
}
impl<'db> InferenceTable<'db> {
- pub(crate) fn new(db: &'db dyn HirDatabase, trait_env: Arc<TraitEnvironment>) -> Self {
+ pub(crate) fn new(db: &'db dyn HirDatabase, trait_env: Arc<TraitEnvironment<'db>>) -> Self {
let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block);
let infer_ctxt = interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis {
defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []),
@@ -239,12 +237,11 @@
InferenceTable {
db,
interner,
- param_env: trait_env.env.to_nextsolver(interner),
trait_env,
tait_coercion_table: None,
fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt),
infer_ctxt,
- diverging_tys: FxHashSet::default(),
+ diverging_type_vars: FxHashSet::default(),
}
}
@@ -327,74 +324,8 @@
}
}
- /// Chalk doesn't know about the `diverging` flag, so when it unifies two
- /// type variables of which one is diverging, the chosen root might not be
- /// diverging and we have no way of marking it as such at that time. This
- /// function goes through all type variables and make sure their root is
- /// marked as diverging if necessary, so that resolving them gives the right
- /// result.
- pub(super) fn propagate_diverging_flag(&mut self) {
- let mut new_tys = FxHashSet::default();
- for ty in self.diverging_tys.iter() {
- match ty.kind(Interner) {
- TyKind::InferenceVar(var, kind) => match kind {
- TyVariableKind::General => {
- let root = InferenceVar::from(
- self.infer_ctxt.root_var(TyVid::from_u32(var.index())).as_u32(),
- );
- if root.index() != var.index() {
- new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner));
- }
- }
- TyVariableKind::Integer => {
- let root = InferenceVar::from(
- self.infer_ctxt
- .inner
- .borrow_mut()
- .int_unification_table()
- .find(IntVid::from_usize(var.index() as usize))
- .as_u32(),
- );
- if root.index() != var.index() {
- new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner));
- }
- }
- TyVariableKind::Float => {
- let root = InferenceVar::from(
- self.infer_ctxt
- .inner
- .borrow_mut()
- .float_unification_table()
- .find(FloatVid::from_usize(var.index() as usize))
- .as_u32(),
- );
- if root.index() != var.index() {
- new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner));
- }
- }
- },
- _ => {}
- }
- }
- self.diverging_tys.extend(new_tys);
- }
-
- pub(super) fn set_diverging(&mut self, iv: InferenceVar, kind: TyVariableKind) {
- self.diverging_tys.insert(TyKind::InferenceVar(iv, kind).intern(Interner));
- }
-
- fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty {
- let is_diverging =
- self.diverging_tys.contains(&TyKind::InferenceVar(iv, kind).intern(Interner));
- if is_diverging {
- return TyKind::Never.intern(Interner);
- }
- match kind {
- TyVariableKind::General => TyKind::Error,
- TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)),
- TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)),
- }
- .intern(Interner)
+ pub(super) fn set_diverging(&mut self, ty: crate::next_solver::Ty<'db>) {
+ self.diverging_type_vars.insert(ty);
}
pub(crate) fn canonicalize<T>(&mut self, t: T) -> rustc_type_ir::Canonical<DbInterner<'db>, T>
@@ -430,7 +361,7 @@
{
let ty = self.resolve_vars_with_obligations(ty);
self.infer_ctxt
- .at(&ObligationCause::new(), self.param_env)
+ .at(&ObligationCause::new(), self.trait_env.env)
.deeply_normalize(ty.clone())
.unwrap_or(ty)
}
@@ -535,7 +466,7 @@
let ty = var.to_ty(Interner, kind);
if diverging {
- self.diverging_tys.insert(ty.clone());
+ self.diverging_type_vars.insert(ty.to_nextsolver(self.interner));
}
ty
}
@@ -579,7 +510,7 @@
pub(crate) fn resolve_with_fallback<T>(
&mut self,
t: T,
- fallback: &dyn Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
+ fallback: &dyn Fn(InferenceVar, VariableKind, DebruijnIndex) -> GenericArg,
) -> T
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
@@ -621,7 +552,7 @@
fn resolve_with_fallback_inner<T>(
&mut self,
t: T,
- fallback: &dyn Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
+ fallback: &dyn Fn(InferenceVar, VariableKind, DebruijnIndex) -> GenericArg,
) -> T
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
@@ -638,53 +569,15 @@
T: HasInterner<Interner = Interner> + TypeFoldable<Interner> + ChalkToNextSolver<'db, U>,
U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable<DbInterner<'db>>,
{
- let t = self.resolve_with_fallback(t, &|_, _, d, _| d);
- let t = self.normalize_associated_types_in(t);
- // let t = self.resolve_opaque_tys_in(t);
- // Resolve again, because maybe normalization inserted infer vars.
- self.resolve_with_fallback(t, &|_, _, d, _| d)
- }
+ let value = t.to_nextsolver(self.interner);
+ let value = self.infer_ctxt.resolve_vars_if_possible(value);
- /// Apply a fallback to unresolved scalar types. Integer type variables and float type
- /// variables are replaced with i32 and f64, respectively.
- ///
- /// This method is only intended to be called just before returning inference results (i.e. in
- /// `InferenceContext::resolve_all()`).
- ///
- /// FIXME: This method currently doesn't apply fallback to unconstrained general type variables
- /// whereas rustc replaces them with `()` or `!`.
- pub(super) fn fallback_if_possible(&mut self) {
- let int_fallback = TyKind::Scalar(Scalar::Int(IntTy::I32)).intern(Interner);
- let float_fallback = TyKind::Scalar(Scalar::Float(FloatTy::F64)).intern(Interner);
+ let mut goals = vec![];
+ let value = value.fold_with(&mut resolve_completely::Resolver::new(self, true, &mut goals));
- let int_vars = self.infer_ctxt.inner.borrow_mut().int_unification_table().len();
- for v in 0..int_vars {
- let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Integer);
- let maybe_resolved = self.resolve_ty_shallow(&var);
- if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) {
- // I don't think we can ever unify these vars with float vars, but keep this here for now
- let fallback = match kind {
- TyVariableKind::Integer => &int_fallback,
- TyVariableKind::Float => &float_fallback,
- TyVariableKind::General => unreachable!(),
- };
- self.unify(&var, fallback);
- }
- }
- let float_vars = self.infer_ctxt.inner.borrow_mut().float_unification_table().len();
- for v in 0..float_vars {
- let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Float);
- let maybe_resolved = self.resolve_ty_shallow(&var);
- if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) {
- // I don't think we can ever unify these vars with float vars, but keep this here for now
- let fallback = match kind {
- TyVariableKind::Integer => &int_fallback,
- TyVariableKind::Float => &float_fallback,
- TyVariableKind::General => unreachable!(),
- };
- self.unify(&var, fallback);
- }
- }
+ // FIXME(next-solver): Handle `goals`.
+
+ value.to_chalk(self.interner)
}
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
@@ -745,7 +638,7 @@
) -> InferResult<'db, ()> {
let variance = rustc_type_ir::Variance::Invariant;
let span = crate::next_solver::Span::dummy();
- match self.infer_ctxt.relate(self.param_env, lhs, variance, rhs, span) {
+ match self.infer_ctxt.relate(self.trait_env.env, lhs, variance, rhs, span) {
Ok(goals) => Ok(crate::infer::InferOk { goals, value: () }),
Err(_) => Err(TypeError),
}
@@ -786,7 +679,7 @@
}
pub(crate) fn structurally_resolve_type(&mut self, ty: &Ty) -> Ty {
- if let TyKind::Alias(..) = ty.kind(Interner) {
+ if let TyKind::Alias(chalk_ir::AliasTy::Projection(..)) = ty.kind(Interner) {
self.structurally_normalize_ty(ty)
} else {
self.resolve_vars_with_obligations(ty.to_nextsolver(self.interner))
@@ -802,7 +695,7 @@
fn structurally_normalize_term(&mut self, term: Term<'db>) -> Term<'db> {
self.infer_ctxt
- .at(&ObligationCause::new(), self.param_env)
+ .at(&ObligationCause::new(), self.trait_env.env)
.structurally_normalize_term(term, &mut self.fulfillment_cx)
.unwrap_or(term)
}
@@ -822,7 +715,7 @@
// in a reentrant borrow, causing an ICE.
let result = self
.infer_ctxt
- .at(&ObligationCause::misc(), self.param_env)
+ .at(&ObligationCause::misc(), self.trait_env.env)
.structurally_normalize_ty(ty, &mut self.fulfillment_cx);
match result {
Ok(normalized_ty) => normalized_ty,
@@ -835,15 +728,13 @@
pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'db> {
let ctxt_snapshot = self.infer_ctxt.start_snapshot();
- let diverging_tys = self.diverging_tys.clone();
let obligations = self.fulfillment_cx.clone();
- InferenceTableSnapshot { ctxt_snapshot, diverging_tys, obligations }
+ InferenceTableSnapshot { ctxt_snapshot, obligations }
}
#[tracing::instrument(skip_all)]
pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'db>) {
self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot);
- self.diverging_tys = snapshot.diverging_tys;
self.fulfillment_cx = snapshot.obligations;
}
@@ -877,26 +768,15 @@
/// whether a trait *might* be implemented before deciding to 'lock in' the
/// choice (during e.g. method resolution or deref).
#[tracing::instrument(level = "debug", skip(self))]
- pub(crate) fn try_obligation(&mut self, goal: Goal) -> NextTraitSolveResult {
- let in_env = InEnvironment::new(&self.trait_env.env, goal);
- let canonicalized = self.canonicalize(in_env.to_nextsolver(self.interner));
+ pub(crate) fn try_obligation(&mut self, predicate: Predicate<'db>) -> NextTraitSolveResult {
+ let goal = next_solver::Goal { param_env: self.trait_env.env, predicate };
+ let canonicalized = self.canonicalize(goal);
next_trait_solve_canonical_in_ctxt(&self.infer_ctxt, canonicalized)
}
- #[tracing::instrument(level = "debug", skip(self))]
- pub(crate) fn solve_obligation(&mut self, goal: Goal) -> Result<Certainty, NoSolution> {
- let goal = InEnvironment::new(&self.trait_env.env, goal);
- let goal = goal.to_nextsolver(self.interner);
- let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal);
- result.map(|m| m.1)
- }
-
pub(crate) fn register_obligation(&mut self, predicate: Predicate<'db>) {
- let goal = next_solver::Goal {
- param_env: self.trait_env.env.to_nextsolver(self.interner),
- predicate,
- };
+ let goal = next_solver::Goal { param_env: self.trait_env.env, predicate };
self.register_obligation_in_env(goal)
}
@@ -984,7 +864,7 @@
&mut self,
ty: &Ty,
num_args: usize,
- ) -> Option<(FnTrait, Vec<crate::next_solver::Ty<'db>>, crate::next_solver::Ty<'db>)> {
+ ) -> Option<(FnTrait, Vec<next_solver::Ty<'db>>, next_solver::Ty<'db>)> {
for (fn_trait_name, output_assoc_name, subtraits) in [
(FnTrait::FnOnce, sym::Output, &[FnTrait::Fn, FnTrait::FnMut][..]),
(FnTrait::AsyncFnMut, sym::CallRefFuture, &[FnTrait::AsyncFn]),
@@ -997,42 +877,34 @@
trait_data.associated_type_by_name(&Name::new_symbol_root(output_assoc_name))?;
let mut arg_tys = Vec::with_capacity(num_args);
- let arg_ty = TyBuilder::tuple(num_args)
- .fill(|it| {
- let arg = match it {
- ParamKind::Type => self.new_type_var(),
- ParamKind::Lifetime => unreachable!("Tuple with lifetime parameter"),
- ParamKind::Const(_) => unreachable!("Tuple with const parameter"),
- };
- arg_tys.push(arg.to_nextsolver(self.interner));
- arg.cast(Interner)
+ let arg_ty = next_solver::Ty::new_tup_from_iter(
+ self.interner,
+ std::iter::repeat_with(|| {
+ let ty = self.next_ty_var();
+ arg_tys.push(ty);
+ ty
})
- .build();
+ .take(num_args),
+ );
+ let args = [ty.to_nextsolver(self.interner), arg_ty];
+ let trait_ref = crate::next_solver::TraitRef::new(self.interner, fn_trait.into(), args);
- let b = TyBuilder::trait_ref(self.db, fn_trait);
- if b.remaining() != 2 {
- return None;
- }
- let mut trait_ref = b.push(ty.clone()).push(arg_ty).build();
+ let projection = crate::next_solver::Ty::new_alias(
+ self.interner,
+ rustc_type_ir::AliasTyKind::Projection,
+ crate::next_solver::AliasTy::new(self.interner, output_assoc_type.into(), args),
+ );
- let projection = TyBuilder::assoc_type_projection(
- self.db,
- output_assoc_type,
- Some(trait_ref.substitution.clone()),
- )
- .fill_with_unknown()
- .build();
-
- let goal: Goal = trait_ref.clone().cast(Interner);
- if !self.try_obligation(goal.clone()).no_solution() {
- self.register_obligation(goal.to_nextsolver(self.interner));
- let return_ty =
- self.normalize_projection_ty(projection).to_nextsolver(self.interner);
+ let pred = crate::next_solver::Predicate::upcast_from(trait_ref, self.interner);
+ if !self.try_obligation(pred).no_solution() {
+ self.register_obligation(pred);
+ let return_ty = self.normalize_alias_ty(projection);
for &fn_x in subtraits {
let fn_x_trait = fn_x.get_id(self.db, krate)?;
- trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
- let goal = trait_ref.clone().cast(Interner);
- if !self.try_obligation(goal).no_solution() {
+ let trait_ref =
+ crate::next_solver::TraitRef::new(self.interner, fn_x_trait.into(), args);
+ let pred = crate::next_solver::Predicate::upcast_from(trait_ref, self.interner);
+ if !self.try_obligation(pred).no_solution() {
return Some((fn_x, arg_tys, return_ty));
}
}
@@ -1171,12 +1043,11 @@
let Some(sized) = LangItem::Sized.resolve_trait(self.db, self.trait_env.krate) else {
return false;
};
- let sized_pred = WhereClause::Implemented(TraitRef {
- trait_id: to_chalk_trait_id(sized),
- substitution: Substitution::from1(Interner, ty),
- });
- let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(sized_pred)).intern(Interner);
- self.try_obligation(goal).certain()
+ let sized_pred = Predicate::upcast_from(
+ TraitRef::new(self.interner, sized.into(), [ty.to_nextsolver(self.interner)]),
+ self.interner,
+ );
+ self.try_obligation(sized_pred).certain()
}
}
@@ -1192,14 +1063,10 @@
mod resolve {
use super::InferenceTable;
use crate::{
- ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg,
- InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind,
- next_solver::mapping::NextSolverToChalk,
+ Const, DebruijnIndex, GenericArg, InferenceVar, Interner, Lifetime, Ty, TyVariableKind,
+ VariableKind, next_solver::mapping::NextSolverToChalk,
};
- use chalk_ir::{
- cast::Cast,
- fold::{TypeFoldable, TypeFolder},
- };
+ use chalk_ir::fold::{TypeFoldable, TypeFolder};
use rustc_type_ir::{FloatVid, IntVid, TyVid};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -1213,7 +1080,7 @@
pub(super) struct Resolver<
'a,
'b,
- F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
+ F: Fn(InferenceVar, VariableKind, DebruijnIndex) -> GenericArg,
> {
pub(super) table: &'a mut InferenceTable<'b>,
pub(super) var_stack: &'a mut Vec<(InferenceVar, VarKind)>,
@@ -1221,7 +1088,7 @@
}
impl<F> TypeFolder<Interner> for Resolver<'_, '_, F>
where
- F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
+ F: Fn(InferenceVar, VariableKind, DebruijnIndex) -> GenericArg,
{
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner> {
self
@@ -1243,8 +1110,7 @@
let var = InferenceVar::from(vid.as_u32());
if self.var_stack.contains(&(var, VarKind::Ty(kind))) {
// recursive type
- let default = self.table.fallback_value(var, kind).cast(Interner);
- return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
+ return (self.fallback)(var, VariableKind::Ty(kind), outer_binder)
.assert_ty_ref(Interner)
.clone();
}
@@ -1256,8 +1122,7 @@
self.var_stack.pop();
result
} else {
- let default = self.table.fallback_value(var, kind).cast(Interner);
- (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
+ (self.fallback)(var, VariableKind::Ty(kind), outer_binder)
.assert_ty_ref(Interner)
.clone()
}
@@ -1273,8 +1138,7 @@
let var = InferenceVar::from(vid.as_u32());
if self.var_stack.contains(&(var, VarKind::Ty(kind))) {
// recursive type
- let default = self.table.fallback_value(var, kind).cast(Interner);
- return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
+ return (self.fallback)(var, VariableKind::Ty(kind), outer_binder)
.assert_ty_ref(Interner)
.clone();
}
@@ -1286,8 +1150,7 @@
self.var_stack.pop();
result
} else {
- let default = self.table.fallback_value(var, kind).cast(Interner);
- (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
+ (self.fallback)(var, VariableKind::Ty(kind), outer_binder)
.assert_ty_ref(Interner)
.clone()
}
@@ -1303,8 +1166,7 @@
let var = InferenceVar::from(vid.as_u32());
if self.var_stack.contains(&(var, VarKind::Ty(kind))) {
// recursive type
- let default = self.table.fallback_value(var, kind).cast(Interner);
- return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
+ return (self.fallback)(var, VariableKind::Ty(kind), outer_binder)
.assert_ty_ref(Interner)
.clone();
}
@@ -1316,8 +1178,7 @@
self.var_stack.pop();
result
} else {
- let default = self.table.fallback_value(var, kind).cast(Interner);
- (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
+ (self.fallback)(var, VariableKind::Ty(kind), outer_binder)
.assert_ty_ref(Interner)
.clone()
}
@@ -1336,15 +1197,9 @@
.infer_ctxt
.root_const_var(rustc_type_ir::ConstVid::from_u32(var.index()));
let var = InferenceVar::from(vid.as_u32());
- let default = ConstData {
- ty: ty.clone(),
- value: ConstValue::Concrete(ConcreteConst { interned: ConstScalar::Unknown }),
- }
- .intern(Interner)
- .cast(Interner);
if self.var_stack.contains(&(var, VarKind::Const)) {
// recursive
- return (self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
+ return (self.fallback)(var, VariableKind::Const(ty), outer_binder)
.assert_const_ref(Interner)
.clone();
}
@@ -1356,7 +1211,7 @@
self.var_stack.pop();
result
} else {
- (self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
+ (self.fallback)(var, VariableKind::Const(ty), outer_binder)
.assert_const_ref(Interner)
.clone()
}
@@ -1375,3 +1230,124 @@
}
}
}
+
+mod resolve_completely {
+ use rustc_type_ir::{
+ DebruijnIndex, Flags, TypeFolder, TypeSuperFoldable,
+ inherent::{Const as _, Ty as _},
+ };
+
+ use crate::next_solver::Region;
+ use crate::{
+ infer::unify::InferenceTable,
+ next_solver::{
+ Const, DbInterner, ErrorGuaranteed, Goal, Predicate, Term, Ty,
+ infer::traits::ObligationCause,
+ normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals,
+ },
+ };
+
+ pub(super) struct Resolver<'a, 'db> {
+ ctx: &'a mut InferenceTable<'db>,
+ /// Whether we should normalize, disabled when resolving predicates.
+ should_normalize: bool,
+ nested_goals: &'a mut Vec<Goal<'db, Predicate<'db>>>,
+ }
+
+ impl<'a, 'db> Resolver<'a, 'db> {
+ pub(super) fn new(
+ ctx: &'a mut InferenceTable<'db>,
+ should_normalize: bool,
+ nested_goals: &'a mut Vec<Goal<'db, Predicate<'db>>>,
+ ) -> Resolver<'a, 'db> {
+ Resolver { ctx, nested_goals, should_normalize }
+ }
+
+ fn handle_term<T>(
+ &mut self,
+ value: T,
+ outer_exclusive_binder: impl FnOnce(T) -> DebruijnIndex,
+ ) -> T
+ where
+ T: Into<Term<'db>> + TypeSuperFoldable<DbInterner<'db>> + Copy,
+ {
+ let value = if self.should_normalize {
+ let cause = ObligationCause::new();
+ let at = self.ctx.infer_ctxt.at(&cause, self.ctx.trait_env.env);
+ let universes = vec![None; outer_exclusive_binder(value).as_usize()];
+ match deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
+ at, value, universes,
+ ) {
+ Ok((value, goals)) => {
+ self.nested_goals.extend(goals);
+ value
+ }
+ Err(_errors) => {
+ // FIXME: Report the error.
+ value
+ }
+ }
+ } else {
+ value
+ };
+
+ value.fold_with(&mut ReplaceInferWithError { interner: self.ctx.interner })
+ }
+ }
+
+ impl<'cx, 'db> TypeFolder<DbInterner<'db>> for Resolver<'cx, 'db> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.ctx.interner
+ }
+
+ fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
+ if r.is_var() { Region::error(self.ctx.interner) } else { r }
+ }
+
+ fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
+ self.handle_term(ty, |it| it.outer_exclusive_binder())
+ }
+
+ fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
+ self.handle_term(ct, |it| it.outer_exclusive_binder())
+ }
+
+ fn fold_predicate(&mut self, predicate: Predicate<'db>) -> Predicate<'db> {
+ assert!(
+ !self.should_normalize,
+ "normalizing predicates in writeback is not generally sound"
+ );
+ predicate.super_fold_with(self)
+ }
+ }
+
+ struct ReplaceInferWithError<'db> {
+ interner: DbInterner<'db>,
+ }
+
+ impl<'db> TypeFolder<DbInterner<'db>> for ReplaceInferWithError<'db> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.interner
+ }
+
+ fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
+ if t.is_infer() {
+ Ty::new_error(self.interner, ErrorGuaranteed)
+ } else {
+ t.super_fold_with(self)
+ }
+ }
+
+ fn fold_const(&mut self, c: Const<'db>) -> Const<'db> {
+ if c.is_ct_infer() {
+ Const::new_error(self.interner, ErrorGuaranteed)
+ } else {
+ c.super_fold_with(self)
+ }
+ }
+
+ fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
+ if r.is_var() { Region::error(self.interner) } else { r }
+ }
+ }
+}
diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs
index b16b6a1..bdebe41 100644
--- a/crates/hir-ty/src/inhabitedness.rs
+++ b/crates/hir-ty/src/inhabitedness.rs
@@ -20,7 +20,7 @@
db: &dyn HirDatabase,
ty: &Ty,
target_mod: ModuleId,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'_>>,
) -> bool {
let _p = tracing::info_span!("is_ty_uninhabited_from", ?ty).entered();
let mut uninhabited_from =
@@ -36,7 +36,7 @@
variant: EnumVariantId,
subst: &Substitution,
target_mod: ModuleId,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'_>>,
) -> bool {
let _p = tracing::info_span!("is_enum_variant_uninhabited_from").entered();
@@ -52,7 +52,7 @@
// guard for preventing stack overflow in non trivial non terminating types
max_depth: usize,
db: &'a dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'a>>,
}
const CONTINUE_OPAQUELY_INHABITED: ControlFlow<VisiblyUninhabited> = Continue(());
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
index f21673c..4071b9a 100644
--- a/crates/hir-ty/src/layout.rs
+++ b/crates/hir-ty/src/layout.rs
@@ -132,7 +132,7 @@
id: StructId,
repr_packed: bool,
args: &GenericArgs<'db>,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
dl: &TargetDataLayout,
) -> Result<Arc<Layout>, LayoutError> {
// Supported SIMD vectors are homogeneous ADTs with exactly one array field:
@@ -160,7 +160,7 @@
pub fn layout_of_ty_query<'db>(
db: &'db dyn HirDatabase,
ty: Ty<'db>,
- trait_env: Arc<TraitEnvironment>,
+ trait_env: Arc<TraitEnvironment<'db>>,
) -> Result<Arc<Layout>, LayoutError> {
let krate = trait_env.krate;
let interner = DbInterner::new_with(db, Some(krate), trait_env.block);
@@ -371,7 +371,7 @@
pub(crate) fn layout_of_ty_cycle_result<'db>(
_: &dyn HirDatabase,
_: Ty<'db>,
- _: Arc<TraitEnvironment>,
+ _: Arc<TraitEnvironment<'db>>,
) -> Result<Arc<Layout>, LayoutError> {
Err(LayoutError::RecursiveTypeWithoutIndirection)
}
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs
index 9a746ca..a8f04bf 100644
--- a/crates/hir-ty/src/layout/adt.rs
+++ b/crates/hir-ty/src/layout/adt.rs
@@ -23,7 +23,7 @@
db: &'db dyn HirDatabase,
def: AdtId,
args: GenericArgs<'db>,
- trait_env: Arc<TraitEnvironment>,
+ trait_env: Arc<TraitEnvironment<'db>>,
) -> Result<Arc<Layout>, LayoutError> {
let krate = trait_env.krate;
let Ok(target) = db.target_data_layout(krate) else {
@@ -99,7 +99,7 @@
_: &'db dyn HirDatabase,
_def: AdtId,
_args: GenericArgs<'db>,
- _trait_env: Arc<TraitEnvironment>,
+ _trait_env: Arc<TraitEnvironment<'db>>,
) -> Result<Arc<Layout>, LayoutError> {
Err(LayoutError::RecursiveTypeWithoutIndirection)
}
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
index 523ddad..275ad84 100644
--- a/crates/hir-ty/src/layout/tests.rs
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -1,18 +1,17 @@
use base_db::target::TargetData;
-use chalk_ir::{AdtId, TyKind};
use either::Either;
use hir_def::db::DefDatabase;
use project_model::{Sysroot, toolchain_info::QueryConfig};
use rustc_hash::FxHashMap;
+use rustc_type_ir::inherent::{GenericArgs as _, Ty as _};
use syntax::ToSmolStr;
use test_fixture::WithFixture;
use triomphe::Arc;
use crate::{
- Interner, Substitution,
db::HirDatabase,
layout::{Layout, LayoutError},
- next_solver::{DbInterner, mapping::ChalkToNextSolver},
+ next_solver::{AdtDef, DbInterner, GenericArgs, mapping::ChalkToNextSolver},
setup_tracing,
test_db::TestDB,
};
@@ -80,18 +79,18 @@
Some(adt_or_type_alias_id)
})
.unwrap();
- let goal_ty = match adt_or_type_alias_id {
- Either::Left(adt_id) => {
- TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner)
- }
- Either::Right(ty_id) => {
- db.ty(ty_id.into()).substitute(Interner, &Substitution::empty(Interner))
- }
- };
salsa::attach(&db, || {
let interner = DbInterner::new_with(&db, None, None);
+ let goal_ty = match adt_or_type_alias_id {
+ Either::Left(adt_id) => crate::next_solver::Ty::new_adt(
+ interner,
+ AdtDef::new(adt_id, interner),
+ GenericArgs::identity_for_item(interner, adt_id.into()),
+ ),
+ Either::Right(ty_id) => db.ty(ty_id.into()).instantiate_identity(),
+ };
db.layout_of_ty(
- goal_ty.to_nextsolver(interner),
+ goal_ty,
db.trait_environment(match adt_or_type_alias_id {
Either::Left(adt) => hir_def::GenericDefId::AdtId(adt),
Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty),
@@ -150,7 +149,7 @@
) {
let l = eval_goal(ra_fixture, minicore).unwrap();
assert_eq!(l.size.bytes(), size, "size mismatch");
- assert_eq!(l.align.abi.bytes(), align, "align mismatch");
+ assert_eq!(l.align.bytes(), align, "align mismatch");
}
#[track_caller]
@@ -162,7 +161,7 @@
) {
let l = eval_expr(ra_fixture, minicore).unwrap();
assert_eq!(l.size.bytes(), size, "size mismatch");
- assert_eq!(l.align.abi.bytes(), align, "align mismatch");
+ assert_eq!(l.align.bytes(), align, "align mismatch");
}
#[track_caller]
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 451622e..281cf6b 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -3,45 +3,24 @@
#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_index;
+// FIXME: We used to import `rustc_*` deps from `rustc_private` with `feature = "in-rust-tree" but
+// temporarily switched to crates.io versions due to hardships that working on them from rustc
+// demands corresponding changes on rust-analyzer at the same time.
+// For details, see the zulip discussion below:
+// https://rust-lang.zulipchat.com/#narrow/channel/185405-t-compiler.2Frust-analyzer/topic/relying.20on.20in-tree.20.60rustc_type_ir.60.2F.60rustc_next_trait_solver.60/with/541055689
-#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_index as rustc_index;
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_abi;
-
-#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_abi as rustc_abi;
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_pattern_analysis;
-
-#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_ast_ir;
-
-#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_ast_ir as rustc_ast_ir;
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_type_ir;
-
-#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_type_ir as rustc_type_ir;
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_next_trait_solver;
-
-#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver;
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_data_structures as ena;
-
mod builder;
mod chalk_db;
mod chalk_ext;
@@ -93,7 +72,10 @@
use la_arena::{Arena, Idx};
use mir::{MirEvalError, VTableMap};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
-use rustc_type_ir::inherent::SliceLike;
+use rustc_type_ir::{
+ UpcastFrom,
+ inherent::{SliceLike, Ty as _},
+};
use syntax::ast::{ConstArg, make};
use traits::FnTrait;
use triomphe::Arc;
@@ -106,7 +88,7 @@
infer::unify::InferenceTable,
next_solver::{
DbInterner,
- mapping::{ChalkToNextSolver, convert_ty_for_result},
+ mapping::{ChalkToNextSolver, NextSolverToChalk, convert_ty_for_result},
},
};
@@ -575,8 +557,10 @@
pub fn from_def(db: &dyn HirDatabase, def: FnDefId, substs: &Substitution) -> CallableSig {
let callable_def = ToChalk::from_chalk(db, def);
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
let sig = db.callable_item_signature(callable_def);
- sig.substitute(Interner, substs)
+ sig.instantiate(interner, args).skip_binder().to_chalk(interner)
}
pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig {
CallableSig {
@@ -937,10 +921,10 @@
Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) }
}
-pub fn callable_sig_from_fn_trait(
+pub fn callable_sig_from_fn_trait<'db>(
self_ty: &Ty,
- trait_env: Arc<TraitEnvironment>,
- db: &dyn HirDatabase,
+ trait_env: Arc<TraitEnvironment<'db>>,
+ db: &'db dyn HirDatabase,
) -> Option<(FnTrait, CallableSig)> {
let krate = trait_env.krate;
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
@@ -957,26 +941,32 @@
// Register two obligations:
// - Self: FnOnce<?args_ty>
// - <Self as FnOnce<?args_ty>>::Output == ?ret_ty
- let args_ty = table.new_type_var();
- let mut trait_ref = b.push(self_ty.clone()).push(args_ty.clone()).build();
- let projection = TyBuilder::assoc_type_projection(
- db,
- output_assoc_type,
- Some(trait_ref.substitution.clone()),
- )
- .build();
+ let args_ty = table.next_ty_var();
+ let args = [self_ty.to_nextsolver(table.interner), args_ty];
+ let trait_ref = crate::next_solver::TraitRef::new(table.interner, fn_once_trait.into(), args);
+ let projection = crate::next_solver::Ty::new_alias(
+ table.interner,
+ rustc_type_ir::AliasTyKind::Projection,
+ crate::next_solver::AliasTy::new(table.interner, output_assoc_type.into(), args),
+ );
- let goal: Goal = trait_ref.clone().cast(Interner);
- let pred = goal.to_nextsolver(table.interner);
- if !table.try_obligation(goal).no_solution() {
+ let pred = crate::next_solver::Predicate::upcast_from(trait_ref, table.interner);
+ if !table.try_obligation(pred).no_solution() {
table.register_obligation(pred);
- let return_ty = table.normalize_projection_ty(projection);
+ let return_ty = table.normalize_alias_ty(projection);
for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
let fn_x_trait = fn_x.get_id(db, krate)?;
- trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
- if !table.try_obligation(trait_ref.clone().cast(Interner)).no_solution() {
- let ret_ty = table.resolve_completely(return_ty);
- let args_ty = table.resolve_completely(args_ty);
+ let trait_ref =
+ crate::next_solver::TraitRef::new(table.interner, fn_x_trait.into(), args);
+ if !table
+ .try_obligation(crate::next_solver::Predicate::upcast_from(
+ trait_ref,
+ table.interner,
+ ))
+ .no_solution()
+ {
+ let ret_ty = table.resolve_completely(return_ty.to_chalk(table.interner));
+ let args_ty = table.resolve_completely(args_ty.to_chalk(table.interner));
let params = args_ty
.as_tuple()?
.iter(Interner)
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 4d5172f..0c197b2 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -24,19 +24,18 @@
use either::Either;
use hir_def::{
- AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
- FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LocalFieldId,
- Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId,
+ AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId,
+ GenericParamId, ItemContainerId, LocalFieldId, Lookup, StaticId, StructId, TypeAliasId,
+ TypeOrConstParamId, UnionId, VariantId,
builtin_type::BuiltinType,
expr_store::{ExpressionStore, path::Path},
hir::generics::{GenericParamDataRef, TypeOrConstParamData, WherePredicate},
- item_tree::FieldsShape,
lang_item::LangItem,
resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
- signatures::{FunctionSignature, TraitFlags, TypeAliasFlags},
+ signatures::{FunctionSignature, TraitFlags},
type_ref::{
- ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier,
- TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId,
+ ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, TypeBound, TypeRef,
+ TypeRefId,
},
};
use hir_expand::name::Name;
@@ -46,11 +45,10 @@
use triomphe::{Arc, ThinArc};
use crate::{
- AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DomainGoal, DynTy, FnAbi,
- FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime,
- LifetimeData, LifetimeOutlives, PolyFnSig, QuantifiedWhereClause, QuantifiedWhereClauses,
- Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
- all_super_traits,
+ AliasTy, Binders, BoundVar, Const, DebruijnIndex, DynTy, FnAbi, FnPointer, FnSig, FnSubst,
+ ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, LifetimeData, LifetimeOutlives,
+ QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitRef, TraitRefExt, Ty,
+ TyBuilder, TyKind, WhereClause, all_super_traits,
consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic},
db::HirDatabase,
error_lifetime,
@@ -60,7 +58,11 @@
path::{PathDiagnosticCallback, PathLoweringContext},
},
make_binders,
- mapping::{ToChalk, from_chalk_trait_id, lt_to_placeholder_idx},
+ mapping::{from_chalk_trait_id, lt_to_placeholder_idx},
+ next_solver::{
+ DbInterner,
+ mapping::{ChalkToNextSolver, NextSolverToChalk},
+ },
static_lifetime, to_chalk_trait_id, to_placeholder_idx,
utils::all_super_trait_refs,
variable_kinds_from_iter,
@@ -567,14 +569,6 @@
Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty, false), ctx))
}
- fn lower_trait_ref(
- &mut self,
- trait_ref: &HirTraitRef,
- explicit_self_ty: Ty,
- ) -> Option<TraitRef> {
- self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0)
- }
-
/// When lowering predicates from parents (impl, traits) for children defs (fns, consts, types), `generics` should
/// contain the `Generics` for the **child**, while `predicate_owner` should contain the `GenericDefId` of the
/// **parent**. This is important so we generate the correct bound var/placeholder.
@@ -826,15 +820,6 @@
}
}
-/// Build the signature of a callable item (function, struct or enum variant).
-pub(crate) fn callable_item_signature_query(db: &dyn HirDatabase, def: CallableDefId) -> PolyFnSig {
- match def {
- CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f),
- CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s),
- CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e),
- }
-}
-
fn named_associated_type_shorthand_candidates<R>(
db: &dyn HirDatabase,
// If the type parameter is defined in an impl and we're in a method, there
@@ -862,21 +847,21 @@
})
};
+ let interner = DbInterner::new_with(db, None, None);
match res {
TypeNs::SelfType(impl_id) => {
- // we're _in_ the impl -- the binders get added back later. Correct,
- // but it would be nice to make this more explicit
- let trait_ref = db.impl_trait(impl_id)?.into_value_and_skipped_binders().0;
+ let trait_ref = db.impl_trait(impl_id)?;
let impl_id_as_generic_def: GenericDefId = impl_id.into();
if impl_id_as_generic_def != def {
let subst = TyBuilder::subst_for_def(db, impl_id, None)
.fill_with_bound_vars(DebruijnIndex::INNERMOST, 0)
.build();
- let trait_ref = subst.apply(trait_ref, Interner);
+ let args: crate::next_solver::GenericArgs<'_> = subst.to_nextsolver(interner);
+ let trait_ref = trait_ref.instantiate(interner, args).to_chalk(interner);
search(trait_ref)
} else {
- search(trait_ref)
+ search(trait_ref.skip_binder().to_chalk(interner))
}
}
TypeNs::GenericParam(param_id) => {
@@ -919,7 +904,7 @@
db: &dyn HirDatabase,
variant_id: VariantId,
) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>> {
- db.field_types_with_diagnostics(variant_id).0
+ field_types_with_diagnostics_query(db, variant_id).0
}
/// Build the type of all specific fields of a struct or enum variant.
@@ -1086,102 +1071,6 @@
GenericPredicates(None)
}
-pub(crate) fn trait_environment_for_body_query(
- db: &dyn HirDatabase,
- def: DefWithBodyId,
-) -> Arc<TraitEnvironment> {
- let Some(def) = def.as_generic_def_id(db) else {
- let krate = def.module(db).krate();
- return TraitEnvironment::empty(krate);
- };
- db.trait_environment(def)
-}
-
-pub(crate) fn trait_environment_query(
- db: &dyn HirDatabase,
- def: GenericDefId,
-) -> Arc<TraitEnvironment> {
- let generics = generics(db, def);
- if generics.has_no_predicates() && generics.is_empty() {
- return TraitEnvironment::empty(def.krate(db));
- }
-
- let resolver = def.resolver(db);
- let mut ctx = TyLoweringContext::new(
- db,
- &resolver,
- generics.store(),
- def,
- LifetimeElisionKind::AnonymousReportError,
- )
- .with_type_param_mode(ParamLoweringMode::Placeholder);
- let mut traits_in_scope = Vec::new();
- let mut clauses = Vec::new();
- for maybe_parent_generics in
- std::iter::successors(Some(&generics), |generics| generics.parent_generics())
- {
- ctx.store = maybe_parent_generics.store();
- for pred in maybe_parent_generics.where_predicates() {
- for pred in ctx.lower_where_predicate(pred, false) {
- if let WhereClause::Implemented(tr) = pred.skip_binders() {
- traits_in_scope
- .push((tr.self_type_parameter(Interner).clone(), tr.hir_trait_id()));
- }
- let program_clause: Binders<DomainGoal> =
- pred.map(|pred| pred.into_from_env_goal(Interner).cast(Interner));
- clauses.push(program_clause);
- }
- }
- }
-
- if let Some(trait_id) = def.assoc_trait_container(db) {
- // add `Self: Trait<T1, T2, ...>` to the environment in trait
- // function default implementations (and speculative code
- // inside consts or type aliases)
- cov_mark::hit!(trait_self_implements_self);
- let substs = TyBuilder::placeholder_subst(db, trait_id);
- let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs };
- let pred = WhereClause::Implemented(trait_ref);
- clauses.push(Binders::empty(
- Interner,
- pred.cast::<DomainGoal>(Interner).into_from_env_goal(Interner),
- ));
- }
-
- let subst = generics.placeholder_subst(db);
- if !subst.is_empty(Interner) {
- let explicitly_unsized_tys = ctx.unsized_types;
- if let Some(implicitly_sized_clauses) =
- implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver)
- {
- clauses.extend(implicitly_sized_clauses.map(|pred| {
- Binders::empty(
- Interner,
- pred.into_from_env_goal(Interner).cast::<DomainGoal>(Interner),
- )
- }));
- };
- }
-
- let clauses = chalk_ir::ProgramClauses::from_iter(
- Interner,
- clauses.into_iter().map(|g| {
- chalk_ir::ProgramClause::new(
- Interner,
- chalk_ir::ProgramClauseData(g.map(|g| chalk_ir::ProgramClauseImplication {
- consequence: g,
- conditions: chalk_ir::Goals::empty(Interner),
- constraints: chalk_ir::Constraints::empty(Interner),
- priority: chalk_ir::ClausePriority::High,
- })),
- )
- }),
- );
- let env = chalk_ir::Environment { clauses };
-
- TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env)
-}
-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GenericPredicates(Option<Arc<[Binders<QuantifiedWhereClause>]>>);
@@ -1410,208 +1299,6 @@
(GenericDefaults(None), None)
}
-fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
- let data = db.function_signature(def);
- let resolver = def.resolver(db);
- let mut ctx_params = TyLoweringContext::new(
- db,
- &resolver,
- &data.store,
- def.into(),
- LifetimeElisionKind::for_fn_params(&data),
- )
- .with_type_param_mode(ParamLoweringMode::Variable);
- let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr));
-
- let ret = match data.ret_type {
- Some(ret_type) => {
- let mut ctx_ret = TyLoweringContext::new(
- db,
- &resolver,
- &data.store,
- def.into(),
- LifetimeElisionKind::for_fn_ret(),
- )
- .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
- .with_type_param_mode(ParamLoweringMode::Variable);
- ctx_ret.lower_ty(ret_type)
- }
- None => TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner),
- };
- let generics = generics(db, def.into());
- let sig = CallableSig::from_params_and_return(
- params,
- ret,
- data.is_varargs(),
- if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe },
- data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol),
- );
- make_binders(db, &generics, sig)
-}
-
-/// Build the declared type of a function. This should not need to look at the
-/// function body.
-fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> {
- let generics = generics(db, def.into());
- let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
- make_binders(
- db,
- &generics,
- TyKind::FnDef(CallableDefId::FunctionId(def).to_chalk(db), substs).intern(Interner),
- )
-}
-
-/// Build the declared type of a const.
-fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
- let data = db.const_signature(def);
- let generics = generics(db, def.into());
- let resolver = def.resolver(db);
- let parent = def.loc(db).container;
- let mut ctx = TyLoweringContext::new(
- db,
- &resolver,
- &data.store,
- def.into(),
- LifetimeElisionKind::for_const(parent),
- )
- .with_type_param_mode(ParamLoweringMode::Variable);
-
- make_binders(db, &generics, ctx.lower_ty(data.type_ref))
-}
-
-/// Build the declared type of a static.
-fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders<Ty> {
- let data = db.static_signature(def);
- let resolver = def.resolver(db);
- let mut ctx = TyLoweringContext::new(
- db,
- &resolver,
- &data.store,
- def.into(),
- LifetimeElisionKind::Elided(static_lifetime()),
- );
-
- Binders::empty(Interner, ctx.lower_ty(data.type_ref))
-}
-
-fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnSig {
- let field_tys = db.field_types(def.into());
- let params = field_tys.iter().map(|(_, ty)| ty.skip_binders().clone());
- let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
- Binders::new(
- binders,
- CallableSig::from_params_and_return(params, ret, false, Safety::Safe, FnAbi::RustCall),
- )
-}
-
-/// Build the type of a tuple struct constructor.
-fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Option<Binders<Ty>> {
- let struct_data = def.fields(db);
- match struct_data.shape {
- FieldsShape::Record => None,
- FieldsShape::Unit => Some(type_for_adt(db, def.into())),
- FieldsShape::Tuple => {
- let generics = generics(db, AdtId::from(def).into());
- let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
- Some(make_binders(
- db,
- &generics,
- TyKind::FnDef(CallableDefId::StructId(def).to_chalk(db), substs).intern(Interner),
- ))
- }
- }
-}
-
-fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -> PolyFnSig {
- let field_tys = db.field_types(def.into());
- let params = field_tys.iter().map(|(_, ty)| ty.skip_binders().clone());
- let parent = def.lookup(db).parent;
- let (ret, binders) = type_for_adt(db, parent.into()).into_value_and_skipped_binders();
- Binders::new(
- binders,
- CallableSig::from_params_and_return(params, ret, false, Safety::Safe, FnAbi::RustCall),
- )
-}
-
-/// Build the type of a tuple enum variant constructor.
-fn type_for_enum_variant_constructor(
- db: &dyn HirDatabase,
- def: EnumVariantId,
-) -> Option<Binders<Ty>> {
- let e = def.lookup(db).parent;
- match def.fields(db).shape {
- FieldsShape::Record => None,
- FieldsShape::Unit => Some(type_for_adt(db, e.into())),
- FieldsShape::Tuple => {
- let generics = generics(db, e.into());
- let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
- Some(make_binders(
- db,
- &generics,
- TyKind::FnDef(CallableDefId::EnumVariantId(def).to_chalk(db), substs)
- .intern(Interner),
- ))
- }
- }
-}
-
-#[salsa_macros::tracked(cycle_result = type_for_adt_cycle_result)]
-fn type_for_adt_tracked(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
- type_for_adt(db, adt)
-}
-
-fn type_for_adt_cycle_result(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
- let generics = generics(db, adt.into());
- make_binders(db, &generics, TyKind::Error.intern(Interner))
-}
-
-fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
- let generics = generics(db, adt.into());
- let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
- let ty = TyKind::Adt(crate::AdtId(adt), subst).intern(Interner);
- make_binders(db, &generics, ty)
-}
-
-pub(crate) fn type_for_type_alias_with_diagnostics_query(
- db: &dyn HirDatabase,
- t: TypeAliasId,
-) -> (Binders<Ty>, Diagnostics) {
- let generics = generics(db, t.into());
- let type_alias_data = db.type_alias_signature(t);
- let mut diags = None;
- let inner = if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) {
- TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner)
- } else {
- let resolver = t.resolver(db);
- let alias = db.type_alias_signature(t);
- let mut ctx = TyLoweringContext::new(
- db,
- &resolver,
- &alias.store,
- t.into(),
- LifetimeElisionKind::AnonymousReportError,
- )
- .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
- .with_type_param_mode(ParamLoweringMode::Variable);
- let res = alias
- .ty
- .map(|type_ref| ctx.lower_ty(type_ref))
- .unwrap_or_else(|| TyKind::Error.intern(Interner));
- diags = create_diagnostics(ctx.diagnostics);
- res
- };
-
- (make_binders(db, &generics, inner), diags)
-}
-
-pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result(
- db: &dyn HirDatabase,
- adt: TypeAliasId,
-) -> (Binders<Ty>, Diagnostics) {
- let generics = generics(db, adt.into());
- (make_binders(db, &generics, TyKind::Error.intern(Interner)), None)
-}
-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TyDefId {
BuiltinType(BuiltinType),
@@ -1644,64 +1331,8 @@
}
}
-/// Build the declared type of an item. This depends on the namespace; e.g. for
-/// `struct Foo(usize)`, we have two types: The type of the struct itself, and
-/// the constructor function `(usize) -> Foo` which lives in the values
-/// namespace.
-pub(crate) fn ty_query(db: &dyn HirDatabase, def: TyDefId) -> Binders<Ty> {
- match def {
- TyDefId::BuiltinType(it) => Binders::empty(Interner, TyBuilder::builtin(it)),
- TyDefId::AdtId(it) => type_for_adt_tracked(db, it),
- TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).0,
- }
-}
-
-pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Option<Binders<Ty>> {
- match def {
- ValueTyDefId::FunctionId(it) => Some(type_for_fn(db, it)),
- ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it),
- ValueTyDefId::UnionId(it) => Some(type_for_adt(db, it.into())),
- ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it),
- ValueTyDefId::ConstId(it) => Some(type_for_const(db, it)),
- ValueTyDefId::StaticId(it) => Some(type_for_static(db, it)),
- }
-}
-
-pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binders<Ty> {
- db.impl_self_ty_with_diagnostics(impl_id).0
-}
-
-pub(crate) fn impl_self_ty_with_diagnostics_query(
- db: &dyn HirDatabase,
- impl_id: ImplId,
-) -> (Binders<Ty>, Diagnostics) {
- let impl_data = db.impl_signature(impl_id);
- let resolver = impl_id.resolver(db);
- let generics = generics(db, impl_id.into());
- let mut ctx = TyLoweringContext::new(
- db,
- &resolver,
- &impl_data.store,
- impl_id.into(),
- LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true },
- )
- .with_type_param_mode(ParamLoweringMode::Variable);
- (
- make_binders(db, &generics, ctx.lower_ty(impl_data.self_ty)),
- create_diagnostics(ctx.diagnostics),
- )
-}
-
-pub(crate) fn impl_self_ty_with_diagnostics_cycle_result(
- db: &dyn HirDatabase,
- impl_id: ImplId,
-) -> (Binders<Ty>, Diagnostics) {
- let generics = generics(db, impl_id.into());
- (make_binders(db, &generics, TyKind::Error.intern(Interner)), None)
-}
-
pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
- db.const_param_ty_with_diagnostics(def).0
+ const_param_ty_with_diagnostics_query(db, def).0
}
// returns None if def is a type arg
@@ -1729,36 +1360,12 @@
(ty, create_diagnostics(ctx.diagnostics))
}
-pub(crate) fn const_param_ty_with_diagnostics_cycle_result(
+pub(crate) fn const_param_ty_cycle_result(
_: &dyn HirDatabase,
_: crate::db::HirDatabaseData,
_: ConstParamId,
-) -> (Ty, Diagnostics) {
- (TyKind::Error.intern(Interner), None)
-}
-
-pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> {
- db.impl_trait_with_diagnostics(impl_id).map(|it| it.0)
-}
-
-pub(crate) fn impl_trait_with_diagnostics_query(
- db: &dyn HirDatabase,
- impl_id: ImplId,
-) -> Option<(Binders<TraitRef>, Diagnostics)> {
- let impl_data = db.impl_signature(impl_id);
- let resolver = impl_id.resolver(db);
- let mut ctx = TyLoweringContext::new(
- db,
- &resolver,
- &impl_data.store,
- impl_id.into(),
- LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true },
- )
- .with_type_param_mode(ParamLoweringMode::Variable);
- let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders();
- let target_trait = impl_data.target_trait.as_ref()?;
- let trait_ref = Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?);
- Some((trait_ref, create_diagnostics(ctx.diagnostics)))
+) -> Ty {
+ TyKind::Error.intern(Interner)
}
pub(crate) fn return_type_impl_traits(
diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs
index b0132e4..da9dd21 100644
--- a/crates/hir-ty/src/lower/path.rs
+++ b/crates/hir-ty/src/lower/path.rs
@@ -28,6 +28,10 @@
error_lifetime,
generics::{Generics, generics},
lower::{LifetimeElisionKind, named_associated_type_shorthand_candidates},
+ next_solver::{
+ DbInterner,
+ mapping::{ChalkToNextSolver, NextSolverToChalk},
+ },
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
utils::associated_type_by_name_including_super_traits,
};
@@ -251,12 +255,20 @@
// `def` can be either impl itself or item within, and we need impl itself
// now.
let generics = generics.parent_or_self();
+ let interner = DbInterner::new_with(self.ctx.db, None, None);
let subst = generics.placeholder_subst(self.ctx.db);
- self.ctx.db.impl_self_ty(impl_id).substitute(Interner, &subst)
+ let args: crate::next_solver::GenericArgs<'_> =
+ subst.to_nextsolver(interner);
+ self.ctx
+ .db
+ .impl_self_ty(impl_id)
+ .instantiate(interner, args)
+ .to_chalk(interner)
}
ParamLoweringMode::Variable => TyBuilder::impl_self_ty(self.ctx.db, impl_id)
.fill_with_bound_vars(self.ctx.in_binders, 0)
- .build(),
+ .build(DbInterner::conjure())
+ .to_chalk(DbInterner::conjure()),
}
}
TypeNs::AdtSelfType(adt) => {
@@ -267,7 +279,9 @@
generics.bound_vars_subst(self.ctx.db, self.ctx.in_binders)
}
};
- self.ctx.db.ty(adt.into()).substitute(Interner, &substs)
+ let interner = DbInterner::conjure();
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ self.ctx.db.ty(adt.into()).instantiate(interner, args).to_chalk(interner)
}
TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args),
@@ -537,7 +551,9 @@
TyDefId::TypeAliasId(it) => it.into(),
};
let substs = self.substs_from_path_segment(generic_def, infer_args, None, false);
- self.ctx.db.ty(typeable).substitute(Interner, &substs)
+ let interner = DbInterner::conjure();
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ self.ctx.db.ty(typeable).instantiate(interner, args).to_chalk(interner)
}
/// Collect generic arguments from a path into a `Substs`. See also
@@ -603,7 +619,7 @@
explicit_self_ty: Option<Ty>,
lowering_assoc_type_generics: bool,
) -> Substitution {
- let mut lifetime_elision = self.ctx.lifetime_elision.clone();
+ let old_lifetime_elision = self.ctx.lifetime_elision.clone();
if let Some(args) = self.current_or_prev_segment.args_and_bindings
&& args.parenthesized != GenericArgsParentheses::No
@@ -633,19 +649,21 @@
}
// `Fn()`-style generics are treated like functions for the purpose of lifetime elision.
- lifetime_elision =
+ self.ctx.lifetime_elision =
LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false };
}
- self.substs_from_args_and_bindings(
+ let result = self.substs_from_args_and_bindings(
self.current_or_prev_segment.args_and_bindings,
def,
infer_args,
explicit_self_ty,
PathGenericsSource::Segment(self.current_segment_u32()),
lowering_assoc_type_generics,
- lifetime_elision,
- )
+ self.ctx.lifetime_elision.clone(),
+ );
+ self.ctx.lifetime_elision = old_lifetime_elision;
+ result
}
pub(super) fn substs_from_args_and_bindings(
diff --git a/crates/hir-ty/src/lower_nextsolver.rs b/crates/hir-ty/src/lower_nextsolver.rs
index 0076446..84cd216 100644
--- a/crates/hir-ty/src/lower_nextsolver.rs
+++ b/crates/hir-ty/src/lower_nextsolver.rs
@@ -19,9 +19,9 @@
use either::Either;
use hir_def::item_tree::FieldsShape;
use hir_def::{
- AdtId, AssocItemId, CallableDefId, ConstParamId, EnumVariantId, FunctionId, GenericDefId,
- GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TraitId, TypeAliasId,
- TypeOrConstParamId, VariantId,
+ AdtId, AssocItemId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId,
+ GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup,
+ StructId, TraitId, TypeAliasId, TypeOrConstParamId, VariantId,
expr_store::{
ExpressionStore,
path::{GenericArg, Path},
@@ -57,7 +57,7 @@
use crate::ValueTyDefId;
use crate::{
- FnAbi, ImplTraitId, Interner, ParamKind, TyDefId, TyLoweringDiagnostic,
+ FnAbi, ImplTraitId, Interner, ParamKind, TraitEnvironment, TyDefId, TyLoweringDiagnostic,
TyLoweringDiagnosticKind,
consteval_nextsolver::{intern_const_ref, path_to_const, unknown_const_as_generic},
db::HirDatabase,
@@ -66,8 +66,10 @@
next_solver::{
AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind,
BoundVarKind, BoundVarKinds, Clause, Clauses, Const, DbInterner, EarlyBinder,
- EarlyParamRegion, ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId,
- TraitPredicate, TraitRef, Ty, Tys, abi::Safety, mapping::ChalkToNextSolver,
+ EarlyParamRegion, ErrorGuaranteed, GenericArgs, ParamEnv, PolyFnSig, Predicate, Region,
+ SolverDefId, TraitPredicate, TraitRef, Ty, Tys,
+ abi::Safety,
+ mapping::{ChalkToNextSolver, convert_ty_for_result},
},
};
@@ -902,7 +904,7 @@
db: &'db dyn HirDatabase,
impl_id: ImplId,
) -> Option<EarlyBinder<'db, TraitRef<'db>>> {
- db.impl_trait_with_diagnostics_ns(impl_id).map(|it| it.0)
+ db.impl_trait_with_diagnostics(impl_id).map(|it| it.0)
}
pub(crate) fn impl_trait_with_diagnostics_query<'db>(
@@ -918,7 +920,7 @@
impl_id.into(),
LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true },
);
- let self_ty = db.impl_self_ty_ns(impl_id).skip_binder();
+ let self_ty = db.impl_self_ty(impl_id).skip_binder();
let target_trait = impl_data.target_trait.as_ref()?;
let trait_ref = EarlyBinder::bind(ctx.lower_trait_ref(target_trait, self_ty)?);
Some((trait_ref, create_diagnostics(ctx.diagnostics)))
@@ -984,7 +986,7 @@
AdtDef::new(it, interner),
GenericArgs::identity_for_item(interner, it.into()),
)),
- TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics_ns(it).0,
+ TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).0,
}
}
@@ -1129,7 +1131,7 @@
db: &'db dyn HirDatabase,
impl_id: ImplId,
) -> EarlyBinder<'db, Ty<'db>> {
- db.impl_self_ty_with_diagnostics_ns(impl_id).0
+ db.impl_self_ty_with_diagnostics(impl_id).0
}
pub(crate) fn impl_self_ty_with_diagnostics_query<'db>(
@@ -1160,7 +1162,7 @@
}
pub(crate) fn const_param_ty_query<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> {
- db.const_param_ty_with_diagnostics_ns(def).0
+ db.const_param_ty_with_diagnostics(def).0
}
// returns None if def is a type arg
@@ -1189,11 +1191,21 @@
(ty, create_diagnostics(ctx.diagnostics))
}
+pub(crate) fn const_param_ty_with_diagnostics_cycle_result<'db>(
+ db: &'db dyn HirDatabase,
+ _: crate::db::HirDatabaseData,
+ def: ConstParamId,
+) -> (Ty<'db>, Diagnostics) {
+ let resolver = def.parent().resolver(db);
+ let interner = DbInterner::new_with(db, Some(resolver.krate()), None);
+ (Ty::new_error(interner, ErrorGuaranteed), None)
+}
+
pub(crate) fn field_types_query<'db>(
db: &'db dyn HirDatabase,
variant_id: VariantId,
) -> Arc<ArenaMap<LocalFieldId, EarlyBinder<'db, Ty<'db>>>> {
- db.field_types_with_diagnostics_ns(variant_id).0
+ db.field_types_with_diagnostics(variant_id).0
}
/// Build the type of all specific fields of a struct or enum variant.
@@ -1355,6 +1367,18 @@
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GenericPredicates<'db>(Option<Arc<[Clause<'db>]>>);
+impl<'db> GenericPredicates<'db> {
+ pub fn instantiate(
+ &self,
+ interner: DbInterner<'db>,
+ args: GenericArgs<'db>,
+ ) -> Option<impl Iterator<Item = Clause<'db>>> {
+ self.0
+ .as_ref()
+ .map(|it| EarlyBinder::bind(it.iter().copied()).iter_instantiated(interner, args))
+ }
+}
+
impl<'db> ops::Deref for GenericPredicates<'db> {
type Target = [Clause<'db>];
@@ -1363,6 +1387,122 @@
}
}
+pub(crate) fn trait_environment_for_body_query(
+ db: &dyn HirDatabase,
+ def: DefWithBodyId,
+) -> Arc<TraitEnvironment<'_>> {
+ let Some(def) = def.as_generic_def_id(db) else {
+ let krate = def.module(db).krate();
+ return TraitEnvironment::empty(krate);
+ };
+ db.trait_environment(def)
+}
+
+pub(crate) fn trait_environment_query<'db>(
+ db: &'db dyn HirDatabase,
+ def: GenericDefId,
+) -> Arc<TraitEnvironment<'db>> {
+ let generics = generics(db, def);
+ if generics.has_no_predicates() && generics.is_empty() {
+ return TraitEnvironment::empty(def.krate(db));
+ }
+
+ let interner = DbInterner::new_with(db, Some(def.krate(db)), None);
+ let resolver = def.resolver(db);
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ generics.store(),
+ def,
+ LifetimeElisionKind::AnonymousReportError,
+ );
+ let mut traits_in_scope = Vec::new();
+ let mut clauses = Vec::new();
+ for maybe_parent_generics in
+ std::iter::successors(Some(&generics), |generics| generics.parent_generics())
+ {
+ ctx.store = maybe_parent_generics.store();
+ for pred in maybe_parent_generics.where_predicates() {
+ for pred in ctx.lower_where_predicate(pred, false, &generics, PredicateFilter::All) {
+ if let rustc_type_ir::ClauseKind::Trait(tr) = pred.kind().skip_binder() {
+ traits_in_scope
+ .push((convert_ty_for_result(interner, tr.self_ty()), tr.def_id().0));
+ }
+ clauses.push(pred);
+ }
+ }
+ }
+
+ if let Some(trait_id) = def.assoc_trait_container(db) {
+ // add `Self: Trait<T1, T2, ...>` to the environment in trait
+ // function default implementations (and speculative code
+ // inside consts or type aliases)
+ cov_mark::hit!(trait_self_implements_self);
+ let trait_ref = TraitRef::identity(ctx.interner, trait_id.into());
+ let clause = Clause(Predicate::new(
+ ctx.interner,
+ Binder::dummy(rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait(
+ TraitPredicate { trait_ref, polarity: rustc_type_ir::PredicatePolarity::Positive },
+ ))),
+ ));
+ clauses.push(clause);
+ }
+
+ let explicitly_unsized_tys = ctx.unsized_types;
+
+ let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate());
+ if let Some(sized_trait) = sized_trait {
+ let (mut generics, mut def_id) =
+ (crate::next_solver::generics::generics(db, def.into()), def);
+ loop {
+ let self_idx = trait_self_param_idx(db, def_id);
+ for (idx, p) in generics.own_params.iter().enumerate() {
+ if let Some(self_idx) = self_idx
+ && p.index() as usize == self_idx
+ {
+ continue;
+ }
+ let GenericParamId::TypeParamId(param_id) = p.id else {
+ continue;
+ };
+ let idx = idx as u32 + generics.parent_count as u32;
+ let param_ty = Ty::new_param(ctx.interner, param_id, idx, p.name.clone());
+ if explicitly_unsized_tys.contains(¶m_ty) {
+ continue;
+ }
+ let trait_ref = TraitRef::new_from_args(
+ ctx.interner,
+ sized_trait.into(),
+ GenericArgs::new_from_iter(ctx.interner, [param_ty.into()]),
+ );
+ let clause = Clause(Predicate::new(
+ ctx.interner,
+ Binder::dummy(rustc_type_ir::PredicateKind::Clause(
+ rustc_type_ir::ClauseKind::Trait(TraitPredicate {
+ trait_ref,
+ polarity: rustc_type_ir::PredicatePolarity::Positive,
+ }),
+ )),
+ ));
+ clauses.push(clause);
+ }
+
+ if let Some(g) = generics.parent {
+ generics = crate::next_solver::generics::generics(db, g.into());
+ def_id = g;
+ } else {
+ break;
+ }
+ }
+ }
+
+ let clauses = rustc_type_ir::elaborate::elaborate(ctx.interner, clauses);
+ let clauses = Clauses::new_from_iter(ctx.interner, clauses);
+ let env = ParamEnv { clauses };
+
+ TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env)
+}
+
#[derive(Copy, Clone, Debug)]
pub(crate) enum PredicateFilter {
SelfTrait,
@@ -1830,7 +1970,8 @@
let mut search = |t: TraitRef<'db>| -> Option<R> {
let trait_id = t.def_id.0;
let mut checked_traits = FxHashSet::default();
- let mut check_trait = |trait_id: TraitId| {
+ let mut check_trait = |trait_ref: TraitRef<'db>| {
+ let trait_id = trait_ref.def_id.0;
let name = &db.trait_signature(trait_id).name;
tracing::debug!(?trait_id, ?name);
if !checked_traits.insert(trait_id) {
@@ -1841,37 +1982,39 @@
tracing::debug!(?data.items);
for (name, assoc_id) in &data.items {
if let &AssocItemId::TypeAliasId(alias) = assoc_id
- && let Some(ty) = check_alias(name, t, alias)
+ && let Some(ty) = check_alias(name, trait_ref, alias)
{
return Some(ty);
}
}
None
};
- let mut stack: SmallVec<[_; 4]> = smallvec![trait_id];
- while let Some(trait_def_id) = stack.pop() {
- if let Some(alias) = check_trait(trait_def_id) {
+ let mut stack: SmallVec<[_; 4]> = smallvec![t];
+ while let Some(trait_ref) = stack.pop() {
+ if let Some(alias) = check_trait(trait_ref) {
return Some(alias);
}
for pred in generic_predicates_filtered_by(
db,
- GenericDefId::TraitId(trait_def_id),
+ GenericDefId::TraitId(trait_ref.def_id.0),
PredicateFilter::SelfTrait,
// We are likely in the midst of lowering generic predicates of `def`.
// So, if we allow `pred == def` we might fall into an infinite recursion.
// Actually, we have already checked for the case `pred == def` above as we started
// with a stack including `trait_id`
- |pred| pred != def && pred == GenericDefId::TraitId(trait_def_id),
+ |pred| pred != def && pred == GenericDefId::TraitId(trait_ref.def_id.0),
)
.0
.deref()
{
tracing::debug!(?pred);
- let trait_id = match pred.kind().skip_binder() {
- rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(),
+ let sup_trait_ref = match pred.kind().skip_binder() {
+ rustc_type_ir::ClauseKind::Trait(pred) => pred.trait_ref,
_ => continue,
};
- stack.push(trait_id.0);
+ let sup_trait_ref =
+ EarlyBinder::bind(sup_trait_ref).instantiate(interner, trait_ref.args);
+ stack.push(sup_trait_ref);
}
tracing::debug!(?stack);
}
@@ -1881,7 +2024,7 @@
match res {
TypeNs::SelfType(impl_id) => {
- let trait_ref = db.impl_trait_ns(impl_id)?;
+ let trait_ref = db.impl_trait(impl_id)?;
// FIXME(next-solver): same method in `lower` checks for impl or not
// Is that needed here?
diff --git a/crates/hir-ty/src/lower_nextsolver/path.rs b/crates/hir-ty/src/lower_nextsolver/path.rs
index 7d67343..0a9f34c 100644
--- a/crates/hir-ty/src/lower_nextsolver/path.rs
+++ b/crates/hir-ty/src/lower_nextsolver/path.rs
@@ -287,7 +287,7 @@
}
}
}
- TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty_ns(impl_id).skip_binder(),
+ TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty(impl_id).skip_binder(),
TypeNs::AdtSelfType(adt) => {
let args = crate::next_solver::GenericArgs::identity_for_item(
self.ctx.interner,
@@ -616,7 +616,7 @@
explicit_self_ty: Option<Ty<'db>>,
lowering_assoc_type_generics: bool,
) -> crate::next_solver::GenericArgs<'db> {
- let mut lifetime_elision = self.ctx.lifetime_elision.clone();
+ let old_lifetime_elision = self.ctx.lifetime_elision.clone();
if let Some(args) = self.current_or_prev_segment.args_and_bindings
&& args.parenthesized != GenericArgsParentheses::No
@@ -646,19 +646,21 @@
}
// `Fn()`-style generics are treated like functions for the purpose of lifetime elision.
- lifetime_elision =
+ self.ctx.lifetime_elision =
LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false };
}
- self.substs_from_args_and_bindings(
+ let result = self.substs_from_args_and_bindings(
self.current_or_prev_segment.args_and_bindings,
def,
infer_args,
explicit_self_ty,
PathGenericsSource::Segment(self.current_segment_u32()),
lowering_assoc_type_generics,
- lifetime_elision,
- )
+ self.ctx.lifetime_elision.clone(),
+ );
+ self.ctx.lifetime_elision = old_lifetime_elision;
+ result
}
pub(super) fn substs_from_args_and_bindings(
@@ -915,22 +917,36 @@
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
);
if let Some(type_ref) = binding.type_ref {
- match (&self.ctx.store[type_ref], self.ctx.impl_trait_mode.mode) {
- (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (),
- (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => {
- let ty = self.ctx.lower_ty(type_ref);
- let pred = Clause(Predicate::new(
- interner,
- Binder::dummy(rustc_type_ir::PredicateKind::Clause(
- rustc_type_ir::ClauseKind::Projection(ProjectionPredicate {
- projection_term,
- term: ty.into(),
- }),
- )),
- ));
- predicates.push(pred);
+ let lifetime_elision =
+ if args_and_bindings.parenthesized == GenericArgsParentheses::ParenSugar {
+ // `Fn()`-style generics are elided like functions. This is `Output` (we lower to it in hir-def).
+ LifetimeElisionKind::for_fn_ret(self.ctx.interner)
+ } else {
+ self.ctx.lifetime_elision.clone()
+ };
+ self.with_lifetime_elision(lifetime_elision, |this| {
+ match (&this.ctx.store[type_ref], this.ctx.impl_trait_mode.mode) {
+ (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (),
+ (
+ _,
+ ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque,
+ ) => {
+ let ty = this.ctx.lower_ty(type_ref);
+ let pred = Clause(Predicate::new(
+ interner,
+ Binder::dummy(rustc_type_ir::PredicateKind::Clause(
+ rustc_type_ir::ClauseKind::Projection(
+ ProjectionPredicate {
+ projection_term,
+ term: ty.into(),
+ },
+ ),
+ )),
+ ));
+ predicates.push(pred);
+ }
}
- }
+ })
}
for bound in binding.bounds.iter() {
predicates.extend(self.ctx.lower_type_bound(
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 7fa3d31..61d3091 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -31,10 +31,13 @@
infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable},
lang_items::is_box,
next_solver::{
- self, SolverDefId,
- fulfill::FulfillmentCtxt,
- infer::DefineOpaqueTypes,
+ self, DbInterner, SolverDefId,
+ infer::{
+ DefineOpaqueTypes,
+ traits::{ObligationCause, PredicateObligation},
+ },
mapping::{ChalkToNextSolver, NextSolverToChalk},
+ obligation_ctxt::ObligationCtxt,
},
primitive::{FloatTy, IntTy, UintTy},
to_chalk_trait_id,
@@ -294,11 +297,12 @@
continue;
}
let target_trait = match db.impl_trait(impl_id) {
- Some(tr) => tr.skip_binders().hir_trait_id(),
+ Some(tr) => tr.skip_binder().def_id.0,
None => continue,
};
- let self_ty = db.impl_self_ty(impl_id);
- let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders());
+ let interner = DbInterner::new_with(db, None, None);
+ let self_ty = db.impl_self_ty(impl_id).instantiate_identity().to_chalk(interner);
+ let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty);
map.entry(target_trait).or_default().entry(self_ty_fp).or_default().push(impl_id);
}
@@ -411,8 +415,8 @@
continue;
}
- let self_ty = db.impl_self_ty(impl_id);
- let self_ty = self_ty.skip_binders();
+ let interner = DbInterner::new_with(db, None, None);
+ let self_ty = &db.impl_self_ty(impl_id).instantiate_identity().to_chalk(interner);
match is_inherent_impl_coherent(db, def_map, impl_id, self_ty) {
true => {
@@ -542,7 +546,7 @@
pub(crate) fn lookup_method<'db>(
db: &'db dyn HirDatabase,
ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule,
name: &Name,
@@ -711,7 +715,7 @@
pub(crate) fn iterate_method_candidates<'db, T>(
ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
db: &'db dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule,
name: Option<&Name>,
@@ -739,9 +743,9 @@
slot
}
-pub fn lookup_impl_const(
- db: &dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+pub fn lookup_impl_const<'db>(
+ db: &'db dyn HirDatabase,
+ env: Arc<TraitEnvironment<'db>>,
const_id: ConstId,
subs: Substitution,
) -> (ConstId, Substitution) {
@@ -767,9 +771,9 @@
/// Checks if the self parameter of `Trait` method is the `dyn Trait` and we should
/// call the method using the vtable.
-pub fn is_dyn_method(
- db: &dyn HirDatabase,
- _env: Arc<TraitEnvironment>,
+pub fn is_dyn_method<'db>(
+ db: &'db dyn HirDatabase,
+ _env: Arc<TraitEnvironment<'db>>,
func: FunctionId,
fn_subst: Substitution,
) -> Option<usize> {
@@ -809,9 +813,9 @@
/// Looks up the impl method that actually runs for the trait method `func`.
///
/// Returns `func` if it's not a method defined in a trait or the lookup failed.
-pub(crate) fn lookup_impl_method_query(
- db: &dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+pub(crate) fn lookup_impl_method_query<'db>(
+ db: &'db dyn HirDatabase,
+ env: Arc<TraitEnvironment<'db>>,
func: FunctionId,
fn_subst: Substitution,
) -> (FunctionId, Substitution) {
@@ -842,10 +846,10 @@
)
}
-fn lookup_impl_assoc_item_for_trait_ref(
+fn lookup_impl_assoc_item_for_trait_ref<'db>(
trait_ref: TraitRef,
- db: &dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+ db: &'db dyn HirDatabase,
+ env: Arc<TraitEnvironment<'db>>,
name: &Name,
) -> Option<(AssocItemId, Substitution)> {
let hir_trait_id = trait_ref.hir_trait_id();
@@ -894,10 +898,13 @@
table.run_in_snapshot(|table| {
let impl_substs =
TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build();
+ let args: crate::next_solver::GenericArgs<'_> =
+ impl_substs.to_nextsolver(table.interner);
let trait_ref = db
.impl_trait(impl_)
.expect("non-trait method in find_matching_impl")
- .substitute(Interner, &impl_substs);
+ .instantiate(table.interner, args)
+ .to_chalk(table.interner);
if !table.unify(&trait_ref, &actual_trait_ref) {
return None;
@@ -907,10 +914,11 @@
.into_iter()
.map(|b| -> Goal { b.cast(Interner) });
for goal in wcs {
- if table.try_obligation(goal.clone()).no_solution() {
+ let goal = goal.to_nextsolver(table.interner);
+ if table.try_obligation(goal).no_solution() {
return None;
}
- table.register_obligation(goal.to_nextsolver(table.interner));
+ table.register_obligation(goal);
}
Some((
impl_.impl_items(db),
@@ -1014,7 +1022,9 @@
let local_crate = impl_.lookup(db).container.krate();
let is_local = |tgt_crate| tgt_crate == local_crate;
- let trait_ref = impl_trait.substitute(Interner, &substs);
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let trait_ref = impl_trait.instantiate(interner, args).to_chalk(interner);
let trait_id = from_chalk_trait_id(trait_ref.trait_id);
if is_local(trait_id.module(db).krate()) {
// trait to be implemented is local
@@ -1063,7 +1073,7 @@
pub fn iterate_path_candidates<'db>(
ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
db: &'db dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule,
name: Option<&Name>,
@@ -1085,7 +1095,7 @@
pub fn iterate_method_candidates_dyn<'db>(
ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
db: &'db dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule,
name: Option<&Name>,
@@ -1347,7 +1357,7 @@
fn iterate_method_candidates_for_self_ty<'db>(
self_ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>,
db: &'db dyn HirDatabase,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule,
name: Option<&Name>,
@@ -1395,7 +1405,7 @@
let db = table.db;
let canonical_self_ty = table.canonicalize(self_ty.clone().to_nextsolver(table.interner));
- let TraitEnvironment { krate, .. } = *table.trait_env;
+ let krate = table.trait_env.krate;
'traits: for &t in traits_in_scope {
let data = db.trait_signature(t);
@@ -1635,7 +1645,6 @@
let ty = table.instantiate_canonical_ns(ty);
let deref_chain = autoderef_method_receiver(table, ty);
for (ty, adj) in deref_chain {
- //let goal = generic_implements_goal_ns(db, &table.trait_env, index_trait, &ty);
let goal = generic_implements_goal_ns(table, index_trait, ty);
if !next_trait_solve_canonical_in_ctxt(&table.infer_ctxt, goal).no_solution() {
return Some(adj);
@@ -1694,8 +1703,10 @@
return IsValidCandidate::NotVisible;
}
let self_ty_matches = table.run_in_snapshot(|table| {
- let expected_self_ty =
- TyBuilder::impl_self_ty(db, impl_id).fill_with_inference_vars(table).build();
+ let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id)
+ .fill_with_inference_vars(table)
+ .build(DbInterner::conjure())
+ .to_chalk(DbInterner::conjure());
table.unify(&expected_self_ty, self_ty)
});
if !self_ty_matches {
@@ -1741,9 +1752,13 @@
.fill_with_inference_vars(table)
.build();
+ let args: crate::next_solver::GenericArgs<'_> =
+ fn_subst.to_nextsolver(table.interner);
let sig = db.callable_item_signature(fn_id.into());
- let expected_receiver =
- sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
+ let expected_receiver = sig
+ .map_bound(|s| s.skip_binder().inputs_and_output.as_slice()[0])
+ .instantiate(table.interner, args)
+ .to_chalk(table.interner);
// FIXME: Clean up this mess with some context struct like rustc's `ProbeContext`
let variance = match mode {
@@ -1754,7 +1769,7 @@
.infer_ctxt
.at(
&next_solver::infer::traits::ObligationCause::dummy(),
- table.trait_env.env.to_nextsolver(table.interner),
+ table.trait_env.env,
)
.relate(
DefineOpaqueTypes::No,
@@ -1767,12 +1782,10 @@
};
if !infer_ok.obligations.is_empty() {
- let mut ctxt = FulfillmentCtxt::new(&table.infer_ctxt);
- for pred in infer_ok.into_obligations() {
- ctxt.register_predicate_obligation(&table.infer_ctxt, pred);
- }
+ let mut ctxt = ObligationCtxt::new(&table.infer_ctxt);
+ ctxt.register_obligations(infer_ok.into_obligations());
// FIXME: Are we doing this correctly? Probably better to follow rustc more closely.
- check_that!(ctxt.select_where_possible(&table.infer_ctxt).is_empty());
+ check_that!(ctxt.select_where_possible().is_empty());
}
check_that!(table.unify(receiver_ty, &expected_receiver));
@@ -1815,9 +1828,11 @@
}
table.run_in_snapshot(|table| {
let _p = tracing::info_span!("subst_for_def").entered();
- let impl_subst =
- TyBuilder::subst_for_def(db, impl_id, None).fill_with_inference_vars(table).build();
- let expect_self_ty = db.impl_self_ty(impl_id).substitute(Interner, &impl_subst);
+ let impl_subst = table.infer_ctxt.fresh_args_for_item(impl_id.into());
+ let expect_self_ty = db
+ .impl_self_ty(impl_id)
+ .instantiate(table.interner, &impl_subst)
+ .to_chalk(table.interner);
check_that!(table.unify(&expect_self_ty, self_ty));
@@ -1825,65 +1840,49 @@
let _p = tracing::info_span!("check_receiver_ty").entered();
check_that!(data.has_self_param());
- let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone()))
- .fill_with_inference_vars(table)
- .build();
+ let fn_subst: crate::Substitution =
+ table.infer_ctxt.fresh_args_for_item(fn_id.into()).to_chalk(table.interner);
+ let args: crate::next_solver::GenericArgs<'_> = fn_subst.to_nextsolver(table.interner);
let sig = db.callable_item_signature(fn_id.into());
- let expected_receiver =
- sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
+ let expected_receiver = sig
+ .map_bound(|s| s.skip_binder().inputs_and_output.as_slice()[0])
+ .instantiate(table.interner, args)
+ .to_chalk(table.interner);
check_that!(table.unify(receiver_ty, &expected_receiver));
}
// We need to consider the bounds on the impl to distinguish functions of the same name
// for a type.
- let predicates = db.generic_predicates(impl_id.into());
- let goals = predicates.iter().map(|p| {
- let (p, b) = p
- .clone()
- .substitute(Interner, &impl_subst)
- // Skipping the inner binders is ok, as we don't handle quantified where
- // clauses yet.
- .into_value_and_skipped_binders();
- stdx::always!(b.len(Interner) == 0);
+ let predicates = db.generic_predicates_ns(impl_id.into());
+ let Some(predicates) = predicates.instantiate(table.interner, impl_subst) else {
+ return IsValidCandidate::Yes;
+ };
- p.cast::<Goal>(Interner)
- });
+ let mut ctxt = ObligationCtxt::new(&table.infer_ctxt);
- for goal in goals.clone() {
- match table.solve_obligation(goal) {
- Ok(_) => {}
- Err(_) => {
- return IsValidCandidate::No;
- }
- }
+ ctxt.register_obligations(predicates.into_iter().map(|p| {
+ PredicateObligation::new(
+ table.interner,
+ ObligationCause::new(),
+ table.trait_env.env,
+ p.0,
+ )
+ }));
+
+ if ctxt.select_where_possible().is_empty() {
+ IsValidCandidate::Yes
+ } else {
+ IsValidCandidate::No
}
-
- for goal in goals {
- if table.try_obligation(goal).no_solution() {
- return IsValidCandidate::No;
- }
- }
-
- IsValidCandidate::Yes
})
}
-pub fn implements_trait(
+pub fn implements_trait_unique<'db>(
ty: &Canonical<Ty>,
- db: &dyn HirDatabase,
- env: &TraitEnvironment,
- trait_: TraitId,
-) -> bool {
- let goal = generic_implements_goal(db, env, trait_, ty);
- !db.trait_solve(env.krate, env.block, goal.cast(Interner)).no_solution()
-}
-
-pub fn implements_trait_unique(
- ty: &Canonical<Ty>,
- db: &dyn HirDatabase,
- env: &TraitEnvironment,
+ db: &'db dyn HirDatabase,
+ env: &TraitEnvironment<'db>,
trait_: TraitId,
) -> bool {
let goal = generic_implements_goal(db, env, trait_, ty);
@@ -1891,11 +1890,11 @@
}
/// This creates Substs for a trait with the given Self type and type variables
-/// for all other parameters, to query Chalk with it.
+/// for all other parameters, to query next solver with it.
#[tracing::instrument(skip_all)]
-fn generic_implements_goal(
- db: &dyn HirDatabase,
- env: &TraitEnvironment,
+fn generic_implements_goal<'db>(
+ db: &'db dyn HirDatabase,
+ env: &TraitEnvironment<'db>,
trait_: TraitId,
self_ty: &Canonical<Ty>,
) -> Canonical<InEnvironment<super::DomainGoal>> {
@@ -1917,7 +1916,10 @@
let binders = CanonicalVarKinds::from_iter(Interner, kinds);
let obligation = trait_ref.cast(Interner);
- let value = InEnvironment::new(&env.env, obligation);
+ let value = InEnvironment::new(
+ &env.env.to_chalk(DbInterner::new_with(db, Some(env.krate), env.block)),
+ obligation,
+ );
Canonical { binders, value }
}
@@ -1934,11 +1936,7 @@
let trait_ref =
rustc_type_ir::TraitRef::new_from_args(table.infer_ctxt.interner, trait_.into(), args)
.with_replaced_self_ty(table.infer_ctxt.interner, self_ty);
- let goal = next_solver::Goal::new(
- table.infer_ctxt.interner,
- table.trait_env.env.to_nextsolver(table.infer_ctxt.interner),
- trait_ref,
- );
+ let goal = next_solver::Goal::new(table.infer_ctxt.interner, table.trait_env.env, trait_ref);
table.canonicalize(goal)
}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 3e658cb..c93165a 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -165,7 +165,7 @@
pub struct Evaluator<'a> {
db: &'a dyn HirDatabase,
- trait_env: Arc<TraitEnvironment>,
+ trait_env: Arc<TraitEnvironment<'a>>,
target_data_layout: Arc<TargetDataLayout>,
stack: Vec<u8>,
heap: Vec<u8>,
@@ -432,9 +432,12 @@
let self_ = match func.lookup(db).container {
ItemContainerId::ImplId(impl_id) => Some({
let generics = crate::generics::generics(db, impl_id.into());
+ let interner = DbInterner::new_with(db, None, None);
let substs = generics.placeholder_subst(db);
+ let args: crate::next_solver::GenericArgs<'_> =
+ substs.to_nextsolver(interner);
db.impl_self_ty(impl_id)
- .substitute(Interner, &substs)
+ .instantiate(interner, args)
.display(db, display_target)
.to_string()
}),
@@ -582,8 +585,8 @@
}
}
-pub fn interpret_mir(
- db: &dyn HirDatabase,
+pub fn interpret_mir<'db>(
+ db: &'db dyn HirDatabase,
body: Arc<MirBody>,
// FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now
// they share their body with their parent, so in MIR lowering we have locals of the parent body, which
@@ -591,7 +594,7 @@
// a zero size, hoping that they are all outside of our current body. Even without a fix for #7434, we can
// (and probably should) do better here, for example by excluding bindings outside of the target expression.
assert_placeholder_ty_is_unused: bool,
- trait_env: Option<Arc<TraitEnvironment>>,
+ trait_env: Option<Arc<TraitEnvironment<'db>>>,
) -> Result<(Result<Const>, MirOutput)> {
let ty = body.locals[return_slot()].ty.clone();
let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?;
@@ -632,11 +635,11 @@
impl<'db> Evaluator<'db> {
pub fn new(
- db: &dyn HirDatabase,
+ db: &'db dyn HirDatabase,
owner: DefWithBodyId,
assert_placeholder_ty_is_unused: bool,
- trait_env: Option<Arc<TraitEnvironment>>,
- ) -> Result<Evaluator<'_>> {
+ trait_env: Option<Arc<TraitEnvironment<'db>>>,
+ ) -> Result<Evaluator<'db>> {
let crate_id = owner.module(db).krate();
let target_data_layout = match db.target_data_layout(crate_id) {
Ok(target_data_layout) => target_data_layout,
@@ -2085,7 +2088,7 @@
if let Some(layout) = self.layout_cache.borrow().get(&ty.to_nextsolver(interner)) {
return Ok(layout
.is_sized()
- .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize)));
+ .then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize)));
}
if let DefWithBodyId::VariantId(f) = locals.body.owner
&& let Some((AdtId::EnumId(e), _)) = ty.as_adt()
@@ -2104,7 +2107,7 @@
let layout = layout?;
Ok(layout
.is_sized()
- .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize)))
+ .then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize)))
}
/// A version of `self.size_of` which returns error if the type is unsized. `what` argument should
@@ -2797,7 +2800,7 @@
)?;
// FIXME: there is some leak here
let size = layout.size.bytes_usize();
- let addr = self.heap_allocate(size, layout.align.abi.bytes() as usize)?;
+ let addr = self.heap_allocate(size, layout.align.bytes() as usize)?;
self.write_memory(addr, &result)?;
IntervalAndTy { interval: Interval { addr, size }, ty }
};
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index f67778b..40d76bf 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -14,6 +14,7 @@
use intern::{Symbol, sym};
use stdx::never;
+use crate::next_solver::mapping::NextSolverToChalk;
use crate::{
DropGlue,
display::DisplayTarget,
@@ -767,7 +768,7 @@
"align_of generic arg is not provided".into(),
));
};
- let align = self.layout(ty.to_nextsolver(interner))?.align.abi.bytes();
+ let align = self.layout(ty.to_nextsolver(interner))?.align.bytes();
destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size])
}
"size_of_val" => {
@@ -1371,9 +1372,8 @@
result = (l as i8).cmp(&(r as i8));
}
if let Some(e) = LangItem::Ordering.resolve_enum(self.db, self.crate_id) {
- let ty = self.db.ty(e.into());
- let r = self
- .compute_discriminant(ty.skip_binders().clone(), &[result as i8 as u8])?;
+ let ty = self.db.ty(e.into()).skip_binder().to_chalk(interner);
+ let r = self.compute_discriminant(ty.clone(), &[result as i8 as u8])?;
destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])?;
Ok(())
} else {
@@ -1431,7 +1431,7 @@
field_types.iter().next_back().unwrap().1.clone().substitute(Interner, subst);
let sized_part_size =
layout.fields.offset(field_types.iter().count() - 1).bytes_usize();
- let sized_part_align = layout.align.abi.bytes() as usize;
+ let sized_part_align = layout.align.bytes() as usize;
let (unsized_part_size, unsized_part_align) =
self.size_align_of_unsized(&last_field_ty, metadata, locals)?;
let align = sized_part_align.max(unsized_part_align) as isize;
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 50e416a..3e44e8c 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -43,7 +43,10 @@
Terminator, TerminatorKind, TupleFieldId, Ty, UnOp, VariantId, intern_const_scalar,
return_slot,
},
- next_solver::{DbInterner, mapping::ChalkToNextSolver},
+ next_solver::{
+ DbInterner,
+ mapping::{ChalkToNextSolver, NextSolverToChalk},
+ },
static_lifetime,
traits::FnTrait,
utils::ClosureSubst,
@@ -82,7 +85,7 @@
infer: &'db InferenceResult,
resolver: Resolver<'db>,
drop_scopes: Vec<DropScope>,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
}
// FIXME: Make this smaller, its stored in database queries
@@ -2207,8 +2210,13 @@
// otherwise it's an inline const, and has no parameter
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);
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let callable_sig = db
+ .callable_item_signature(fid.into())
+ .instantiate(interner, args)
+ .skip_binder()
+ .to_chalk(interner);
let mut params = callable_sig.params().iter();
let self_param = body.self_param.and_then(|id| Some((id, params.next()?.clone())));
break 'b ctx.lower_params_and_bindings(
diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs
index 555b878..f293f38 100644
--- a/crates/hir-ty/src/mir/monomorphization.rs
+++ b/crates/hir-ty/src/mir/monomorphization.rs
@@ -35,7 +35,7 @@
struct Filler<'a> {
db: &'a dyn HirDatabase,
- trait_env: Arc<TraitEnvironment>,
+ trait_env: Arc<TraitEnvironment<'a>>,
subst: &'a Substitution,
generics: Option<Generics>,
}
@@ -301,11 +301,11 @@
}
}
-pub fn monomorphized_mir_body_query(
- db: &dyn HirDatabase,
+pub fn monomorphized_mir_body_query<'db>(
+ db: &'db dyn HirDatabase,
owner: DefWithBodyId,
subst: Substitution,
- trait_env: Arc<crate::TraitEnvironment>,
+ trait_env: Arc<crate::TraitEnvironment<'db>>,
) -> Result<Arc<MirBody>, MirLowerError> {
let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def));
let filler = &mut Filler { db, subst: &subst, trait_env, generics };
@@ -315,20 +315,20 @@
Ok(Arc::new(body))
}
-pub(crate) fn monomorphized_mir_body_cycle_result(
- _db: &dyn HirDatabase,
+pub(crate) fn monomorphized_mir_body_cycle_result<'db>(
+ _db: &'db dyn HirDatabase,
_: DefWithBodyId,
_: Substitution,
- _: Arc<crate::TraitEnvironment>,
+ _: Arc<crate::TraitEnvironment<'db>>,
) -> Result<Arc<MirBody>, MirLowerError> {
Err(MirLowerError::Loop)
}
-pub fn monomorphized_mir_body_for_closure_query(
- db: &dyn HirDatabase,
+pub fn monomorphized_mir_body_for_closure_query<'db>(
+ db: &'db dyn HirDatabase,
closure: InternedClosureId,
subst: Substitution,
- trait_env: Arc<crate::TraitEnvironment>,
+ trait_env: Arc<crate::TraitEnvironment<'db>>,
) -> Result<Arc<MirBody>, MirLowerError> {
let InternedClosure(owner, _) = db.lookup_intern_closure(closure);
let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def));
diff --git a/crates/hir-ty/src/next_solver.rs b/crates/hir-ty/src/next_solver.rs
index 073a029..ab167e8 100644
--- a/crates/hir-ty/src/next_solver.rs
+++ b/crates/hir-ty/src/next_solver.rs
@@ -13,7 +13,7 @@
pub mod interner;
mod ir_print;
pub mod mapping;
-mod normalize;
+pub mod normalize;
pub mod obligation_ctxt;
mod opaques;
pub mod predicate;
diff --git a/crates/hir-ty/src/next_solver/consts.rs b/crates/hir-ty/src/next_solver/consts.rs
index 7ebefa7..0b35820 100644
--- a/crates/hir-ty/src/next_solver/consts.rs
+++ b/crates/hir-ty/src/next_solver/consts.rs
@@ -36,8 +36,6 @@
internee: kind,
flags: flags.flags,
outer_exclusive_binder: flags.outer_exclusive_binder,
- #[cfg(feature = "in-rust-tree")]
- stable_hash: ena::fingerprint::Fingerprint::ZERO,
};
Const::new_(interner.db(), InternedWrapperNoDebug(cached))
}
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs
index 9cf56be..b72504a 100644
--- a/crates/hir-ty/src/next_solver/interner.rs
+++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -1091,23 +1091,21 @@
ItemContainerId::ImplId(it) => it,
_ => panic!("assoc ty value should be in impl"),
};
- self.db().ty_ns(id.into())
+ self.db().ty(id.into())
}
- SolverDefId::AdtId(id) => self.db().ty_ns(id.into()),
+ SolverDefId::AdtId(id) => self.db().ty(id.into()),
// FIXME(next-solver): This uses the types of `query mir_borrowck` in rustc.
//
// We currently always use the type from HIR typeck which ignores regions. This
// should be fine.
SolverDefId::InternedOpaqueTyId(_) => self.type_of_opaque_hir_typeck(def_id),
- SolverDefId::FunctionId(id) => self.db.value_ty_ns(id.into()).unwrap(),
+ SolverDefId::FunctionId(id) => self.db.value_ty(id.into()).unwrap(),
SolverDefId::Ctor(id) => {
let id = match id {
Ctor::Struct(id) => id.into(),
Ctor::Enum(id) => id.into(),
};
- self.db
- .value_ty_ns(id)
- .expect("`SolverDefId::Ctor` should have a function-like ctor")
+ self.db.value_ty(id).expect("`SolverDefId::Ctor` should have a function-like ctor")
}
_ => panic!("Unexpected def_id `{def_id:?}` provided for `type_of`"),
}
@@ -1227,7 +1225,7 @@
self,
def_id: Self::FunctionId,
) -> EarlyBinder<Self, rustc_type_ir::Binder<Self, rustc_type_ir::FnSig<Self>>> {
- self.db().callable_item_signature_ns(def_id.0)
+ self.db().callable_item_signature(def_id.0)
}
fn coroutine_movability(self, def_id: Self::CoroutineId) -> rustc_ast_ir::Movability {
@@ -1322,7 +1320,7 @@
self,
def_id: Self::DefId,
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
- let predicates = self.db().generic_predicates_without_parent_ns(def_id.try_into().unwrap());
+ let predicates = self.db().generic_predicates_without_parent(def_id.try_into().unwrap());
let predicates: Vec<_> = predicates.iter().cloned().collect();
EarlyBinder::bind(predicates.into_iter())
}
@@ -1396,7 +1394,7 @@
self,
impl_id: Self::ImplId,
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
- let trait_ref = self.db().impl_trait_ns(impl_id.0).expect("expected an impl of trait");
+ let trait_ref = self.db().impl_trait(impl_id.0).expect("expected an impl of trait");
trait_ref.map_bound(|trait_ref| {
let clause: Clause<'_> = trait_ref.upcast(self);
Clauses::new_from_iter(
@@ -1635,7 +1633,7 @@
|impls| {
for i in impls.for_trait(trait_) {
use rustc_type_ir::TypeVisitable;
- let contains_errors = self.db().impl_trait_ns(i).map_or(false, |b| {
+ let contains_errors = self.db().impl_trait(i).map_or(false, |b| {
b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break()
});
if contains_errors {
@@ -1658,7 +1656,7 @@
for fp in fps {
for i in impls.for_trait_and_self_ty(trait_, *fp) {
use rustc_type_ir::TypeVisitable;
- let contains_errors = self.db().impl_trait_ns(i).map_or(false, |b| {
+ let contains_errors = self.db().impl_trait(i).map_or(false, |b| {
b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break()
});
if contains_errors {
@@ -1704,7 +1702,7 @@
impl_id: Self::ImplId,
) -> EarlyBinder<Self, rustc_type_ir::TraitRef<Self>> {
let db = self.db();
- db.impl_trait_ns(impl_id.0)
+ db.impl_trait(impl_id.0)
// ImplIds for impls where the trait ref can't be resolved should never reach trait solving
.expect("invalid impl passed to trait solver")
}
diff --git a/crates/hir-ty/src/next_solver/mapping.rs b/crates/hir-ty/src/next_solver/mapping.rs
index b24b996..f3f74f6 100644
--- a/crates/hir-ty/src/next_solver/mapping.rs
+++ b/crates/hir-ty/src/next_solver/mapping.rs
@@ -575,6 +575,17 @@
}
}
+impl<'db, T: NextSolverToChalk<'db, U>, U: HasInterner<Interner = Interner>>
+ NextSolverToChalk<'db, chalk_ir::Binders<U>> for rustc_type_ir::Binder<DbInterner<'db>, T>
+{
+ fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Binders<U> {
+ chalk_ir::Binders::new(
+ self.bound_vars().to_chalk(interner),
+ self.skip_binder().to_chalk(interner),
+ )
+ }
+}
+
impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds<Interner> {
fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKinds {
BoundVarKinds::new_from_iter(
@@ -584,6 +595,12 @@
}
}
+impl<'db> NextSolverToChalk<'db, chalk_ir::VariableKinds<Interner>> for BoundVarKinds {
+ fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::VariableKinds<Interner> {
+ chalk_ir::VariableKinds::from_iter(Interner, self.iter().map(|v| v.to_chalk(interner)))
+ }
+}
+
impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind<Interner> {
fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKind {
match self {
@@ -594,6 +611,18 @@
}
}
+impl<'db> NextSolverToChalk<'db, chalk_ir::VariableKind<Interner>> for BoundVarKind {
+ fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::VariableKind<Interner> {
+ match self {
+ BoundVarKind::Ty(_) => chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
+ BoundVarKind::Region(_) => chalk_ir::VariableKind::Lifetime,
+ BoundVarKind::Const => {
+ chalk_ir::VariableKind::Const(chalk_ir::TyKind::Error.intern(Interner))
+ }
+ }
+ }
+}
+
impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg<Interner> {
fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArg<'db> {
match self.data(Interner) {
@@ -1233,6 +1262,22 @@
}
}
+impl<'db> NextSolverToChalk<'db, crate::CallableSig> for rustc_type_ir::FnSig<DbInterner<'db>> {
+ fn to_chalk(self, interner: DbInterner<'db>) -> crate::CallableSig {
+ crate::CallableSig {
+ abi: self.abi,
+ is_varargs: self.c_variadic,
+ safety: match self.safety {
+ super::abi::Safety::Safe => chalk_ir::Safety::Safe,
+ super::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe,
+ },
+ params_and_return: triomphe::Arc::from_iter(
+ self.inputs_and_output.iter().map(|ty| convert_ty_for_result(interner, ty)),
+ ),
+ }
+ }
+}
+
pub fn convert_canonical_args_for_result<'db>(
interner: DbInterner<'db>,
args: Canonical<'db, Vec<GenericArg<'db>>>,
@@ -1266,7 +1311,7 @@
Substitution::from_iter(Interner, substs)
}
-pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) -> crate::Ty {
+pub fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) -> crate::Ty {
use crate::{Scalar, TyKind};
use chalk_ir::{FloatTy, IntTy, UintTy};
match ty.kind() {
diff --git a/crates/hir-ty/src/next_solver/predicate.rs b/crates/hir-ty/src/next_solver/predicate.rs
index 8654541..99b1354 100644
--- a/crates/hir-ty/src/next_solver/predicate.rs
+++ b/crates/hir-ty/src/next_solver/predicate.rs
@@ -227,8 +227,6 @@
internee: kind,
flags: flags.flags,
outer_exclusive_binder: flags.outer_exclusive_binder,
- #[cfg(feature = "in-rust-tree")]
- stable_hash: ena::fingerprint::Fingerprint::ZERO,
};
Predicate::new_(interner.db(), InternedWrapperNoDebug(cached))
}
diff --git a/crates/hir-ty/src/next_solver/region.rs b/crates/hir-ty/src/next_solver/region.rs
index d6214d9..0bfd2b8 100644
--- a/crates/hir-ty/src/next_solver/region.rs
+++ b/crates/hir-ty/src/next_solver/region.rs
@@ -15,7 +15,7 @@
interner::{BoundVarKind, DbInterner, Placeholder},
};
-type RegionKind<'db> = rustc_type_ir::RegionKind<DbInterner<'db>>;
+pub type RegionKind<'db> = rustc_type_ir::RegionKind<DbInterner<'db>>;
#[salsa::interned(constructor = new_, debug)]
pub struct Region<'db> {
@@ -53,6 +53,10 @@
Region::new(interner, RegionKind::ReVar(v))
}
+ pub fn new_erased(interner: DbInterner<'db>) -> Region<'db> {
+ Region::new(interner, RegionKind::ReErased)
+ }
+
pub fn is_placeholder(&self) -> bool {
matches!(self.inner(), RegionKind::RePlaceholder(..))
}
@@ -61,6 +65,10 @@
matches!(self.inner(), RegionKind::ReStatic)
}
+ pub fn is_var(&self) -> bool {
+ matches!(self.inner(), RegionKind::ReVar(_))
+ }
+
pub fn error(interner: DbInterner<'db>) -> Self {
Region::new(interner, RegionKind::ReError(ErrorGuaranteed))
}
diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs
index 946e57e..a161423 100644
--- a/crates/hir-ty/src/next_solver/solver.rs
+++ b/crates/hir-ty/src/next_solver/solver.rs
@@ -156,16 +156,16 @@
SolverDefId::TypeAliasId(id) => id,
_ => panic!("Unexpected SolverDefId"),
};
- let trait_ref = self
+ let trait_ = self
.0
.interner
.db()
.impl_trait(impl_id.0)
// ImplIds for impls where the trait ref can't be resolved should never reach solver
.expect("invalid impl passed to next-solver")
- .into_value_and_skipped_binders()
+ .skip_binder()
+ .def_id
.0;
- let trait_ = trait_ref.hir_trait_id();
let trait_data = trait_.trait_items(self.0.interner.db());
let id =
impl_id.0.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> {
diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs
index c7a747a..a25996a 100644
--- a/crates/hir-ty/src/next_solver/ty.rs
+++ b/crates/hir-ty/src/next_solver/ty.rs
@@ -7,6 +7,7 @@
use intern::{Interned, Symbol, sym};
use rustc_abi::{Float, Integer, Size};
use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult};
+use rustc_type_ir::TyVid;
use rustc_type_ir::{
BoundVar, ClosureKind, CollectAndApply, FlagComputation, Flags, FloatTy, FloatVid, InferTy,
IntTy, IntVid, Interner, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
@@ -60,8 +61,6 @@
internee: kind,
flags: flags.flags,
outer_exclusive_binder: flags.outer_exclusive_binder,
- #[cfg(feature = "in-rust-tree")]
- stable_hash: ena::fingerprint::Fingerprint::ZERO,
};
Ty::new_(interner.db(), InternedWrapperNoDebug(cached))
}
@@ -338,6 +337,14 @@
matches!(self.kind(), TyKind::Tuple(tys) if tys.inner().is_empty())
}
+ #[inline]
+ pub fn ty_vid(self) -> Option<TyVid> {
+ match self.kind() {
+ TyKind::Infer(rustc_type_ir::TyVar(vid)) => Some(vid),
+ _ => None,
+ }
+ }
+
/// Given a `fn` type, returns an equivalent `unsafe fn` type;
/// that is, a `fn` type that is equivalent in every way for being
/// unsafe.
diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs
index c0b930e..8587c13 100644
--- a/crates/hir-ty/src/tests/incremental.rs
+++ b/crates/hir-ty/src/tests/incremental.rs
@@ -511,7 +511,6 @@
"struct_signature_shim",
"struct_signature_with_source_map_shim",
"attrs_shim",
- "type_for_adt_tracked",
]
"#]],
);
@@ -609,9 +608,6 @@
"trait_impls_in_crate_shim",
"impl_trait_with_diagnostics_shim",
"impl_self_ty_with_diagnostics_shim",
- "type_for_adt_tracked",
- "impl_trait_with_diagnostics_ns_shim",
- "impl_self_ty_with_diagnostics_ns_shim",
"generic_predicates_ns_shim",
"value_ty_shim",
"generic_predicates_shim",
@@ -700,8 +696,6 @@
"trait_impls_in_crate_shim",
"impl_trait_with_diagnostics_shim",
"impl_self_ty_with_diagnostics_shim",
- "impl_trait_with_diagnostics_ns_shim",
- "impl_self_ty_with_diagnostics_ns_shim",
"generic_predicates_ns_shim",
"generic_predicates_shim",
]
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index b14ce35..2f8f666 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -2050,10 +2050,10 @@
/// Attempts to downcast the box to a concrete type.
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> {
let err: Box<dyn Error> = self;
- // ^^^^ expected Box<dyn Error + '?>, got Box<dyn Error + Send + '?>
+ // ^^^^ expected Box<dyn Error + '?>, got Box<dyn Error + Send + 'static>
// FIXME, type mismatch should not occur
<dyn Error>::downcast(err).map_err(|_| loop {})
- //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box<dyn Error + '?>) -> Result<Box<{unknown}>, Box<dyn Error + '?>>
+ //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box<dyn Error + 'static>) -> Result<Box<{unknown}>, Box<dyn Error + 'static>>
}
}
"#,
diff --git a/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs
index af5290d..4d68179 100644
--- a/crates/hir-ty/src/tests/never_type.rs
+++ b/crates/hir-ty/src/tests/never_type.rs
@@ -14,8 +14,6 @@
);
}
-// FIXME(next-solver): The never type fallback implemented in r-a no longer works properly because of
-// `Coerce` predicates. We should reimplement fallback like rustc.
#[test]
fn infer_never2() {
check_types(
@@ -26,7 +24,7 @@
let a = gen();
if false { a } else { loop {} };
a;
-} //^ {unknown}
+} //^ !
"#,
);
}
@@ -41,7 +39,7 @@
let a = gen();
if false { loop {} } else { a };
a;
- //^ {unknown}
+ //^ !
}
"#,
);
@@ -56,7 +54,7 @@
fn test() {
let a = if true { Option::None } else { Option::Some(return) };
a;
-} //^ Option<{unknown}>
+} //^ Option<!>
"#,
);
}
@@ -220,7 +218,7 @@
_ => loop {},
};
i;
-} //^ {unknown}
+} //^ !
"#,
);
}
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 2ba1e23..00835aa 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -632,7 +632,7 @@
488..522 '{ ... }': <SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}> as BoxedDsl<DB>>::Output
498..502 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}>
498..508 'self.order': O
- 498..515 'self.o...into()': dyn QueryFragment<DB> + '?
+ 498..515 'self.o...into()': dyn QueryFragment<DB> + 'static
"#]],
);
}
@@ -1951,7 +1951,7 @@
Alias::Braced;
//^^^^^^^^^^^^^ {unknown}
let Alias::Braced = loop {};
- //^^^^^^^^^^^^^ {unknown}
+ //^^^^^^^^^^^^^ !
let Alias::Braced(..) = loop {};
//^^^^^^^^^^^^^^^^^ Enum
diff --git a/crates/hir-ty/src/tests/regression/new_solver.rs b/crates/hir-ty/src/tests/regression/new_solver.rs
index ead79a8..adc35cc 100644
--- a/crates/hir-ty/src/tests/regression/new_solver.rs
+++ b/crates/hir-ty/src/tests/regression/new_solver.rs
@@ -1,6 +1,6 @@
use expect_test::expect;
-use crate::tests::{check_infer, check_no_mismatches};
+use crate::tests::{check_infer, check_no_mismatches, check_types};
#[test]
fn regression_20365() {
@@ -418,3 +418,57 @@
"#]],
);
}
+
+#[test]
+fn regression_19637() {
+ check_no_mismatches(
+ r#"
+//- minicore: coerce_unsized
+pub trait Any {}
+
+impl<T: 'static> Any for T {}
+
+pub trait Trait: Any {
+ type F;
+}
+
+pub struct TT {}
+
+impl Trait for TT {
+ type F = f32;
+}
+
+pub fn coercion(x: &mut dyn Any) -> &mut dyn Any {
+ x
+}
+
+fn main() {
+ let mut t = TT {};
+ let tt = &mut t as &mut dyn Trait<F = f32>;
+ let st = coercion(tt);
+}
+ "#,
+ );
+}
+
+#[test]
+fn double_into_iter() {
+ check_types(
+ r#"
+//- minicore: iterator
+
+fn intoiter_issue<A, B>(foo: A)
+where
+ A: IntoIterator<Item = B>,
+ B: IntoIterator<Item = usize>,
+{
+ for x in foo {
+ // ^ B
+ for m in x {
+ // ^ usize
+ }
+ }
+}
+"#,
+ );
+}
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 41f8d4e..66faac0 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -1487,8 +1487,8 @@
268..269 'x': Box<dyn Trait<u64> + '?>
275..276 'y': &'? (dyn Trait<u64> + '?)
286..287 'z': Box<dyn Trait<u64> + '?>
- 290..293 'bar': fn bar() -> Box<dyn Trait<u64> + '?>
- 290..295 'bar()': Box<dyn Trait<u64> + '?>
+ 290..293 'bar': fn bar() -> Box<dyn Trait<u64> + 'static>
+ 290..295 'bar()': Box<dyn Trait<u64> + 'static>
301..302 'x': Box<dyn Trait<u64> + '?>
301..308 'x.foo()': u64
314..315 'y': &'? (dyn Trait<u64> + '?)
@@ -1535,7 +1535,7 @@
251..252 's': S<u32, i32>
267..289 '{ ...z(); }': ()
273..274 's': S<u32, i32>
- 273..280 's.bar()': &'? (dyn Trait<u32, i32> + '?)
+ 273..280 's.bar()': &'? (dyn Trait<u32, i32> + 'static)
273..286 's.bar().baz()': (u32, i32)
"#]],
);
@@ -1568,8 +1568,8 @@
106..107 'x': dyn Trait + '?
113..114 'y': &'? (dyn Trait + '?)
124..125 'z': dyn Trait + '?
- 128..131 'bar': fn bar() -> dyn Trait + '?
- 128..133 'bar()': dyn Trait + '?
+ 128..131 'bar': fn bar() -> dyn Trait + 'static
+ 128..133 'bar()': dyn Trait + 'static
139..140 'x': dyn Trait + '?
139..146 'x.foo()': u64
152..153 'y': &'? (dyn Trait + '?)
@@ -1597,7 +1597,7 @@
47..48 '_': &'? (dyn Fn(S) + '?)
58..60 '{}': ()
71..105 '{ ...()); }': ()
- 77..78 'f': fn f(&'? (dyn Fn(S) + '?))
+ 77..78 'f': fn f(&'? (dyn Fn(S) + 'static))
77..102 'f(&|nu...foo())': ()
79..101 '&|numb....foo()': &'? impl Fn(S)
80..101 '|numbe....foo()': impl Fn(S)
@@ -2952,7 +2952,7 @@
34..36 '{}': ()
46..47 'x': &'? (dyn Foo + '?)
59..74 '{ foo(x); }': ()
- 65..68 'foo': fn foo(&'? (dyn Foo + '?))
+ 65..68 'foo': fn foo(&'? (dyn Foo + 'static))
65..71 'foo(x)': ()
69..70 'x': &'? (dyn Foo + '?)
"#]],
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index 8095d70..8ac1523 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -1,4 +1,4 @@
-//! Trait solving using Chalk.
+//! Trait solving using next trait solver.
use core::fmt;
use std::hash::Hash;
@@ -25,7 +25,7 @@
db::HirDatabase,
infer::unify::InferenceTable,
next_solver::{
- DbInterner, GenericArg, Predicate, SolverContext, Span,
+ DbInterner, GenericArg, ParamEnv, Predicate, SolverContext, Span,
infer::{DbInternerInferExt, InferCtxt},
mapping::{ChalkToNextSolver, convert_canonical_args_for_result},
util::mini_canonicalize,
@@ -39,21 +39,21 @@
/// ```
/// we assume that `T: Default`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct TraitEnvironment {
+pub struct TraitEnvironment<'db> {
pub krate: Crate,
pub block: Option<BlockId>,
// FIXME make this a BTreeMap
traits_from_clauses: Box<[(Ty, TraitId)]>,
- pub env: chalk_ir::Environment<Interner>,
+ pub env: ParamEnv<'db>,
}
-impl TraitEnvironment {
+impl<'db> TraitEnvironment<'db> {
pub fn empty(krate: Crate) -> Arc<Self> {
Arc::new(TraitEnvironment {
krate,
block: None,
traits_from_clauses: Box::default(),
- env: chalk_ir::Environment::new(Interner),
+ env: ParamEnv::empty(),
})
}
@@ -61,7 +61,7 @@
krate: Crate,
block: Option<BlockId>,
traits_from_clauses: Box<[(Ty, TraitId)]>,
- env: chalk_ir::Environment<Interner>,
+ env: ParamEnv<'db>,
) -> Arc<Self> {
Arc::new(TraitEnvironment { krate, block, traits_from_clauses, env })
}
@@ -78,10 +78,10 @@
}
}
-pub(crate) fn normalize_projection_query(
- db: &dyn HirDatabase,
+pub(crate) fn normalize_projection_query<'db>(
+ db: &'db dyn HirDatabase,
projection: ProjectionTy,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
) -> Ty {
if projection.substitution.iter(Interner).any(|arg| {
arg.ty(Interner)
@@ -128,7 +128,7 @@
chalk_ir::Canonical { binders, value: identity_subst }
}
-/// Solve a trait goal using Chalk.
+/// Solve a trait goal using next trait solver.
pub(crate) fn trait_solve_query(
db: &dyn HirDatabase,
krate: Crate,
@@ -325,7 +325,7 @@
}
}
-/// Solve a trait goal using Chalk.
+/// Solve a trait goal using next trait solver.
pub fn next_trait_solve_in_ctxt<'db, 'a>(
infer_ctxt: &'a InferCtxt<'db>,
goal: crate::next_solver::Goal<'db, crate::next_solver::Predicate<'db>>,
diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs
index 8593dba..a17cf37 100644
--- a/crates/hir-ty/src/variance.rs
+++ b/crates/hir-ty/src/variance.rs
@@ -15,6 +15,8 @@
use crate::db::HirDatabase;
use crate::generics::{Generics, generics};
+use crate::next_solver::DbInterner;
+use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk};
use crate::{
AliasTy, Const, ConstScalar, DynTyExt, GenericArg, GenericArgData, Interner, Lifetime,
LifetimeData, Ty, TyKind,
@@ -238,14 +240,15 @@
}
GenericDefId::FunctionId(f) => {
let subst = self.generics.placeholder_subst(self.db);
- self.add_constraints_from_sig(
- self.db
- .callable_item_signature(f.into())
- .substitute(Interner, &subst)
- .params_and_return
- .iter(),
- Variance::Covariant,
- );
+ let interner = DbInterner::new_with(self.db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = subst.to_nextsolver(interner);
+ let sig = self
+ .db
+ .callable_item_signature(f.into())
+ .instantiate(interner, args)
+ .skip_binder()
+ .to_chalk(interner);
+ self.add_constraints_from_sig(sig.params_and_return.iter(), Variance::Covariant);
}
_ => {}
}
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 833a9ef..2bf9bb8 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -24,7 +24,7 @@
Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam,
Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef, TupleField, TyBuilder,
- Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant,
+ Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, Variant,
};
impl HirDisplay for Function {
@@ -437,6 +437,12 @@
}
}
+impl HirDisplay for TypeNs<'_> {
+ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+ self.ty.hir_fmt(f)
+ }
+}
+
impl HirDisplay for ExternCrateDecl {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index f8dacf0..4342624 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -20,10 +20,6 @@
#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
#![recursion_limit = "512"]
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_type_ir;
-
-#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_type_ir as rustc_type_ir;
mod attrs;
@@ -86,7 +82,9 @@
method_resolution,
mir::{MutBorrowKind, interpret_mir},
next_solver::{
- ClauseKind, DbInterner, GenericArgs, infer::InferCtxt, mapping::ChalkToNextSolver,
+ ClauseKind, DbInterner, GenericArgs,
+ infer::InferCtxt,
+ mapping::{ChalkToNextSolver, NextSolverToChalk, convert_ty_for_result},
},
primitive::UintTy,
traits::FnTrait,
@@ -867,10 +865,13 @@
.collect();
if !missing.is_empty() {
- let self_ty = db.impl_self_ty(impl_def.id).substitute(
- Interner,
- &hir_ty::generics::generics(db, impl_def.id.into()).placeholder_subst(db),
- );
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> =
+ hir_ty::generics::generics(db, impl_def.id.into())
+ .placeholder_subst(db)
+ .to_nextsolver(interner);
+ let self_ty =
+ db.impl_self_ty(impl_def.id).instantiate(interner, args).to_chalk(interner);
let self_ty = if let TyKind::Alias(AliasTy::Projection(projection)) =
self_ty.kind(Interner)
{
@@ -1346,19 +1347,12 @@
u32::from(self.id.into_raw()) as usize
}
- /// Returns the type as in the signature of the struct (i.e., with
- /// placeholder types for type parameters). Only use this in the context of
- /// the field definition.
- pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> {
+ /// Returns the type as in the signature of the struct. Only use this in the
+ /// context of the field definition.
+ pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> {
let var_id = self.parent.into();
- let generic_def_id: GenericDefId = match self.parent {
- VariantDef::Struct(it) => it.id.into(),
- VariantDef::Union(it) => it.id.into(),
- VariantDef::Variant(it) => it.id.lookup(db).parent.into(),
- };
- let substs = TyBuilder::placeholder_subst(db, generic_def_id);
- let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs);
- Type::new(db, var_id, ty)
+ let ty = db.field_types_ns(var_id)[self.id].skip_binder();
+ TypeNs::new(db, var_id, ty)
}
// FIXME: Find better API to also handle const generics
@@ -1388,9 +1382,8 @@
}
pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
- let interner = DbInterner::new_with(db, None, None);
db.layout_of_ty(
- self.ty(db).ty.to_nextsolver(interner),
+ self.ty(db).ty,
db.trait_environment(match hir_def::VariantId::from(self.parent) {
hir_def::VariantId::EnumVariantId(id) => {
GenericDefId::AdtId(id.lookup(db).parent.into())
@@ -1510,7 +1503,7 @@
let krate = self.inner.krate(db);
let interner = DbInterner::new_with(db, Some(krate.base()), None);
- let ty = db.ty_ns(self.inner.id.into());
+ let ty = db.ty(self.inner.id.into());
TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args))
}
}
@@ -1670,7 +1663,7 @@
let krate = self.inner.krate(db);
let interner = DbInterner::new_with(db, Some(krate.base()), None);
- let ty = db.ty_ns(self.inner.id.into());
+ let ty = db.ty(self.inner.id.into());
TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args))
}
}
@@ -1857,7 +1850,8 @@
ParamKind::Lifetime => error_lifetime().cast(Interner),
}
})
- .build();
+ .build(DbInterner::conjure())
+ .to_chalk(DbInterner::conjure());
Type::new(db, id, ty)
}
@@ -2292,7 +2286,13 @@
pub fn fn_ptr_type(self, db: &dyn HirDatabase) -> Type<'_> {
let resolver = self.id.resolver(db);
let substs = TyBuilder::placeholder_subst(db, self.id);
- let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let callable_sig = db
+ .callable_item_signature(self.id.into())
+ .instantiate(interner, args)
+ .skip_binder()
+ .to_chalk(interner);
let ty = TyKind::Function(callable_sig.to_fn_ptr()).intern(Interner);
Type::new_with_resolver_inner(db, &resolver, ty)
}
@@ -2301,8 +2301,14 @@
pub fn ret_type(self, db: &dyn HirDatabase) -> Type<'_> {
let resolver = self.id.resolver(db);
let substs = TyBuilder::placeholder_subst(db, self.id);
- let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
- let ty = callable_sig.ret().clone();
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let ty = db
+ .callable_item_signature(self.id.into())
+ .instantiate(interner, args)
+ .skip_binder()
+ .output()
+ .to_chalk(interner);
Type::new_with_resolver_inner(db, &resolver, ty)
}
@@ -2331,8 +2337,14 @@
parent_id.map(|id| TyBuilder::subst_for_def(db, id, None).fill(&mut filler).build());
let substs = TyBuilder::subst_for_def(db, self.id, parent_substs).fill(&mut filler).build();
- let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
- let ty = callable_sig.ret().clone();
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let ty = db
+ .callable_item_signature(self.id.into())
+ .instantiate(interner, args)
+ .skip_binder()
+ .output()
+ .to_chalk(interner);
Type::new_with_resolver_inner(db, &resolver, ty)
}
@@ -2342,8 +2354,14 @@
}
let resolver = self.id.resolver(db);
let substs = TyBuilder::placeholder_subst(db, self.id);
- let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
- let ret_ty = callable_sig.ret().clone();
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let ret_ty = db
+ .callable_item_signature(self.id.into())
+ .instantiate(interner, args)
+ .skip_binder()
+ .output()
+ .to_chalk(interner);
for pred in ret_ty.impl_trait_bounds(db).into_iter().flatten() {
if let WhereClause::AliasEq(output_eq) = pred.into_value_and_skipped_binders().0 {
return Type::new_with_resolver_inner(db, &resolver, output_eq.ty).into();
@@ -2363,7 +2381,13 @@
pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param<'_>> {
let environment = db.trait_environment(self.id.into());
let substs = TyBuilder::placeholder_subst(db, self.id);
- let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let callable_sig = db
+ .callable_item_signature(self.id.into())
+ .instantiate(interner, args)
+ .skip_binder()
+ .to_chalk(interner);
callable_sig
.params()
.iter()
@@ -2391,7 +2415,13 @@
pub fn params_without_self(self, db: &dyn HirDatabase) -> Vec<Param<'_>> {
let environment = db.trait_environment(self.id.into());
let substs = TyBuilder::placeholder_subst(db, self.id);
- let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let callable_sig = db
+ .callable_item_signature(self.id.into())
+ .instantiate(interner, args)
+ .skip_binder()
+ .to_chalk(interner);
let skip = if db.function_signature(self.id).has_self_param() { 1 } else { 0 };
callable_sig
.params()
@@ -2441,7 +2471,13 @@
GenericArg::new(Interner, GenericArgData::Ty(ty))
})
.build();
- let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let callable_sig = db
+ .callable_item_signature(self.id.into())
+ .instantiate(interner, args)
+ .skip_binder()
+ .to_chalk(interner);
let skip = if db.function_signature(self.id).has_self_param() { 1 } else { 0 };
callable_sig
.params()
@@ -2736,8 +2772,13 @@
pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> {
let substs = TyBuilder::placeholder_subst(db, self.func);
- let callable_sig =
- db.callable_item_signature(self.func.into()).substitute(Interner, &substs);
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let callable_sig = db
+ .callable_item_signature(self.func.into())
+ .instantiate(interner, args)
+ .skip_binder()
+ .to_chalk(interner);
let environment = db.trait_environment(self.func.into());
let ty = callable_sig.params()[0].clone();
Type { env: environment, ty, _pd: PhantomCovariantLifetime::new() }
@@ -2769,8 +2810,13 @@
let parent_substs = TyBuilder::subst_for_def(db, parent_id, None).fill(&mut filler).build();
let substs =
TyBuilder::subst_for_def(db, self.func, Some(parent_substs)).fill(&mut filler).build();
- let callable_sig =
- db.callable_item_signature(self.func.into()).substitute(Interner, &substs);
+ let interner = DbInterner::new_with(db, None, None);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let callable_sig = db
+ .callable_item_signature(self.func.into())
+ .instantiate(interner, args)
+ .skip_binder()
+ .to_chalk(interner);
let environment = db.trait_environment(self.func.into());
let ty = callable_sig.params()[0].clone();
Type { env: environment, ty, _pd: PhantomCovariantLifetime::new() }
@@ -3774,7 +3820,7 @@
push_ty_diagnostics(
db,
acc,
- db.generic_predicates_without_parent_with_diagnostics_ns(def).1,
+ db.generic_predicates_without_parent_with_diagnostics(def).1,
&source_map,
);
for (param_id, param) in generics.iter_type_or_consts() {
@@ -3814,12 +3860,12 @@
pub struct GenericSubstitution<'db> {
def: GenericDefId,
subst: Substitution,
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
_pd: PhantomCovariantLifetime<'db>,
}
impl<'db> GenericSubstitution<'db> {
- fn new(def: GenericDefId, subst: Substitution, env: Arc<TraitEnvironment>) -> Self {
+ fn new(def: GenericDefId, subst: Substitution, env: Arc<TraitEnvironment<'db>>) -> Self {
Self { def, subst, env, _pd: PhantomCovariantLifetime::new() }
}
@@ -4499,21 +4545,23 @@
}
pub fn trait_(self, db: &dyn HirDatabase) -> Option<Trait> {
- let trait_ref = db.impl_trait_ns(self.id)?;
+ let trait_ref = db.impl_trait(self.id)?;
let id = trait_ref.skip_binder().def_id;
Some(Trait { id: id.0 })
}
pub fn trait_ref(self, db: &dyn HirDatabase) -> Option<TraitRef<'_>> {
- let trait_ref = db.impl_trait_ns(self.id)?.instantiate_identity();
+ let trait_ref = db.impl_trait(self.id)?.instantiate_identity();
let resolver = self.id.resolver(db);
Some(TraitRef::new_with_resolver(db, &resolver, trait_ref))
}
pub fn self_ty(self, db: &dyn HirDatabase) -> Type<'_> {
let resolver = self.id.resolver(db);
+ let interner = DbInterner::new_with(db, Some(resolver.krate()), None);
let substs = TyBuilder::placeholder_subst(db, self.id);
- let ty = db.impl_self_ty(self.id).substitute(Interner, &substs);
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let ty = db.impl_self_ty(self.id).instantiate(interner, args).to_chalk(interner);
Type::new_with_resolver_inner(db, &resolver, ty)
}
@@ -4573,7 +4621,7 @@
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct TraitRef<'db> {
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
trait_ref: hir_ty::next_solver::TraitRef<'db>,
_pd: PhantomCovariantLifetime<'db>,
}
@@ -4796,7 +4844,7 @@
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct Type<'db> {
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
ty: Ty,
_pd: PhantomCovariantLifetime<'db>,
}
@@ -4834,32 +4882,40 @@
}
fn from_def(db: &'db dyn HirDatabase, def: impl Into<TyDefId> + HasResolver) -> Self {
+ let interner = DbInterner::new_with(db, None, None);
let ty = db.ty(def.into());
let substs = TyBuilder::unknown_subst(
db,
match def.into() {
TyDefId::AdtId(it) => GenericDefId::AdtId(it),
TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it),
- TyDefId::BuiltinType(_) => return Type::new(db, def, ty.skip_binders().clone()),
+ TyDefId::BuiltinType(_) => {
+ return Type::new(db, def, ty.skip_binder().to_chalk(interner));
+ }
},
);
- Type::new(db, def, ty.substitute(Interner, &substs))
+ let args: hir_ty::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ Type::new(db, def, ty.instantiate(interner, args).to_chalk(interner))
}
fn from_def_placeholders(
db: &'db dyn HirDatabase,
def: impl Into<TyDefId> + HasResolver,
) -> Self {
+ let interner = DbInterner::new_with(db, None, None);
let ty = db.ty(def.into());
let substs = TyBuilder::placeholder_subst(
db,
match def.into() {
TyDefId::AdtId(it) => GenericDefId::AdtId(it),
TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it),
- TyDefId::BuiltinType(_) => return Type::new(db, def, ty.skip_binders().clone()),
+ TyDefId::BuiltinType(_) => {
+ return Type::new(db, def, ty.skip_binder().to_chalk(interner));
+ }
},
);
- Type::new(db, def, ty.substitute(Interner, &substs))
+ let args: hir_ty::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ Type::new(db, def, ty.instantiate(interner, args).to_chalk(interner))
}
fn from_value_def(
@@ -4869,6 +4925,7 @@
let Some(ty) = db.value_ty(def.into()) else {
return Type::new(db, def, TyKind::Error.intern(Interner));
};
+ let interner = DbInterner::new_with(db, None, None);
let substs = TyBuilder::unknown_subst(
db,
match def.into() {
@@ -4879,10 +4936,13 @@
ValueTyDefId::EnumVariantId(it) => {
GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent))
}
- ValueTyDefId::StaticId(_) => return Type::new(db, def, ty.skip_binders().clone()),
+ ValueTyDefId::StaticId(_) => {
+ return Type::new(db, def, ty.skip_binder().to_chalk(interner));
+ }
},
);
- Type::new(db, def, ty.substitute(Interner, &substs))
+ let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ Type::new(db, def, ty.instantiate(interner, args).to_chalk(interner))
}
pub fn new_slice(ty: Self) -> Self {
@@ -5177,7 +5237,14 @@
.build();
let goal = Canonical {
- value: hir_ty::InEnvironment::new(&self.env.env, trait_ref.cast(Interner)),
+ value: hir_ty::InEnvironment::new(
+ &self.env.env.to_chalk(DbInterner::new_with(
+ db,
+ Some(self.env.krate),
+ self.env.block,
+ )),
+ trait_ref.cast(Interner),
+ ),
binders: CanonicalVarKinds::empty(Interner),
};
@@ -5951,7 +6018,7 @@
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct TypeNs<'db> {
- env: Arc<TraitEnvironment>,
+ env: Arc<TraitEnvironment<'db>>,
ty: hir_ty::next_solver::Ty<'db>,
_pd: PhantomCovariantLifetime<'db>,
}
@@ -5969,6 +6036,11 @@
TypeNs { env: environment, ty, _pd: PhantomCovariantLifetime::new() }
}
+ pub fn to_type(&self, db: &'db dyn HirDatabase) -> Type<'db> {
+ let interner = DbInterner::new_with(db, Some(self.env.krate), self.env.block);
+ Type { env: self.env.clone(), ty: convert_ty_for_result(interner, self.ty), _pd: self._pd }
+ }
+
// FIXME: Find better API that also handles const generics
pub fn impls_trait(&self, infcx: InferCtxt<'db>, trait_: Trait, args: &[TypeNs<'db>]) -> bool {
let args = GenericArgs::new_from_iter(
@@ -5992,6 +6064,10 @@
let res = hir_ty::traits::next_trait_solve_in_ctxt(&infcx, goal);
res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes))
}
+
+ pub fn is_bool(&self) -> bool {
+ matches!(self.ty.kind(), rustc_type_ir::TyKind::Bool)
+ }
}
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
@@ -6098,7 +6174,7 @@
}
pub fn align(&self) -> u64 {
- self.0.align.abi.bytes()
+ self.0.align.bytes()
}
pub fn niches(&self) -> Option<u128> {
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 539b253..c6b7e84 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -46,6 +46,10 @@
from_assoc_type_id,
lang_items::lang_items_for_bin_op,
method_resolution,
+ next_solver::{
+ DbInterner,
+ mapping::{ChalkToNextSolver, NextSolverToChalk},
+ },
};
use intern::sym;
use itertools::Itertools;
@@ -219,7 +223,7 @@
})
}
- fn trait_environment(&self, db: &'db dyn HirDatabase) -> Arc<TraitEnvironment> {
+ fn trait_environment(&self, db: &'db dyn HirDatabase) -> Arc<TraitEnvironment<'db>> {
self.body_().map(|(def, ..)| def).map_or_else(
|| TraitEnvironment::empty(self.resolver.krate()),
|def| db.trait_environment_for_body(def),
@@ -372,8 +376,10 @@
) -> Option<Callable<'db>> {
let expr_id = self.expr_id(call.clone().into())?.as_expr()?;
let (func, substs) = self.infer()?.method_resolution(expr_id)?;
- let ty = db.value_ty(func.into())?.substitute(Interner, &substs);
- let ty = Type::new_with_resolver(db, &self.resolver, ty);
+ let interner = DbInterner::new_with(db, None, None);
+ let args: hir_ty::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner);
+ let ty = db.value_ty(func.into())?.instantiate(interner, args);
+ let ty = Type::new_with_resolver(db, &self.resolver, ty.to_chalk(interner));
let mut res = ty.as_callable(db)?;
res.is_bound_method = true;
Some(res)
diff --git a/crates/ide-assists/src/handlers/add_braces.rs b/crates/ide-assists/src/handlers/add_braces.rs
index 745ae67..5af622e 100644
--- a/crates/ide-assists/src/handlers/add_braces.rs
+++ b/crates/ide-assists/src/handlers/add_braces.rs
@@ -1,3 +1,4 @@
+use either::Either;
use syntax::{
AstNode,
ast::{self, edit_in_place::Indent, syntax_factory::SyntaxFactory},
@@ -59,7 +60,8 @@
}
fn get_replacement_node(ctx: &AssistContext<'_>) -> Option<(ParentType, ast::Expr)> {
- if let Some(match_arm) = ctx.find_node_at_offset::<ast::MatchArm>() {
+ let node = ctx.find_node_at_offset::<Either<ast::MatchArm, ast::ClosureExpr>>()?;
+ if let Either::Left(match_arm) = &node {
let match_arm_expr = match_arm.expr()?;
if matches!(match_arm_expr, ast::Expr::BlockExpr(_)) {
@@ -67,7 +69,7 @@
}
return Some((ParentType::MatchArmExpr, match_arm_expr));
- } else if let Some(closure_expr) = ctx.find_node_at_offset::<ast::ClosureExpr>() {
+ } else if let Either::Right(closure_expr) = &node {
let body = closure_expr.body()?;
if matches!(body, ast::Expr::BlockExpr(_)) {
@@ -106,6 +108,33 @@
}
#[test]
+ fn suggest_add_braces_for_closure_in_match() {
+ check_assist(
+ add_braces,
+ r#"
+fn foo() {
+ match () {
+ () => {
+ t(|n|$0 n + 100);
+ }
+ }
+}
+"#,
+ r#"
+fn foo() {
+ match () {
+ () => {
+ t(|n| {
+ n + 100
+ });
+ }
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn no_assist_for_closures_with_braces() {
check_assist_not_applicable(
add_braces,
diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index 4d3212c..3910921 100644
--- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -521,7 +521,7 @@
hir::StructKind::Tuple => {
let mut name_generator = suggest_name::NameGenerator::default();
let pats = fields.into_iter().map(|f| {
- let name = name_generator.for_type(&f.ty(db), db, edition);
+ let name = name_generator.for_type(&f.ty(db).to_type(db), db, edition);
match name {
Some(name) => make::ext::simple_ident_pat(make.name(&name)).into(),
None => make.wildcard_pat().into(),
diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs
index 753a9e5..53a0a11 100644
--- a/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -6,7 +6,7 @@
syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
};
use syntax::{
- SyntaxKind, T,
+ NodeOrToken, SyntaxKind, T,
ast::{
self, AstNode,
Expr::BinExpr,
@@ -38,15 +38,27 @@
// }
// ```
pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- let mut bin_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
+ let mut bin_expr = if let Some(not) = ctx.find_token_syntax_at_offset(T![!])
+ && let Some(NodeOrToken::Node(next)) = not.next_sibling_or_token()
+ && let Some(paren) = ast::ParenExpr::cast(next)
+ && let Some(ast::Expr::BinExpr(bin_expr)) = paren.expr()
+ {
+ bin_expr
+ } else {
+ let bin_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
+ let op_range = bin_expr.op_token()?.text_range();
+
+ // Is the cursor on the expression's logical operator?
+ if !op_range.contains_range(ctx.selection_trimmed()) {
+ return None;
+ }
+
+ bin_expr
+ };
+
let op = bin_expr.op_kind()?;
let op_range = bin_expr.op_token()?.text_range();
- // Is the cursor on the expression's logical operator?
- if !op_range.contains_range(ctx.selection_trimmed()) {
- return None;
- }
-
// Walk up the tree while we have the same binary operator
while let Some(parent_expr) = bin_expr.syntax().parent().and_then(ast::BinExpr::cast) {
match parent_expr.op_kind() {
@@ -367,6 +379,15 @@
}
#[test]
+ fn demorgan_on_not() {
+ check_assist(
+ apply_demorgan,
+ "fn f() { $0!(1 || 3 && 4 || 5) }",
+ "fn f() { !1 && !(3 && 4) && !5 }",
+ )
+ }
+
+ #[test]
fn demorgan_keep_pars_for_op_precedence() {
check_assist(
apply_demorgan,
diff --git a/crates/ide-assists/src/handlers/bind_unused_param.rs b/crates/ide-assists/src/handlers/bind_unused_param.rs
index 00c7d25..1b24f7f 100644
--- a/crates/ide-assists/src/handlers/bind_unused_param.rs
+++ b/crates/ide-assists/src/handlers/bind_unused_param.rs
@@ -2,7 +2,7 @@
use ide_db::{LineIndexDatabase, assists::AssistId, defs::Definition};
use syntax::{
AstNode,
- ast::{self, edit_in_place::Indent},
+ ast::{self, HasName, edit_in_place::Indent},
};
// Assist: bind_unused_param
@@ -22,6 +22,7 @@
let param: ast::Param = ctx.find_node_at_offset()?;
let Some(ast::Pat::IdentPat(ident_pat)) = param.pat() else { return None };
+ let name = ident_pat.name().filter(|n| !n.text().starts_with('_'))?;
let param_def = {
let local = ctx.sema.to_def(&ident_pat)?;
@@ -39,14 +40,14 @@
acc.add(
AssistId::quick_fix("bind_unused_param"),
- format!("Bind as `let _ = {ident_pat};`"),
+ format!("Bind as `let _ = {name};`"),
param.syntax().text_range(),
|builder| {
let line_index = ctx.db().line_index(ctx.vfs_file_id());
let indent = func.indent_level();
let text_indent = indent + 1;
- let mut text = format!("\n{text_indent}let _ = {ident_pat};");
+ let mut text = format!("\n{text_indent}let _ = {name};");
let left_line = line_index.line_col(l_curly_range.end()).line;
let right_line = line_index.line_col(r_curly_range.start()).line;
@@ -84,6 +85,22 @@
}
#[test]
+ fn bind_unused_ref_ident_pat() {
+ cov_mark::check!(single_line);
+ check_assist(
+ bind_unused_param,
+ r#"
+fn foo(ref $0y: i32) {}
+"#,
+ r#"
+fn foo(ref y: i32) {
+ let _ = y;
+}
+"#,
+ );
+ }
+
+ #[test]
fn bind_unused_empty_block_with_newline() {
check_assist(
bind_unused_param,
@@ -152,4 +169,14 @@
"#,
);
}
+
+ #[test]
+ fn keep_underscore_used() {
+ check_assist_not_applicable(
+ bind_unused_param,
+ r#"
+fn foo($0_x: i32, y: i32) {}
+"#,
+ );
+ }
}
diff --git a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
index 2ea032f..82213ae 100644
--- a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
+++ b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
@@ -1,13 +1,12 @@
use std::iter::once;
-use ide_db::{
- syntax_helpers::node_ext::{is_pattern_cond, single_let},
- ty_filter::TryEnum,
-};
+use either::Either;
+use hir::{Semantics, TypeInfo};
+use ide_db::{RootDatabase, ty_filter::TryEnum};
use syntax::{
AstNode,
- SyntaxKind::{FN, FOR_EXPR, LOOP_EXPR, WHILE_EXPR, WHITESPACE},
- T,
+ SyntaxKind::{CLOSURE_EXPR, FN, FOR_EXPR, LOOP_EXPR, WHILE_EXPR, WHITESPACE},
+ SyntaxNode, T,
ast::{
self,
edit::{AstNodeEdit, IndentLevel},
@@ -44,12 +43,9 @@
// }
// ```
pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- if let Some(let_stmt) = ctx.find_node_at_offset() {
- let_stmt_to_guarded_return(let_stmt, acc, ctx)
- } else if let Some(if_expr) = ctx.find_node_at_offset() {
- if_expr_to_guarded_return(if_expr, acc, ctx)
- } else {
- None
+ match ctx.find_node_at_offset::<Either<ast::LetStmt, ast::IfExpr>>()? {
+ Either::Left(let_stmt) => let_stmt_to_guarded_return(let_stmt, acc, ctx),
+ Either::Right(if_expr) => if_expr_to_guarded_return(if_expr, acc, ctx),
}
}
@@ -73,13 +69,7 @@
return None;
}
- // Check if there is an IfLet that we can handle.
- let (if_let_pat, cond_expr) = if is_pattern_cond(cond.clone()) {
- let let_ = single_let(cond)?;
- (Some(let_.pat()?), let_.expr()?)
- } else {
- (None, cond)
- };
+ let let_chains = flat_let_chain(cond);
let then_block = if_expr.then_branch()?;
let then_block = then_block.stmt_list()?;
@@ -106,11 +96,7 @@
let parent_container = parent_block.syntax().parent()?;
- let early_expression: ast::Expr = match parent_container.kind() {
- WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make::expr_continue(None),
- FN => make::expr_return(None),
- _ => return None,
- };
+ let early_expression: ast::Expr = early_expression(parent_container, &ctx.sema)?;
then_block.syntax().first_child_or_token().map(|t| t.kind() == T!['{'])?;
@@ -132,32 +118,42 @@
target,
|edit| {
let if_indent_level = IndentLevel::from_node(if_expr.syntax());
- let replacement = match if_let_pat {
- None => {
- // If.
- let new_expr = {
- let then_branch =
- make::block_expr(once(make::expr_stmt(early_expression).into()), None);
- let cond = invert_boolean_expression_legacy(cond_expr);
- make::expr_if(cond, then_branch, None).indent(if_indent_level)
- };
- new_expr.syntax().clone()
- }
- Some(pat) => {
+ let replacement = let_chains.into_iter().map(|expr| {
+ if let ast::Expr::LetExpr(let_expr) = &expr
+ && let (Some(pat), Some(expr)) = (let_expr.pat(), let_expr.expr())
+ {
// If-let.
let let_else_stmt = make::let_else_stmt(
pat,
None,
- cond_expr,
- ast::make::tail_only_block_expr(early_expression),
+ expr,
+ ast::make::tail_only_block_expr(early_expression.clone()),
);
let let_else_stmt = let_else_stmt.indent(if_indent_level);
let_else_stmt.syntax().clone()
+ } else {
+ // If.
+ let new_expr = {
+ let then_branch = make::block_expr(
+ once(make::expr_stmt(early_expression.clone()).into()),
+ None,
+ );
+ let cond = invert_boolean_expression_legacy(expr);
+ make::expr_if(cond, then_branch, None).indent(if_indent_level)
+ };
+ new_expr.syntax().clone()
}
- };
+ });
+ let newline = &format!("\n{if_indent_level}");
let then_statements = replacement
- .children_with_tokens()
+ .enumerate()
+ .flat_map(|(i, node)| {
+ (i != 0)
+ .then(|| make::tokens::whitespace(newline).into())
+ .into_iter()
+ .chain(node.children_with_tokens())
+ })
.chain(
then_block_items
.syntax()
@@ -201,11 +197,7 @@
let_stmt.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
let parent_container = parent_block.syntax().parent()?;
- match parent_container.kind() {
- WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make::expr_continue(None),
- FN => make::expr_return(None),
- _ => return None,
- }
+ early_expression(parent_container, &ctx.sema)?
};
acc.add(
@@ -232,6 +224,54 @@
)
}
+fn early_expression(
+ parent_container: SyntaxNode,
+ sema: &Semantics<'_, RootDatabase>,
+) -> Option<ast::Expr> {
+ let return_none_expr = || {
+ let none_expr = make::expr_path(make::ext::ident_path("None"));
+ make::expr_return(Some(none_expr))
+ };
+ if let Some(fn_) = ast::Fn::cast(parent_container.clone())
+ && let Some(fn_def) = sema.to_def(&fn_)
+ && let Some(TryEnum::Option) = TryEnum::from_ty(sema, &fn_def.ret_type(sema.db))
+ {
+ return Some(return_none_expr());
+ }
+ if let Some(body) = ast::ClosureExpr::cast(parent_container.clone()).and_then(|it| it.body())
+ && let Some(ret_ty) = sema.type_of_expr(&body).map(TypeInfo::original)
+ && let Some(TryEnum::Option) = TryEnum::from_ty(sema, &ret_ty)
+ {
+ return Some(return_none_expr());
+ }
+
+ Some(match parent_container.kind() {
+ WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make::expr_continue(None),
+ FN | CLOSURE_EXPR => make::expr_return(None),
+ _ => return None,
+ })
+}
+
+fn flat_let_chain(mut expr: ast::Expr) -> Vec<ast::Expr> {
+ let mut chains = vec![];
+
+ while let ast::Expr::BinExpr(bin_expr) = &expr
+ && bin_expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And))
+ && let (Some(lhs), Some(rhs)) = (bin_expr.lhs(), bin_expr.rhs())
+ {
+ if let Some(last) = chains.pop_if(|last| !matches!(last, ast::Expr::LetExpr(_))) {
+ chains.push(make::expr_bin_op(rhs, ast::BinaryOp::LogicOp(ast::LogicOp::And), last));
+ } else {
+ chains.push(rhs);
+ }
+ expr = lhs;
+ }
+
+ chains.push(expr);
+ chains.reverse();
+ chains
+}
+
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
@@ -269,6 +309,71 @@
}
#[test]
+ fn convert_inside_fn_return_option() {
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+//- minicore: option
+fn ret_option() -> Option<()> {
+ bar();
+ if$0 true {
+ foo();
+
+ // comment
+ bar();
+ }
+}
+"#,
+ r#"
+fn ret_option() -> Option<()> {
+ bar();
+ if false {
+ return None;
+ }
+ foo();
+
+ // comment
+ bar();
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn convert_inside_closure() {
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+fn main() {
+ let _f = || {
+ bar();
+ if$0 true {
+ foo();
+
+ // comment
+ bar();
+ }
+ }
+}
+"#,
+ r#"
+fn main() {
+ let _f = || {
+ bar();
+ if false {
+ return;
+ }
+ foo();
+
+ // comment
+ bar();
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn convert_let_inside_fn() {
check_assist(
convert_to_guarded_return,
@@ -317,6 +422,82 @@
}
#[test]
+ fn convert_if_let_result_inside_let() {
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+fn main() {
+ let _x = loop {
+ if$0 let Ok(x) = Err(92) {
+ foo(x);
+ }
+ };
+}
+"#,
+ r#"
+fn main() {
+ let _x = loop {
+ let Ok(x) = Err(92) else { continue };
+ foo(x);
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn convert_if_let_chain_result() {
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+fn main() {
+ if$0 let Ok(x) = Err(92)
+ && x < 30
+ && let Some(y) = Some(8)
+ {
+ foo(x, y);
+ }
+}
+"#,
+ r#"
+fn main() {
+ let Ok(x) = Err(92) else { return };
+ if x >= 30 {
+ return;
+ }
+ let Some(y) = Some(8) else { return };
+ foo(x, y);
+}
+"#,
+ );
+
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+fn main() {
+ if$0 let Ok(x) = Err(92)
+ && x < 30
+ && y < 20
+ && let Some(y) = Some(8)
+ {
+ foo(x, y);
+ }
+}
+"#,
+ r#"
+fn main() {
+ let Ok(x) = Err(92) else { return };
+ if !(x < 30 && y < 20) {
+ return;
+ }
+ let Some(y) = Some(8) else { return };
+ foo(x, y);
+}
+"#,
+ );
+ }
+
+ #[test]
fn convert_let_ok_inside_fn() {
check_assist(
convert_to_guarded_return,
@@ -561,6 +742,32 @@
}
#[test]
+ fn convert_let_stmt_inside_fn_return_option() {
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+//- minicore: option
+fn foo() -> Option<i32> {
+ None
+}
+
+fn ret_option() -> Option<i32> {
+ let x$0 = foo();
+}
+"#,
+ r#"
+fn foo() -> Option<i32> {
+ None
+}
+
+fn ret_option() -> Option<i32> {
+ let Some(x) = foo() else { return None };
+}
+"#,
+ );
+ }
+
+ #[test]
fn convert_let_stmt_inside_loop() {
check_assist(
convert_to_guarded_return,
diff --git a/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/crates/ide-assists/src/handlers/destructure_struct_binding.rs
index 397327c..27755db 100644
--- a/crates/ide-assists/src/handlers/destructure_struct_binding.rs
+++ b/crates/ide-assists/src/handlers/destructure_struct_binding.rs
@@ -7,7 +7,7 @@
search::{FileReference, SearchScope},
};
use itertools::Itertools;
-use syntax::ast::syntax_factory::SyntaxFactory;
+use syntax::ast::{HasName, syntax_factory::SyntaxFactory};
use syntax::syntax_editor::SyntaxEditor;
use syntax::{AstNode, Edition, SmolStr, SyntaxNode, ToSmolStr, ast};
@@ -71,13 +71,14 @@
struct StructEditData {
ident_pat: ast::IdentPat,
+ name: ast::Name,
kind: hir::StructKind,
struct_def_path: hir::ModPath,
visible_fields: Vec<hir::Field>,
usages: Vec<FileReference>,
names_in_scope: FxHashSet<SmolStr>,
has_private_members: bool,
- is_nested: bool,
+ need_record_field_name: bool,
is_ref: bool,
edition: Edition,
}
@@ -114,7 +115,11 @@
}
let is_ref = ty.is_reference();
- let is_nested = ident_pat.syntax().parent().and_then(ast::RecordPatField::cast).is_some();
+ let need_record_field_name = ident_pat
+ .syntax()
+ .parent()
+ .and_then(ast::RecordPatField::cast)
+ .is_some_and(|field| field.colon_token().is_none());
let usages = ctx
.sema
@@ -133,6 +138,7 @@
let names_in_scope = get_names_in_scope(ctx, &ident_pat, &usages).unwrap_or_default();
Some(StructEditData {
+ name: ident_pat.name()?,
ident_pat,
kind,
struct_def_path,
@@ -140,7 +146,7 @@
has_private_members,
visible_fields,
names_in_scope,
- is_nested,
+ need_record_field_name,
is_ref,
edition: module.krate().edition(ctx.db()),
})
@@ -177,6 +183,7 @@
field_names: &[(SmolStr, SmolStr)],
) {
let ident_pat = &data.ident_pat;
+ let name = &data.name;
let struct_path = mod_path_to_ast(&data.struct_def_path, data.edition);
let is_ref = ident_pat.ref_token().is_some();
@@ -194,9 +201,9 @@
hir::StructKind::Record => {
let fields = field_names.iter().map(|(old_name, new_name)| {
// Use shorthand syntax if possible
- if old_name == new_name && !is_mut {
+ if old_name == new_name {
make.record_pat_field_shorthand(
- make.ident_pat(false, false, make.name(old_name)).into(),
+ make.ident_pat(is_ref, is_mut, make.name(old_name)).into(),
)
} else {
make.record_pat_field(
@@ -215,8 +222,8 @@
// If the binding is nested inside a record, we need to wrap the new
// destructured pattern in a non-shorthand record field
- let destructured_pat = if data.is_nested {
- make.record_pat_field(make.name_ref(&ident_pat.to_string()), new_pat).syntax().clone()
+ let destructured_pat = if data.need_record_field_name {
+ make.record_pat_field(make.name_ref(&name.to_string()), new_pat).syntax().clone()
} else {
new_pat.syntax().clone()
};
@@ -288,7 +295,7 @@
Some(field_expr) => Some({
let field_name: SmolStr = field_expr.name_ref()?.to_string().into();
let new_field_name = field_names.get(&field_name)?;
- let new_expr = make.expr_path(ast::make::ext::ident_path(new_field_name));
+ let new_expr = ast::make::expr_path(ast::make::ext::ident_path(new_field_name));
// If struct binding is a reference, we might need to deref field usages
if data.is_ref {
@@ -298,7 +305,7 @@
ref_data.wrap_expr(new_expr).syntax().clone_for_update(),
)
} else {
- (field_expr.syntax().clone(), new_expr.syntax().clone())
+ (field_expr.syntax().clone(), new_expr.syntax().clone_for_update())
}
}),
None => Some((
@@ -579,7 +586,7 @@
struct Foo { bar: i32, baz: i32 }
fn main() {
- let Foo { bar: mut bar, baz: mut baz } = Foo { bar: 1, baz: 2 };
+ let Foo { mut bar, mut baz } = Foo { bar: 1, baz: 2 };
let bar2 = bar;
let baz2 = &baz;
}
@@ -588,6 +595,86 @@
}
#[test]
+ fn mut_record_field() {
+ check_assist(
+ destructure_struct_binding,
+ r#"
+ struct Foo { x: () }
+ struct Bar { foo: Foo }
+ fn f(Bar { mut $0foo }: Bar) {}
+ "#,
+ r#"
+ struct Foo { x: () }
+ struct Bar { foo: Foo }
+ fn f(Bar { foo: Foo { mut x } }: Bar) {}
+ "#,
+ )
+ }
+
+ #[test]
+ fn ref_record_field() {
+ check_assist(
+ destructure_struct_binding,
+ r#"
+ struct Foo { x: () }
+ struct Bar { foo: Foo }
+ fn f(Bar { ref $0foo }: Bar) {
+ let _ = foo.x;
+ }
+ "#,
+ r#"
+ struct Foo { x: () }
+ struct Bar { foo: Foo }
+ fn f(Bar { foo: Foo { ref x } }: Bar) {
+ let _ = *x;
+ }
+ "#,
+ )
+ }
+
+ #[test]
+ fn ref_mut_record_field() {
+ check_assist(
+ destructure_struct_binding,
+ r#"
+ struct Foo { x: () }
+ struct Bar { foo: Foo }
+ fn f(Bar { ref mut $0foo }: Bar) {
+ let _ = foo.x;
+ }
+ "#,
+ r#"
+ struct Foo { x: () }
+ struct Bar { foo: Foo }
+ fn f(Bar { foo: Foo { ref mut x } }: Bar) {
+ let _ = *x;
+ }
+ "#,
+ )
+ }
+
+ #[test]
+ fn ref_mut_record_renamed_field() {
+ check_assist(
+ destructure_struct_binding,
+ r#"
+ struct Foo { x: () }
+ struct Bar { foo: Foo }
+ fn f(Bar { foo: ref mut $0foo1 }: Bar) {
+ let _ = foo1.x;
+ }
+ "#,
+ r#"
+ struct Foo { x: () }
+ struct Bar { foo: Foo }
+ fn f(Bar { foo: Foo { ref mut x } }: Bar) {
+ let _ = *x;
+ }
+ "#,
+ )
+ }
+
+ #[test]
fn mut_ref() {
check_assist(
destructure_struct_binding,
@@ -611,6 +698,52 @@
}
#[test]
+ fn ref_not_add_parenthesis_and_deref_record() {
+ check_assist(
+ destructure_struct_binding,
+ r#"
+ struct Foo { bar: i32, baz: i32 }
+
+ fn main() {
+ let $0foo = &Foo { bar: 1, baz: 2 };
+ let _ = &foo.bar;
+ }
+ "#,
+ r#"
+ struct Foo { bar: i32, baz: i32 }
+
+ fn main() {
+ let Foo { bar, baz } = &Foo { bar: 1, baz: 2 };
+ let _ = bar;
+ }
+ "#,
+ )
+ }
+
+ #[test]
+ fn ref_not_add_parenthesis_and_deref_tuple() {
+ check_assist(
+ destructure_struct_binding,
+ r#"
+ struct Foo(i32, i32);
+
+ fn main() {
+ let $0foo = &Foo(1, 2);
+ let _ = &foo.0;
+ }
+ "#,
+ r#"
+ struct Foo(i32, i32);
+
+ fn main() {
+ let Foo(_0, _1) = &Foo(1, 2);
+ let _ = _0;
+ }
+ "#,
+ )
+ }
+
+ #[test]
fn record_struct_name_collision() {
check_assist(
destructure_struct_binding,
diff --git a/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/crates/ide-assists/src/handlers/expand_rest_pattern.rs
index c80b78f..b746099 100644
--- a/crates/ide-assists/src/handlers/expand_rest_pattern.rs
+++ b/crates/ide-assists/src/handlers/expand_rest_pattern.rs
@@ -24,7 +24,7 @@
// struct Bar { y: Y, z: Z }
//
// fn foo(bar: Bar) {
-// let Bar { y, z } = bar;
+// let Bar { y, z } = bar;
// }
// ```
fn expand_record_rest_pattern(
@@ -53,18 +53,17 @@
|builder| {
let make = SyntaxFactory::with_mappings();
let mut editor = builder.make_editor(rest_pat.syntax());
- let new_field_list = make.record_pat_field_list(old_field_list.fields(), None);
- for (f, _) in missing_fields.iter() {
- let field = make.record_pat_field_shorthand(
+ let new_fields = old_field_list.fields().chain(missing_fields.iter().map(|(f, _)| {
+ make.record_pat_field_shorthand(
make.ident_pat(
false,
false,
make.name(&f.name(ctx.sema.db).display_no_db(edition).to_smolstr()),
)
.into(),
- );
- new_field_list.add_field(field);
- }
+ )
+ }));
+ let new_field_list = make.record_pat_field_list(new_fields, None);
editor.replace(old_field_list.syntax(), new_field_list.syntax());
@@ -114,9 +113,7 @@
};
let rest_pat = rest_pat.into();
- let mut pats = pat.fields();
- let prefix_count = pats.by_ref().position(|p| p == rest_pat)?;
- let suffix_count = pats.count();
+ let (prefix_count, suffix_count) = calculate_counts(&rest_pat, pat.fields())?;
if fields.len().saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
cov_mark::hit!(no_missing_fields_tuple_struct);
@@ -142,16 +139,13 @@
pat.fields()
.take(prefix_count)
.chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| {
- make.ident_pat(
- false,
- false,
- match name_gen.for_type(&f.ty(ctx.sema.db), ctx.sema.db, ctx.edition())
- {
- Some(name) => make.name(&name),
- None => make.name(&format!("_{}", f.index())),
- },
+ gen_unnamed_pat(
+ ctx,
+ &make,
+ &mut name_gen,
+ &f.ty(ctx.db()).to_type(ctx.sema.db),
+ f.index(),
)
- .into()
}))
.chain(pat.fields().skip(prefix_count + 1)),
);
@@ -164,6 +158,134 @@
)
}
+// Assist: expand_tuple_rest_pattern
+//
+// Fills fields by replacing rest pattern in tuple patterns.
+//
+// ```
+// fn foo(bar: (char, i32, i32)) {
+// let (ch, ..$0) = bar;
+// }
+// ```
+// ->
+// ```
+// fn foo(bar: (char, i32, i32)) {
+// let (ch, _1, _2) = bar;
+// }
+// ```
+fn expand_tuple_rest_pattern(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_>,
+ pat: ast::TuplePat,
+ rest_pat: ast::RestPat,
+) -> Option<()> {
+ let fields = ctx.sema.type_of_pat(&pat.clone().into())?.original.tuple_fields(ctx.db());
+ let len = fields.len();
+
+ let rest_pat = rest_pat.into();
+ let (prefix_count, suffix_count) = calculate_counts(&rest_pat, pat.fields())?;
+
+ if len.saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
+ cov_mark::hit!(no_missing_fields_tuple);
+ return None;
+ }
+
+ let old_range = ctx.sema.original_range_opt(pat.syntax())?;
+ if old_range.file_id != ctx.file_id() {
+ return None;
+ }
+
+ acc.add(
+ AssistId::refactor_rewrite("expand_tuple_rest_pattern"),
+ "Fill tuple fields",
+ rest_pat.syntax().text_range(),
+ |builder| {
+ let make = SyntaxFactory::with_mappings();
+ let mut editor = builder.make_editor(rest_pat.syntax());
+
+ let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
+ let new_pat = make.tuple_pat(
+ pat.fields()
+ .take(prefix_count)
+ .chain(fields[prefix_count..len - suffix_count].iter().enumerate().map(
+ |(index, ty)| {
+ gen_unnamed_pat(ctx, &make, &mut name_gen, ty, prefix_count + index)
+ },
+ ))
+ .chain(pat.fields().skip(prefix_count + 1)),
+ );
+
+ editor.replace(pat.syntax(), new_pat.syntax());
+
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
+ },
+ )
+}
+
+// Assist: expand_slice_rest_pattern
+//
+// Fills fields by replacing rest pattern in slice patterns.
+//
+// ```
+// fn foo(bar: [i32; 3]) {
+// let [first, ..$0] = bar;
+// }
+// ```
+// ->
+// ```
+// fn foo(bar: [i32; 3]) {
+// let [first, _1, _2] = bar;
+// }
+// ```
+fn expand_slice_rest_pattern(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_>,
+ pat: ast::SlicePat,
+ rest_pat: ast::RestPat,
+) -> Option<()> {
+ let (ty, len) = ctx.sema.type_of_pat(&pat.clone().into())?.original.as_array(ctx.db())?;
+
+ let rest_pat = rest_pat.into();
+ let (prefix_count, suffix_count) = calculate_counts(&rest_pat, pat.pats())?;
+
+ if len.saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
+ cov_mark::hit!(no_missing_fields_slice);
+ return None;
+ }
+
+ let old_range = ctx.sema.original_range_opt(pat.syntax())?;
+ if old_range.file_id != ctx.file_id() {
+ return None;
+ }
+
+ acc.add(
+ AssistId::refactor_rewrite("expand_slice_rest_pattern"),
+ "Fill slice fields",
+ rest_pat.syntax().text_range(),
+ |builder| {
+ let make = SyntaxFactory::with_mappings();
+ let mut editor = builder.make_editor(rest_pat.syntax());
+
+ let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
+ let new_pat = make.slice_pat(
+ pat.pats()
+ .take(prefix_count)
+ .chain(
+ (prefix_count..len - suffix_count)
+ .map(|index| gen_unnamed_pat(ctx, &make, &mut name_gen, &ty, index)),
+ )
+ .chain(pat.pats().skip(prefix_count + 1)),
+ );
+
+ editor.replace(pat.syntax(), new_pat.syntax());
+
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
+ },
+ )
+}
+
pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let rest_pat = ctx.find_node_at_offset::<ast::RestPat>()?;
let parent = rest_pat.syntax().parent()?;
@@ -171,15 +293,40 @@
match parent {
ast::RecordPatFieldList(it) => expand_record_rest_pattern(acc, ctx, it.syntax().parent().and_then(ast::RecordPat::cast)?, rest_pat),
ast::TupleStructPat(it) => expand_tuple_struct_rest_pattern(acc, ctx, it, rest_pat),
- // FIXME
- // ast::TuplePat(it) => (),
- // FIXME
- // ast::SlicePat(it) => (),
+ ast::TuplePat(it) => expand_tuple_rest_pattern(acc, ctx, it, rest_pat),
+ ast::SlicePat(it) => expand_slice_rest_pattern(acc, ctx, it, rest_pat),
_ => None,
}
}
}
+fn gen_unnamed_pat(
+ ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
+ name_gen: &mut NameGenerator,
+ ty: &hir::Type<'_>,
+ index: usize,
+) -> ast::Pat {
+ make.ident_pat(
+ false,
+ false,
+ match name_gen.for_type(ty, ctx.sema.db, ctx.edition()) {
+ Some(name) => make.name(&name),
+ None => make.name(&format!("_{index}")),
+ },
+ )
+ .into()
+}
+
+fn calculate_counts(
+ rest_pat: &ast::Pat,
+ mut pats: ast::AstChildren<ast::Pat>,
+) -> Option<(usize, usize)> {
+ let prefix_count = pats.by_ref().position(|p| p == *rest_pat)?;
+ let suffix_count = pats.count();
+ Some((prefix_count, suffix_count))
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -211,7 +358,7 @@
fn bar(foo: Foo) {
match foo {
Foo::A(_) => false,
- Foo::B{ y, z } => true,
+ Foo::B{ y, z } => true,
};
}
"#,
@@ -272,7 +419,7 @@
}
fn foo(bar: Bar) {
- let Bar { y, z } = bar;
+ let Bar { y, z } = bar;
}
"#,
);
@@ -350,6 +497,79 @@
}
#[test]
+ fn fill_tuple_with_fields() {
+ check_assist(
+ expand_rest_pattern,
+ r#"
+fn foo(bar: (char, i32, i32)) {
+ let (ch, ..$0) = bar;
+}
+"#,
+ r#"
+fn foo(bar: (char, i32, i32)) {
+ let (ch, _1, _2) = bar;
+}
+"#,
+ );
+ check_assist(
+ expand_rest_pattern,
+ r#"
+fn foo(bar: (char, i32, i32)) {
+ let (ch, ..$0, end) = bar;
+}
+"#,
+ r#"
+fn foo(bar: (char, i32, i32)) {
+ let (ch, _1, end) = bar;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn fill_array_with_fields() {
+ check_assist(
+ expand_rest_pattern,
+ r#"
+fn foo(bar: [i32; 4]) {
+ let [first, ..$0] = bar;
+}
+"#,
+ r#"
+fn foo(bar: [i32; 4]) {
+ let [first, _1, _2, _3] = bar;
+}
+"#,
+ );
+ check_assist(
+ expand_rest_pattern,
+ r#"
+fn foo(bar: [i32; 4]) {
+ let [first, second, ..$0] = bar;
+}
+"#,
+ r#"
+fn foo(bar: [i32; 4]) {
+ let [first, second, _2, _3] = bar;
+}
+"#,
+ );
+ check_assist(
+ expand_rest_pattern,
+ r#"
+fn foo(bar: [i32; 4]) {
+ let [first, second, ..$0, end] = bar;
+}
+"#,
+ r#"
+fn foo(bar: [i32; 4]) {
+ let [first, second, _2, end] = bar;
+}
+"#,
+ );
+ }
+
+ #[test]
fn fill_fields_struct_generated_by_macro() {
check_assist(
expand_rest_pattern,
@@ -376,7 +596,7 @@
position!(usize);
fn macro_call(pos: Pos) {
- let Pos { x, y } = pos;
+ let Pos { x, y } = pos;
}
"#,
);
@@ -420,7 +640,7 @@
fn macro_call(foo: Foo) {
match foo {
Foo::A(_) => false,
- Foo::B{ x, y } => true,
+ Foo::B{ x, y } => true,
}
}
"#,
@@ -484,6 +704,8 @@
// This is still possible even though it's meaningless
cov_mark::check!(no_missing_fields);
cov_mark::check!(no_missing_fields_tuple_struct);
+ cov_mark::check!(no_missing_fields_tuple);
+ cov_mark::check!(no_missing_fields_slice);
check_assist_not_applicable(
expand_rest_pattern,
r#"
@@ -523,5 +745,21 @@
}
"#,
);
+ check_assist_not_applicable(
+ expand_rest_pattern,
+ r#"
+fn foo(bar: (i32, i32)) {
+ let (y, ..$0, z) = bar;
+}
+"#,
+ );
+ check_assist_not_applicable(
+ expand_rest_pattern,
+ r#"
+fn foo(bar: [i32; 2]) {
+ let [y, ..$0, z] = bar;
+}
+"#,
+ );
}
}
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index bd88e8b..da59626 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -285,7 +285,7 @@
/// In general that's true for any expression, but in some cases that would produce invalid code.
fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
match node.kind() {
- SyntaxKind::PATH_EXPR | SyntaxKind::LOOP_EXPR => None,
+ SyntaxKind::PATH_EXPR | SyntaxKind::LOOP_EXPR | SyntaxKind::LET_EXPR => None,
SyntaxKind::BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()),
SyntaxKind::RETURN_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()),
SyntaxKind::BLOCK_EXPR => {
@@ -1404,6 +1404,25 @@
}
#[test]
+ fn extract_var_let_expr() {
+ check_assist_by_label(
+ extract_variable,
+ r#"
+fn main() {
+ if $0let$0 Some(x) = Some(2+2) {}
+}
+"#,
+ r#"
+fn main() {
+ let $0var_name = Some(2+2);
+ if let Some(x) = var_name {}
+}
+"#,
+ "Extract into variable",
+ );
+ }
+
+ #[test]
fn extract_var_for_cast() {
check_assist_by_label(
extract_variable,
@@ -1739,6 +1758,14 @@
}
#[test]
+ fn extract_var_for_let_expr_not_applicable() {
+ check_assist_not_applicable(
+ extract_variable,
+ "fn main() { if $0let Some(x) = Some(2+2) {} }",
+ );
+ }
+
+ #[test]
fn extract_var_unit_expr_not_applicable() {
check_assist_not_applicable(
extract_variable,
diff --git a/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs b/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
index 6198dbc..056edb0 100644
--- a/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
+++ b/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
@@ -39,6 +39,9 @@
cov_mark::hit!(test_gen_default_on_non_unit_variant_not_implemented);
return None;
}
+ if !variant.syntax().text_range().contains_range(ctx.selection_trimmed()) {
+ return None;
+ }
if existing_default_impl(&ctx.sema, &variant).is_some() {
cov_mark::hit!(test_gen_default_impl_already_exists);
@@ -115,6 +118,49 @@
}
#[test]
+ fn test_generate_default_selected_variant() {
+ check_assist(
+ generate_default_from_enum_variant,
+ r#"
+//- minicore: default
+enum Variant {
+ Undefined,
+ $0Minor$0,
+ Major,
+}
+"#,
+ r#"
+enum Variant {
+ Undefined,
+ Minor,
+ Major,
+}
+
+impl Default for Variant {
+ fn default() -> Self {
+ Self::Minor
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_generate_default_not_applicable_with_multiple_variant_selection() {
+ check_assist_not_applicable(
+ generate_default_from_enum_variant,
+ r#"
+//- minicore: default
+enum Variant {
+ Undefined,
+ $0Minor,
+ M$0ajor,
+}
+"#,
+ );
+ }
+
+ #[test]
fn test_generate_default_already_implemented() {
cov_mark::check!(test_gen_default_impl_already_exists);
check_assist_not_applicable(
diff --git a/crates/ide-assists/src/handlers/invert_if.rs b/crates/ide-assists/src/handlers/invert_if.rs
index d198870..7576d2f 100644
--- a/crates/ide-assists/src/handlers/invert_if.rs
+++ b/crates/ide-assists/src/handlers/invert_if.rs
@@ -125,6 +125,18 @@
}
#[test]
+ fn invert_if_doesnt_apply_with_if_let_chain() {
+ check_assist_not_applicable(
+ invert_if,
+ "fn f() { i$0f x && let Some(_) = Some(1) { 1 } else { 0 } }",
+ );
+ check_assist_not_applicable(
+ invert_if,
+ "fn f() { i$0f let Some(_) = Some(1) && x { 1 } else { 0 } }",
+ );
+ }
+
+ #[test]
fn invert_if_option_case() {
check_assist(
invert_if,
diff --git a/crates/ide-assists/src/handlers/pull_assignment_up.rs b/crates/ide-assists/src/handlers/pull_assignment_up.rs
index 21debf6..00902fa 100644
--- a/crates/ide-assists/src/handlers/pull_assignment_up.rs
+++ b/crates/ide-assists/src/handlers/pull_assignment_up.rs
@@ -53,6 +53,10 @@
};
let tgt: ast::Expr = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() {
+ let if_expr = std::iter::successors(Some(if_expr), |it| {
+ it.syntax().parent().and_then(ast::IfExpr::cast)
+ })
+ .last()?;
collector.collect_if(&if_expr)?;
if_expr.into()
} else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() {
@@ -238,6 +242,37 @@
}
#[test]
+ fn test_pull_assignment_up_inner_if() {
+ check_assist(
+ pull_assignment_up,
+ r#"
+fn foo() {
+ let mut a = 1;
+
+ if true {
+ a = 2;
+ } else if true {
+ $0a = 3;
+ } else {
+ a = 4;
+ }
+}"#,
+ r#"
+fn foo() {
+ let mut a = 1;
+
+ a = if true {
+ 2
+ } else if true {
+ 3
+ } else {
+ 4
+ };
+}"#,
+ );
+ }
+
+ #[test]
fn test_pull_assignment_up_match() {
check_assist(
pull_assignment_up,
diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs
index 414f674..08779a3 100644
--- a/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -83,7 +83,9 @@
let input_expressions = input_expressions
.into_iter()
.filter_map(|(is_sep, group)| (!is_sep).then_some(group))
- .map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join(""), Edition::CURRENT))
+ .map(|tokens| tokens.collect::<Vec<_>>())
+ .filter(|tokens| !tokens.iter().all(|it| it.kind().is_trivia()))
+ .map(|tokens| syntax::hacks::parse_expr_from_str(&tokens.iter().join(""), Edition::CURRENT))
.collect::<Option<Vec<ast::Expr>>>()?;
let parent = macro_expr.syntax().parent()?;
@@ -268,6 +270,8 @@
dbg!('x');
dbg!(&n);
dbg!(n);
+ dbg!(n,);
+ dbg!(n, );
// needless comment
dbg!("foo");$0
}
@@ -282,6 +286,17 @@
}
#[test]
+ fn test_remove_trailing_comma_dbg() {
+ check("$0dbg!(1 + 1,)", "1 + 1");
+ check("$0dbg!(1 + 1, )", "1 + 1");
+ check("$0dbg!(1 + 1,\n)", "1 + 1");
+ check("$0dbg!(1 + 1, 2 + 3)", "(1 + 1, 2 + 3)");
+ check("$0dbg!(1 + 1, 2 + 3 )", "(1 + 1, 2 + 3)");
+ check("$0dbg!(1 + 1, 2 + 3, )", "(1 + 1, 2 + 3)");
+ check("$0dbg!(1 + 1, 2 + 3 ,)", "(1 + 1, 2 + 3)");
+ }
+
+ #[test]
fn test_remove_dbg_not_applicable() {
check_assist_not_applicable(remove_dbg, "fn main() {$0vec![1, 2, 3]}");
check_assist_not_applicable(remove_dbg, "fn main() {$0dbg(5, 6, 7)}");
diff --git a/crates/ide-assists/src/handlers/replace_arith_op.rs b/crates/ide-assists/src/handlers/replace_arith_op.rs
index 440ab4d..a3fb851 100644
--- a/crates/ide-assists/src/handlers/replace_arith_op.rs
+++ b/crates/ide-assists/src/handlers/replace_arith_op.rs
@@ -1,7 +1,7 @@
use ide_db::assists::{AssistId, GroupLabel};
use syntax::{
- AstNode, TextRange,
- ast::{self, ArithOp, BinaryOp},
+ AstNode,
+ ast::{self, ArithOp, BinaryOp, syntax_factory::SyntaxFactory},
};
use crate::assist_context::{AssistContext, Assists};
@@ -71,24 +71,31 @@
fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> Option<()> {
let (lhs, op, rhs) = parse_binary_op(ctx)?;
+ let op_expr = lhs.syntax().parent()?;
if !is_primitive_int(ctx, &lhs) || !is_primitive_int(ctx, &rhs) {
return None;
}
- let start = lhs.syntax().text_range().start();
- let end = rhs.syntax().text_range().end();
- let range = TextRange::new(start, end);
-
acc.add_group(
&GroupLabel("Replace arithmetic...".into()),
kind.assist_id(),
kind.label(),
- range,
+ op_expr.text_range(),
|builder| {
+ let mut edit = builder.make_editor(rhs.syntax());
+ let make = SyntaxFactory::with_mappings();
let method_name = kind.method_name(op);
- builder.replace(range, format!("{lhs}.{method_name}({rhs})"))
+ let needs_parentheses =
+ lhs.precedence().needs_parentheses_in(ast::prec::ExprPrecedence::Postfix);
+ let receiver = if needs_parentheses { make.expr_paren(lhs).into() } else { lhs };
+ let arith_expr =
+ make.expr_method_call(receiver, make.name_ref(&method_name), make.arg_list([rhs]));
+ edit.replace(op_expr, arith_expr.syntax());
+
+ edit.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.vfs_file_id(), edit);
},
)
}
@@ -228,6 +235,23 @@
}
#[test]
+ fn replace_arith_with_wrapping_add_add_parenthesis() {
+ check_assist(
+ replace_arith_with_wrapping,
+ r#"
+fn main() {
+ let x = 1*x $0+ 2;
+}
+"#,
+ r#"
+fn main() {
+ let x = (1*x).wrapping_add(2);
+}
+"#,
+ )
+ }
+
+ #[test]
fn replace_arith_not_applicable_with_non_empty_selection() {
check_assist_not_applicable(
replace_arith_with_checked,
diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
index dd24437..3b815a4 100644
--- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
@@ -328,7 +328,14 @@
(pat, pat2) => match (binds_name(sema, &pat), binds_name(sema, &pat2)) {
(true, true) => return None,
(true, false) => (pat, guard, expr, expr2),
- (false, true) => (pat2, guard2, expr2, expr),
+ (false, true) => {
+ // This pattern triggers an invalid transformation.
+ // See issues #11373, #19443
+ if let ast::Pat::IdentPat(_) = pat2 {
+ return None;
+ }
+ (pat2, guard2, expr2, expr)
+ }
_ if is_sad_pat(sema, &pat) => (pat2, guard2, expr2, expr),
(false, false) => (pat, guard, expr, expr2),
},
@@ -1892,4 +1899,19 @@
"#,
)
}
+
+ #[test]
+ fn test_replace_match_with_if_let_not_applicable_pat2_is_ident_pat() {
+ check_assist_not_applicable(
+ replace_match_with_if_let,
+ r"
+fn test(a: i32) {
+ match$0 a {
+ 1 => code(),
+ other => code(other),
+ }
+}
+",
+ )
+ }
}
diff --git a/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
index 5ef8ba4..f507cae 100644
--- a/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
+++ b/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
@@ -31,6 +31,9 @@
ast::Expr::MethodCallExpr(call) => call,
_ => return None,
};
+ if ctx.offset() > if_expr.then_branch()?.stmt_list()?.l_curly_token()?.text_range().end() {
+ return None;
+ }
let name_ref = call_expr.name_ref()?;
match name_ref.text().as_str() {
@@ -191,4 +194,19 @@
"#,
);
}
+
+ #[test]
+ fn replace_is_some_with_if_let_some_not_applicable_after_l_curly() {
+ check_assist_not_applicable(
+ replace_is_method_with_if_let_method,
+ r#"
+fn main() {
+ let x = Some(1);
+ if x.is_some() {
+ ()$0
+ }
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 91348be..e7f91ff 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -1035,7 +1035,41 @@
struct Bar { y: Y, z: Z }
fn foo(bar: Bar) {
- let Bar { y, z } = bar;
+ let Bar { y, z } = bar;
+}
+"#####,
+ )
+}
+
+#[test]
+fn doctest_expand_slice_rest_pattern() {
+ check_doc_test(
+ "expand_slice_rest_pattern",
+ r#####"
+fn foo(bar: [i32; 3]) {
+ let [first, ..$0] = bar;
+}
+"#####,
+ r#####"
+fn foo(bar: [i32; 3]) {
+ let [first, _1, _2] = bar;
+}
+"#####,
+ )
+}
+
+#[test]
+fn doctest_expand_tuple_rest_pattern() {
+ check_doc_test(
+ "expand_tuple_rest_pattern",
+ r#####"
+fn foo(bar: (char, i32, i32)) {
+ let (ch, ..$0) = bar;
+}
+"#####,
+ r#####"
+fn foo(bar: (char, i32, i32)) {
+ let (ch, _1, _2) = bar;
}
"#####,
)
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index e36e0e5..eb2bb31 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -691,6 +691,9 @@
NameKind::RecordField => {
field::complete_field_list_record_variant(acc, ctx);
}
+ NameKind::TypeParam => {
+ acc.add_keyword_snippet(ctx, "const", "const $1: $0");
+ }
NameKind::ConstParam
| NameKind::Enum
| NameKind::MacroDef
@@ -700,7 +703,6 @@
| NameKind::Static
| NameKind::Struct
| NameKind::Trait
- | NameKind::TypeParam
| NameKind::Union
| NameKind::Variant => (),
}
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs
index c542e14..e174b0c 100644
--- a/crates/ide-completion/src/completions/attribute.rs
+++ b/crates/ide-completion/src/completions/attribute.rs
@@ -70,7 +70,7 @@
lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints);
}
- ["cfg"] => cfg::complete_cfg(acc, ctx),
+ ["cfg"] | ["cfg_attr"] => cfg::complete_cfg(acc, ctx),
["macro_use"] => macro_use::complete_macro_use(
acc,
ctx,
diff --git a/crates/ide-completion/src/completions/attribute/cfg.rs b/crates/ide-completion/src/completions/attribute/cfg.rs
index 1676a84..b2e8efd 100644
--- a/crates/ide-completion/src/completions/attribute/cfg.rs
+++ b/crates/ide-completion/src/completions/attribute/cfg.rs
@@ -53,15 +53,33 @@
acc.add(item.build(ctx.db));
}),
},
- None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| {
- let s = s.as_str();
- let item =
- CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s, ctx.edition);
- acc.add(item.build(ctx.db));
- }),
+ None => ctx
+ .krate
+ .potential_cfg(ctx.db)
+ .get_cfg_keys()
+ .unique()
+ .map(|s| (s.as_str(), ""))
+ .chain(CFG_CONDITION.iter().copied())
+ .for_each(|(s, snippet)| {
+ let mut item = CompletionItem::new(
+ SymbolKind::BuiltinAttr,
+ ctx.source_range(),
+ s,
+ ctx.edition,
+ );
+ if let Some(cap) = ctx.config.snippet_cap
+ && !snippet.is_empty()
+ {
+ item.insert_snippet(cap, snippet);
+ }
+ acc.add(item.build(ctx.db));
+ }),
}
}
+const CFG_CONDITION: &[(&str, &str)] =
+ &[("all", "all($0)"), ("any", "any($0)"), ("not", "not($0)")];
+
const KNOWN_ARCH: [&str; 20] = [
"aarch64",
"arm",
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index a7df0ab..080875e 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -59,6 +59,7 @@
in_block_expr,
in_breakable,
after_if_expr,
+ before_else_kw,
in_condition,
incomplete_let,
after_incomplete_let,
@@ -386,7 +387,7 @@
add_keyword("let", "let $1 = $0;");
}
- if after_if_expr || after_incomplete_let {
+ if !before_else_kw && (after_if_expr || after_incomplete_let) {
add_keyword("else", "else {\n $0\n}");
}
diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs
index 36f38a7..2f5abd1 100644
--- a/crates/ide-completion/src/completions/record.rs
+++ b/crates/ide-completion/src/completions/record.rs
@@ -28,7 +28,11 @@
record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some();
match were_fields_specified {
- false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
+ false => un
+ .fields(ctx.db)
+ .into_iter()
+ .map(|f| (f, f.ty(ctx.db).to_type(ctx.db)))
+ .collect(),
true => return,
}
}
@@ -56,7 +60,11 @@
record_expr.record_expr_field_list().and_then(|fl| fl.fields().next()).is_some();
match were_fields_specified {
- false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
+ false => un
+ .fields(ctx.db)
+ .into_iter()
+ .map(|f| (f, f.ty(ctx.db).to_type(ctx.db)))
+ .collect(),
true => return,
}
}
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 0074756..9deaaf6 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -144,6 +144,7 @@
pub(crate) in_block_expr: bool,
pub(crate) in_breakable: BreakableKind,
pub(crate) after_if_expr: bool,
+ pub(crate) before_else_kw: bool,
/// Whether this expression is the direct condition of an if or while expression
pub(crate) in_condition: bool,
pub(crate) incomplete_let: bool,
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index b33a547..77a9440 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -1,6 +1,7 @@
//! Module responsible for analyzing the code surrounding the cursor for completion.
use std::iter;
+use base_db::salsa;
use hir::{ExpandResult, InFile, Semantics, Type, TypeInfo, Variant};
use ide_db::{RootDatabase, active_parameter::ActiveParameter};
use itertools::Either;
@@ -85,9 +86,15 @@
let original_offset = expansion.original_offset + relative_offset;
let token = expansion.original_file.token_at_offset(original_offset).left_biased()?;
- analyze(sema, expansion, original_token, &token).map(|(analysis, expected, qualifier_ctx)| {
- AnalysisResult { analysis, expected, qualifier_ctx, token, original_offset }
- })
+ salsa::attach(sema.db, || analyze(sema, expansion, original_token, &token)).map(
+ |(analysis, expected, qualifier_ctx)| AnalysisResult {
+ analysis,
+ expected,
+ qualifier_ctx,
+ token,
+ original_offset,
+ },
+ )
}
fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Option<SyntaxToken> {
@@ -637,6 +644,9 @@
.or_else(|| it.rhs().and_then(|rhs| sema.type_of_expr(&rhs)))
.map(TypeInfo::original);
(ty, None)
+ } else if let Some(ast::BinaryOp::LogicOp(_)) = it.op_kind() {
+ let ty = sema.type_of_expr(&it.clone().into()).map(TypeInfo::original);
+ (ty, None)
} else {
(None, None)
}
@@ -707,9 +717,13 @@
(ty, None)
},
ast::IfExpr(it) => {
- let ty = it.condition()
- .and_then(|e| sema.type_of_expr(&e))
- .map(TypeInfo::original);
+ let ty = if let Some(body) = it.then_branch()
+ && token.text_range().end() > body.syntax().text_range().start()
+ {
+ sema.type_of_expr(&body.into())
+ } else {
+ it.condition().and_then(|e| sema.type_of_expr(&e))
+ }.map(TypeInfo::original);
(ty, None)
},
ast::IdentPat(it) => {
@@ -1282,11 +1296,12 @@
let after_incomplete_let = after_incomplete_let(it.clone()).is_some();
let incomplete_expr_stmt =
it.parent().and_then(ast::ExprStmt::cast).map(|it| it.semicolon_token().is_none());
+ let before_else_kw = before_else_kw(it);
let incomplete_let = it
.parent()
.and_then(ast::LetStmt::cast)
.is_some_and(|it| it.semicolon_token().is_none())
- || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw(it);
+ || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw;
let in_value = it.parent().and_then(Either::<ast::LetStmt, ast::ArgList>::cast).is_some();
let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax());
@@ -1302,6 +1317,7 @@
in_block_expr,
in_breakable: in_loop_body,
after_if_expr,
+ before_else_kw,
in_condition,
ref_expr_parent,
after_amp,
diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs
index 445afa7..d9ec791 100644
--- a/crates/ide-completion/src/context/tests.rs
+++ b/crates/ide-completion/src/context/tests.rs
@@ -279,6 +279,62 @@
}
#[test]
+fn expected_type_if_let_chain_bool() {
+ check_expected_type_and_name(
+ r#"
+fn foo() {
+ let f = Foo::Quux;
+ if let c = f && $0 { }
+}
+"#,
+ expect![[r#"ty: bool, name: ?"#]],
+ );
+}
+
+#[test]
+fn expected_type_if_condition() {
+ check_expected_type_and_name(
+ r#"
+fn foo() {
+ if a$0 { }
+}
+"#,
+ expect![[r#"ty: bool, name: ?"#]],
+ );
+}
+
+#[test]
+fn expected_type_if_body() {
+ check_expected_type_and_name(
+ r#"
+enum Foo { Bar, Baz, Quux }
+
+fn foo() {
+ let _: Foo = if true {
+ $0
+ };
+}
+"#,
+ expect![[r#"ty: Foo, name: ?"#]],
+ );
+
+ check_expected_type_and_name(
+ r#"
+enum Foo { Bar, Baz, Quux }
+
+fn foo() {
+ let _: Foo = if true {
+ Foo::Bar
+ } else {
+ $0
+ };
+}
+"#,
+ expect![[r#"ty: Foo, name: ?"#]],
+ );
+}
+
+#[test]
fn expected_type_fn_ret_without_leading_char() {
cov_mark::check!(expected_type_fn_ret_without_leading_char);
check_expected_type_and_name(
@@ -526,3 +582,16 @@
expect![[r#"ty: State, name: ?"#]],
);
}
+
+#[test]
+fn expected_type_logic_op() {
+ check_expected_type_and_name(
+ r#"
+enum State { Stop }
+fn foo() {
+ true && $0;
+}
+"#,
+ expect![[r#"ty: bool, name: ?"#]],
+ );
+}
diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs
index 60ec112..312d3bd 100644
--- a/crates/ide-completion/src/render/pattern.rs
+++ b/crates/ide-completion/src/render/pattern.rs
@@ -163,6 +163,7 @@
PatternContext {
param_ctx: Some(ParamContext { kind: ParamKind::Function(_), .. }),
has_type_ascription: false,
+ parent_pat: None,
..
}
);
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs
index 809a26b..b20b570 100644
--- a/crates/ide-completion/src/tests.rs
+++ b/crates/ide-completion/src/tests.rs
@@ -26,6 +26,7 @@
use base_db::{SourceDatabase, salsa};
use expect_test::Expect;
+use hir::db::HirDatabase;
use hir::{PrefixKind, setup_tracing};
use ide_db::{
FilePosition, RootDatabase, SnippetCap,
@@ -306,8 +307,11 @@
trigger_character: Option<char>,
) -> Vec<CompletionItem> {
let (db, position) = position(code);
- let res = salsa::attach(&db, || crate::completions(&db, &config, position, trigger_character))
- .map_or_else(Vec::default, Into::into);
+ let res = salsa::attach(&db, || {
+ HirDatabase::zalsa_register_downcaster(&db);
+ crate::completions(&db, &config, position, trigger_character)
+ })
+ .map_or_else(Vec::default, Into::into);
// validate
res.iter().for_each(|it| {
let sr = it.source_range;
diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs
index 46a3630..1d2a9c7 100644
--- a/crates/ide-completion/src/tests/attribute.rs
+++ b/crates/ide-completion/src/tests/attribute.rs
@@ -815,7 +815,10 @@
#[cfg($0)]
"#,
expect![[r#"
+ ba all
+ ba any
ba dbg
+ ba not
ba opt_level
ba test
ba true
@@ -827,7 +830,74 @@
#[cfg(b$0)]
"#,
expect![[r#"
+ ba all
+ ba any
ba dbg
+ ba not
+ ba opt_level
+ ba test
+ ba true
+ "#]],
+ );
+ }
+
+ #[test]
+ fn inside_cfg_attr() {
+ check(
+ r#"
+//- /main.rs cfg:test,dbg=false,opt_level=2
+#[cfg_attr($0)]
+"#,
+ expect![[r#"
+ ba all
+ ba any
+ ba dbg
+ ba not
+ ba opt_level
+ ba test
+ ba true
+ "#]],
+ );
+ check(
+ r#"
+//- /main.rs cfg:test,dbg=false,opt_level=2
+#[cfg_attr(b$0)]
+"#,
+ expect![[r#"
+ ba all
+ ba any
+ ba dbg
+ ba not
+ ba opt_level
+ ba test
+ ba true
+ "#]],
+ );
+ check(
+ r#"
+//- /main.rs cfg:test,dbg=false,opt_level=2
+#[cfg_attr($0, allow(deprecated))]
+"#,
+ expect![[r#"
+ ba all
+ ba any
+ ba dbg
+ ba not
+ ba opt_level
+ ba test
+ ba true
+ "#]],
+ );
+ check(
+ r#"
+//- /main.rs cfg:test,dbg=false,opt_level=2
+#[cfg_attr(b$0, allow(deprecated))]
+"#,
+ expect![[r#"
+ ba all
+ ba any
+ ba dbg
+ ba not
ba opt_level
ba test
ba true
@@ -852,6 +922,20 @@
"#]],
);
}
+
+ #[test]
+ fn inside_conditional() {
+ check_edit(
+ "all",
+ r#"
+//- /main.rs cfg:test,dbg=false,opt_level=2
+#[cfg($0)]
+"#,
+ r#"
+#[cfg(all($0))]
+"#,
+ );
+ }
}
mod derive {
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index 5cc72ef..98a6f95 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -271,8 +271,6 @@
sn macro_rules
sn pd
sn ppd
- ex false
- ex true
"#]],
)
}
@@ -1668,12 +1666,138 @@
fn foo() { let x = if foo {} $0 else {}; }
"#,
expect![[r#"
- fn foo fn()
+ fn foo() fn()
bt u32 u32
kw async
kw const
kw crate::
- kw else
+ kw else if
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw impl for
+ kw let
+ kw letm
+ 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
+ "#]],
+ );
+ check(
+ r#"
+fn foo() { let x = if foo {} $0 else if true {}; }
+"#,
+ expect![[r#"
+ fn foo() fn()
+ bt u32 u32
+ kw async
+ kw const
+ kw crate::
+ kw else if
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw impl for
+ kw let
+ kw letm
+ 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
+ "#]],
+ );
+ check(
+ r#"
+fn foo() { let x = if foo {} el$0 else if true {} else {}; }
+"#,
+ expect![[r#"
+ fn foo() fn()
+ lc x ()
+ bt u32 u32
+ kw async
+ kw const
+ kw crate::
+ kw else if
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw impl for
+ kw let
+ kw letm
+ 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
+ "#]],
+ );
+ check(
+ r#"
+fn foo() { let x = if foo {} $0 else if true {} else {}; }
+"#,
+ expect![[r#"
+ fn foo() fn()
+ bt u32 u32
+ kw async
+ kw const
+ kw crate::
kw else if
kw enum
kw extern
diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs
index 9ec2725..6eb0b81 100644
--- a/crates/ide-completion/src/tests/pattern.rs
+++ b/crates/ide-completion/src/tests/pattern.rs
@@ -399,6 +399,25 @@
}
#[test]
+fn completes_in_fn_param_in_nested_pattern() {
+ check(
+ r#"
+struct Foo { num: u32 }
+struct Bar(Foo);
+fn foo(Bar($0)) {}
+"#,
+ expect![[r#"
+ st Bar
+ st Foo
+ bn Bar(…) Bar($1)$0
+ bn Foo {…} Foo { num$1 }$0
+ kw mut
+ kw ref
+ "#]],
+ )
+}
+
+#[test]
fn completes_in_closure_param() {
check(
r#"
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index 84ddff8..c438ca7 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -1510,3 +1510,28 @@
"#]],
);
}
+
+#[test]
+fn fn_generic_params_const_param_snippet() {
+ check_edit("const", "fn foo<c$0>() {}", "fn foo<const $1: $0>() {}");
+ check_edit("const", "fn foo<T, c$0>() {}", "fn foo<T, const $1: $0>() {}");
+ check(
+ r#"
+fn foo<T: $0>() {}
+"#,
+ expect![[r#"
+ kw crate::
+ kw self::
+ "#]],
+ );
+ check(
+ r#"
+fn foo<const N: $0>() {}
+"#,
+ expect![[r#"
+ bt u32 u32
+ kw crate::
+ kw self::
+ "#]],
+ );
+}
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index c7e2d05..125e11e 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -429,18 +429,18 @@
impl Tr<$0
"#,
expect![[r#"
- en Enum Enum
- ma makro!(…) macro_rules! makro
+ en Enum Enum
+ ma makro!(…) macro_rules! makro
md module
- sp Self dyn Tr<{unknown}>
- st Record Record
- st S S
- st Tuple Tuple
- st Unit Unit
+ sp Self dyn Tr<{unknown}> + 'static
+ st Record Record
+ st S S
+ st Tuple Tuple
+ st Unit Unit
tt Tr
tt Trait
- un Union Union
- bt u32 u32
+ un Union Union
+ bt u32 u32
kw crate::
kw self::
"#]],
diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs
index cefd8fd..e1d1407 100644
--- a/crates/ide-db/src/syntax_helpers/node_ext.rs
+++ b/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -265,10 +265,7 @@
ast::Expr::BinExpr(expr)
if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) =>
{
- expr.lhs()
- .map(is_pattern_cond)
- .or_else(|| expr.rhs().map(is_pattern_cond))
- .unwrap_or(false)
+ expr.lhs().map_or(false, is_pattern_cond) || expr.rhs().map_or(false, is_pattern_cond)
}
ast::Expr::ParenExpr(expr) => expr.expr().is_some_and(is_pattern_cond),
ast::Expr::LetExpr(_) => true,
diff --git a/crates/ide-db/src/syntax_helpers/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs
index 995bf72..2e03665 100644
--- a/crates/ide-db/src/syntax_helpers/suggest_name.rs
+++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs
@@ -473,7 +473,7 @@
frange.range,
"selection is not an expression(yet contained in one)"
);
- let name = NameGenerator::default().for_variable(&expr, &sema);
+ let name = salsa::attach(sema.db, || NameGenerator::default().for_variable(&expr, &sema));
assert_eq!(&name, expected);
}
diff --git a/crates/ide-diagnostics/src/handlers/missing_lifetime.rs b/crates/ide-diagnostics/src/handlers/missing_lifetime.rs
index 76b3074..b07f9e6 100644
--- a/crates/ide-diagnostics/src/handlers/missing_lifetime.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_lifetime.rs
@@ -88,4 +88,16 @@
"#,
);
}
+
+ #[test]
+ fn fn_traits() {
+ check_diagnostics(
+ r#"
+//- minicore: fn
+struct WithLifetime<'a>(&'a ());
+
+fn foo<T: Fn(WithLifetime) -> WithLifetime>() {}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 4bb6474..029ed18 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -442,6 +442,49 @@
}
#[test]
+ fn raw_deref_on_union_field() {
+ check_diagnostics(
+ r#"
+fn main() {
+
+ union U {
+ a: u8
+ }
+ let x = U { a: 3 };
+
+ let a = &raw mut x.a;
+
+ union U1 {
+ a: u8
+ }
+ let x = U1 { a: 3 };
+
+ let a = x.a;
+ // ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block
+
+
+ let b = &raw const x.a;
+
+ let tmp = Vec::from([1, 2, 3]);
+
+ let c = &raw const tmp[x.a];
+ // ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block
+
+ union URef {
+ p: &'static mut i32,
+ }
+
+ fn deref_union_field(u: URef) {
+ // Not an assignment but an access to the union field!
+ *(u.p) = 13;
+ // ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
fn unsafe_expr_as_an_argument_of_a_method_call() {
check_fix(
r#"
diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs
index e6bbff0..84e63ac 100644
--- a/crates/ide-diagnostics/src/handlers/unused_variables.rs
+++ b/crates/ide-diagnostics/src/handlers/unused_variables.rs
@@ -6,7 +6,7 @@
label::Label,
source_change::SourceChange,
};
-use syntax::{Edition, TextRange};
+use syntax::{AstNode, Edition, TextRange, ToSmolStr};
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
@@ -24,15 +24,21 @@
}
let diagnostic_range = ctx.sema.diagnostics_display_range(ast);
// The range for the Actual Name. We don't want to replace the entire declaration. Using the diagnostic range causes issues within in Array Destructuring.
- let name_range = d
- .local
- .primary_source(ctx.sema.db)
+ let primary_source = d.local.primary_source(ctx.sema.db);
+ let name_range = primary_source
.name()
.map(|v| v.syntax().original_file_range_rooted(ctx.sema.db))
.filter(|it| {
Some(it.file_id) == ast.file_id.file_id()
&& diagnostic_range.range.contains_range(it.range)
});
+ let is_shorthand_field = primary_source
+ .source
+ .value
+ .left()
+ .and_then(|name| name.syntax().parent())
+ .and_then(syntax::ast::RecordPatField::cast)
+ .is_some_and(|field| field.colon_token().is_none());
let var_name = d.local.name(ctx.sema.db);
Some(
Diagnostic::new_with_syntax_node_ptr(
@@ -48,6 +54,7 @@
it.range,
diagnostic_range,
ast.file_id.is_macro(),
+ is_shorthand_field,
ctx.edition,
)
})),
@@ -60,24 +67,24 @@
name_range: TextRange,
diagnostic_range: FileRange,
is_in_marco: bool,
+ is_shorthand_field: bool,
edition: Edition,
) -> Option<Vec<Assist>> {
if is_in_marco {
return None;
}
+ let name = var_name.display(db, edition).to_smolstr();
+ let name = name.strip_prefix("r#").unwrap_or(&name);
+ let new_name = if is_shorthand_field { format!("{name}: _{name}") } else { format!("_{name}") };
Some(vec![Assist {
id: AssistId::quick_fix("unscore_unused_variable_name"),
- label: Label::new(format!(
- "Rename unused {} to _{}",
- var_name.display(db, edition),
- var_name.display(db, edition)
- )),
+ label: Label::new(format!("Rename unused {name} to {new_name}")),
group: None,
target: diagnostic_range.range,
source_change: Some(SourceChange::from_text_edit(
diagnostic_range.file_id,
- TextEdit::replace(name_range, format!("_{}", var_name.display(db, edition))),
+ TextEdit::replace(name_range, new_name),
)),
command: None,
}])
@@ -220,13 +227,26 @@
fn main() {
let f = Foo { f1: 0, f2: 0 };
match f {
- Foo { _f1, f2 } => {
+ Foo { f1: _f1, f2 } => {
_ = f2;
}
}
}
"#,
);
+
+ check_fix(
+ r#"
+fn main() {
+ let $0r#type = 2;
+}
+"#,
+ r#"
+fn main() {
+ let _type = 2;
+}
+"#,
+ );
}
#[test]
@@ -263,6 +283,46 @@
);
}
+ #[test]
+ fn unused_variable_in_record_field() {
+ check_fix(
+ r#"
+struct S { field : u32 }
+fn main() {
+ let s = S { field : 2 };
+ let S { field: $0x } = s
+}
+"#,
+ r#"
+struct S { field : u32 }
+fn main() {
+ let s = S { field : 2 };
+ let S { field: _x } = s
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unused_variable_in_shorthand_record_field() {
+ check_fix(
+ r#"
+struct S { field : u32 }
+fn main() {
+ let s = S { field : 2 };
+ let S { $0field } = s
+}
+"#,
+ r#"
+struct S { field : u32 }
+fn main() {
+ let s = S { field : 2 };
+ let S { field: _field } = s
+}
+"#,
+ );
+ }
+
// regression test as we used to panic in this scenario
#[test]
fn unknown_struct_pattern_param_type() {
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index ffd144a..ae208fe 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -88,7 +88,7 @@
ast::Pat(it) => sema.type_of_pat(&it)?.original,
ast::SelfParam(it) => sema.type_of_self(&it)?,
ast::Type(it) => sema.resolve_type(&it)?,
- ast::RecordField(it) => sema.to_def(&it)?.ty(db),
+ ast::RecordField(it) => sema.to_def(&it)?.ty(db).to_type(db),
// can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise
ast::NameRef(it) => {
if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) {
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 03b9b36..c4fb6d1 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -440,7 +440,7 @@
Definition::Local(it) => Some(it.ty(db)),
Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)),
Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)),
- Definition::Field(field) => Some(field.ty(db)),
+ Definition::Field(field) => Some(field.ty(db).to_type(db)),
Definition::TupleField(it) => Some(it.ty(db)),
Definition::Function(it) => Some(it.ty(db)),
Definition::Adt(it) => Some(it.ty(db)),
@@ -602,7 +602,7 @@
let ty = match def {
Definition::Local(it) => Some(it.ty(db)),
- Definition::Field(field) => Some(field.ty(db)),
+ Definition::Field(field) => Some(field.ty(db).to_type(db)),
Definition::TupleField(field) => Some(field.ty(db)),
Definition::Const(it) => Some(it.ty(db)),
Definition::Static(it) => Some(it.ty(db)),
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 65375ed..c5d695c 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -692,14 +692,14 @@
}
let drop_info = match def {
Definition::Field(field) => {
- DropInfo { drop_glue: field.ty(db).drop_glue(db), has_dtor: None }
+ DropInfo { drop_glue: field.ty(db).to_type(db).drop_glue(db), has_dtor: None }
}
Definition::Adt(Adt::Struct(strukt)) => {
let struct_drop_glue = strukt.ty_placeholders(db).drop_glue(db);
let mut fields_drop_glue = strukt
.fields(db)
.iter()
- .map(|field| field.ty(db).drop_glue(db))
+ .map(|field| field.ty(db).to_type(db).drop_glue(db))
.max()
.unwrap_or(DropGlue::None);
let has_dtor = match (fields_drop_glue, struct_drop_glue) {
@@ -727,7 +727,7 @@
variant
.fields(db)
.iter()
- .map(|field| field.ty(db).drop_glue(db))
+ .map(|field| field.ty(db).to_type(db).drop_glue(db))
.max()
.unwrap_or(DropGlue::None)
})
@@ -742,7 +742,7 @@
let fields_drop_glue = variant
.fields(db)
.iter()
- .map(|field| field.ty(db).drop_glue(db))
+ .map(|field| field.ty(db).to_type(db).drop_glue(db))
.max()
.unwrap_or(DropGlue::None);
DropInfo { drop_glue: fields_drop_glue, has_dtor: None }
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 1ea11a2..8bc0b3f 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -4797,6 +4797,48 @@
}
#[test]
+fn const_generic_negative_literal_macro_expansion() {
+ // Test that negative literals work correctly in const generics
+ // when used through macro expansion. This ensures the transcriber
+ // doesn't wrap negative literals in parentheses, which would create
+ // invalid syntax like Foo::<(-1)> instead of Foo::<-1>.
+ check(
+ r#"
+struct Foo<const I: i16> {
+ pub value: i16,
+}
+
+impl<const I: i16> Foo<I> {
+ pub fn new(value: i16) -> Self {
+ Self { value }
+ }
+}
+
+macro_rules! create_foo {
+ ($val:expr) => {
+ Foo::<$val>::new($val)
+ };
+}
+
+fn main() {
+ let v$0alue = create_foo!(-1);
+}
+"#,
+ expect![[r#"
+ *value*
+
+ ```rust
+ let value: Foo<-1>
+ ```
+
+ ---
+
+ size = 2, align = 2, no Drop
+ "#]],
+ );
+}
+
+#[test]
fn hover_self_param_shows_type() {
check(
r#"
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index 104740c..b7c1241 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -380,7 +380,7 @@
let foo = foo4();
// ^^^ &dyn Fn(f64, f64) -> u32
let foo = foo5();
- // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
+ // ^^^ &dyn Fn(&(dyn Fn(f64, f64) -> u32 + 'static), f64) -> u32
let foo = foo6();
// ^^^ impl Fn(f64, f64) -> u32
let foo = foo7();
diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs
index e80c9dc..9d246ed 100644
--- a/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/crates/ide/src/inlay_hints/closing_brace.rs
@@ -191,7 +191,7 @@
//^ impl Tr for ()
impl dyn Tr {
}
-//^ impl dyn Tr
+//^ impl dyn Tr + 'static
static S0: () = 0;
static S1: () = {};
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index f45d096..e74d997 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -526,7 +526,7 @@
pat.syntax(),
token,
pat.fields(),
- fields.into_iter().map(|it| it.ty(db)),
+ fields.into_iter().map(|it| it.ty(db).to_type(db)),
display_target,
))
}
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 8214b4d..9911b85 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -278,7 +278,7 @@
for token in tokens {
let range = token.text_range();
let node = token.parent().unwrap();
- match get_definitions(&sema, token.clone()) {
+ match salsa::attach(self.db, || get_definitions(&sema, token.clone())) {
Some(it) => {
for i in it {
add_token(i, range, &node);
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 828b8f7..8339daf 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -96,7 +96,7 @@
<span class="variable">u</span><span class="operator">.</span><span class="field unsafe">field</span><span class="semicolon">;</span>
<span class="operator">&</span><span class="variable">u</span><span class="operator">.</span><span class="field unsafe">field</span><span class="semicolon">;</span>
- <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">field</span><span class="semicolon">;</span>
+ <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span> <span class="variable">u</span><span class="operator">.</span><span class="field">field</span><span class="semicolon">;</span>
<span class="comment">// this should be safe!</span>
<span class="keyword">let</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="punctuation">_</span> <span class="brace">}</span><span class="semicolon">;</span>
<span class="comment">// but not these</span>
diff --git a/crates/ide/src/view_memory_layout.rs b/crates/ide/src/view_memory_layout.rs
index 950f3f6..ddd58a0 100644
--- a/crates/ide/src/view_memory_layout.rs
+++ b/crates/ide/src/view_memory_layout.rs
@@ -94,14 +94,14 @@
let def = get_definition(&sema, token)?;
let ty = match def {
- Definition::Adt(it) => it.ty(db),
- Definition::TypeAlias(it) => it.ty(db),
+ Definition::Adt(it) => salsa::attach(db, || it.ty(db)),
+ Definition::TypeAlias(it) => salsa::attach(db, || it.ty(db)),
Definition::BuiltinType(it) => it.ty(db),
Definition::SelfType(it) => it.self_ty(db),
Definition::Local(it) => it.ty(db),
- Definition::Field(it) => it.ty(db),
- Definition::Const(it) => it.ty(db),
- Definition::Static(it) => it.ty(db),
+ Definition::Field(it) => salsa::attach(db, || it.ty(db).to_type(db)),
+ Definition::Const(it) => salsa::attach(db, || it.ty(db)),
+ Definition::Static(it) => salsa::attach(db, || it.ty(db)),
_ => return None,
};
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 1db4f8e..920bdd9 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -516,4 +516,5 @@
flags,
precision,
width,
+ never_type_fallback,
}
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index 2c046df..3e4ab8b 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -401,7 +401,19 @@
let sub = sub.strip_invisible();
let mut span = id;
marker(&mut span);
- let wrap_in_parens = !matches!(sub.flat_tokens(), [tt::TokenTree::Leaf(_)])
+
+ // Check if this is a simple negative literal (MINUS + LITERAL)
+ // that should not be wrapped in parentheses
+ let is_negative_literal = matches!(
+ sub.flat_tokens(),
+ [
+ tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '-', .. })),
+ tt::TokenTree::Leaf(tt::Leaf::Literal(_))
+ ]
+ );
+
+ let wrap_in_parens = !is_negative_literal
+ && !matches!(sub.flat_tokens(), [tt::TokenTree::Leaf(_)])
&& sub.try_into_subtree().is_none_or(|it| {
it.top_subtree().delimiter.kind == tt::DelimiterKind::Invisible
});
diff --git a/crates/project-model/src/build_dependencies.rs b/crates/project-model/src/build_dependencies.rs
index 203173c..5eda5af 100644
--- a/crates/project-model/src/build_dependencies.rs
+++ b/crates/project-model/src/build_dependencies.rs
@@ -9,7 +9,7 @@
use std::{cell::RefCell, io, mem, process::Command};
use base_db::Env;
-use cargo_metadata::{Message, camino::Utf8Path};
+use cargo_metadata::{Message, PackageId, camino::Utf8Path};
use cfg::CfgAtom;
use itertools::Itertools;
use la_arena::ArenaMap;
@@ -18,6 +18,7 @@
use serde::Deserialize as _;
use stdx::never;
use toolchain::Tool;
+use triomphe::Arc;
use crate::{
CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, ManifestPath, Package, Sysroot,
@@ -284,7 +285,7 @@
// NB: Cargo.toml could have been modified between `cargo metadata` and
// `cargo check`. We shouldn't assume that package ids we see here are
// exactly those from `config`.
- let mut by_id: FxHashMap<String, Package> = FxHashMap::default();
+ let mut by_id: FxHashMap<Arc<PackageId>, Package> = FxHashMap::default();
for package in workspace.packages() {
outputs.insert(package, BuildScriptOutput::default());
by_id.insert(workspace[package].id.clone(), package);
@@ -323,7 +324,7 @@
// ideally this would be something like:
// with_output_for: impl FnMut(&str, dyn FnOnce(&mut BuildScriptOutput)),
// but owned trait objects aren't a thing
- mut with_output_for: impl FnMut(&str, &mut dyn FnMut(&str, &mut BuildScriptOutput)),
+ mut with_output_for: impl FnMut(&PackageId, &mut dyn FnMut(&str, &mut BuildScriptOutput)),
progress: &dyn Fn(String),
) -> io::Result<Option<String>> {
let errors = RefCell::new(String::new());
@@ -346,7 +347,7 @@
match message {
Message::BuildScriptExecuted(mut message) => {
- with_output_for(&message.package_id.repr, &mut |name, data| {
+ with_output_for(&message.package_id, &mut |name, data| {
progress(format!("build script {name} run"));
let cfgs = {
let mut acc = Vec::new();
@@ -377,7 +378,7 @@
});
}
Message::CompilerArtifact(message) => {
- with_output_for(&message.package_id.repr, &mut |name, data| {
+ with_output_for(&message.package_id, &mut |name, data| {
progress(format!("proc-macro {name} built"));
if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt {
data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro;
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index e613fd5..adc0cc5 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -5,7 +5,7 @@
use anyhow::Context;
use base_db::Env;
-use cargo_metadata::{CargoOpt, MetadataCommand};
+use cargo_metadata::{CargoOpt, MetadataCommand, PackageId};
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
@@ -14,6 +14,7 @@
use span::Edition;
use stdx::process::spawn_with_streaming_output;
use toolchain::Tool;
+use triomphe::Arc;
use crate::cargo_config_file::make_lockfile_copy;
use crate::{CfgOverrides, InvocationStrategy};
@@ -155,8 +156,8 @@
pub features: FxHashMap<String, Vec<String>>,
/// List of features enabled on this package
pub active_features: Vec<String>,
- /// String representation of package id
- pub id: String,
+ /// Package id
+ pub id: Arc<PackageId>,
/// Authors as given in the `Cargo.toml`
pub authors: Vec<String>,
/// Description as given in the `Cargo.toml`
@@ -173,6 +174,10 @@
pub rust_version: Option<semver::Version>,
/// The contents of [package.metadata.rust-analyzer]
pub metadata: RustAnalyzerPackageMetaData,
+ /// If this package is a member of the workspace, store all direct and transitive
+ /// dependencies as long as they are workspace members, to track dependency relationships
+ /// between members.
+ pub all_member_deps: Option<FxHashSet<Package>>,
}
#[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)]
@@ -334,6 +339,8 @@
let mut is_virtual_workspace = true;
let mut requires_rustc_private = false;
+ let mut members = FxHashSet::default();
+
meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
for meta_pkg in meta.packages {
let cargo_metadata::Package {
@@ -356,6 +363,7 @@
rust_version,
..
} = meta_pkg;
+ let id = Arc::new(id);
let meta = from_value::<PackageMetadata>(metadata).unwrap_or_default();
let edition = match edition {
cargo_metadata::Edition::E2015 => Edition::Edition2015,
@@ -375,7 +383,7 @@
let manifest = ManifestPath::try_from(AbsPathBuf::assert(manifest_path)).unwrap();
is_virtual_workspace &= manifest != ws_manifest_path;
let pkg = packages.alloc(PackageData {
- id: id.repr.clone(),
+ id: id.clone(),
name: name.to_string(),
version,
manifest: manifest.clone(),
@@ -395,7 +403,11 @@
features: features.into_iter().collect(),
active_features: Vec::new(),
metadata: meta.rust_analyzer.unwrap_or_default(),
+ all_member_deps: None,
});
+ if is_member {
+ members.insert(pkg);
+ }
let pkg_data = &mut packages[pkg];
requires_rustc_private |= pkg_data.metadata.rustc_private;
pkg_by_id.insert(id, pkg);
@@ -440,6 +452,43 @@
.extend(node.features.into_iter().map(|it| it.to_string()));
}
+ fn saturate_all_member_deps(
+ packages: &mut Arena<PackageData>,
+ to_visit: Package,
+ visited: &mut FxHashSet<Package>,
+ members: &FxHashSet<Package>,
+ ) {
+ let pkg_data = &mut packages[to_visit];
+
+ if !visited.insert(to_visit) {
+ return;
+ }
+
+ let deps: Vec<_> = pkg_data
+ .dependencies
+ .iter()
+ .filter_map(|dep| {
+ let pkg = dep.pkg;
+ if members.contains(&pkg) { Some(pkg) } else { None }
+ })
+ .collect();
+
+ let mut all_member_deps = FxHashSet::from_iter(deps.iter().copied());
+ for dep in deps {
+ saturate_all_member_deps(packages, dep, visited, members);
+ if let Some(transitives) = &packages[dep].all_member_deps {
+ all_member_deps.extend(transitives);
+ }
+ }
+
+ packages[to_visit].all_member_deps = Some(all_member_deps);
+ }
+
+ let mut visited = FxHashSet::default();
+ for member in members.iter() {
+ saturate_all_member_deps(&mut packages, *member, &mut visited, &members);
+ }
+
CargoWorkspace {
packages,
targets,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 6b489d5..a88d228 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -267,6 +267,8 @@
inlayHints_lifetimeElisionHints_useParameterNames: bool = false,
/// Maximum length for inlay hints. Set to null to have an unlimited length.
+ ///
+ /// **Note:** This is mostly a hint, and we don't guarantee to strictly follow the limit.
inlayHints_maxLength: Option<usize> = Some(25),
/// Show function parameter name inlay hints at the call site.
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index ee50237..4bfad98 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -120,6 +120,29 @@
}
}
+ pub(crate) fn clear_check_older_than_for_package(
+ &mut self,
+ flycheck_id: usize,
+ package_id: Arc<PackageId>,
+ generation: DiagnosticsGeneration,
+ ) {
+ let Some(check) = self.check.get_mut(flycheck_id) else {
+ return;
+ };
+ let package_id = Some(package_id);
+ let Some((_, checks)) = check
+ .per_package
+ .extract_if(|k, v| *k == package_id && v.generation < generation)
+ .next()
+ else {
+ return;
+ };
+ self.changes.extend(checks.per_file.into_keys());
+ if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) {
+ fixes.remove(&package_id);
+ }
+ }
+
pub(crate) fn clear_native_for(&mut self, file_id: FileId) {
self.native_syntax.remove(&file_id);
self.native_semantic.remove(&file_id);
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs
index 315c45d..cded34b 100644
--- a/crates/rust-analyzer/src/flycheck.rs
+++ b/crates/rust-analyzer/src/flycheck.rs
@@ -180,17 +180,27 @@
pub(crate) fn restart_workspace(&self, saved_file: Option<AbsPathBuf>) {
let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1;
self.sender
- .send(StateChange::Restart { generation, package: None, saved_file, target: None })
+ .send(StateChange::Restart {
+ generation,
+ scope: FlycheckScope::Workspace,
+ saved_file,
+ target: None,
+ })
.unwrap();
}
/// Schedule a re-start of the cargo check worker to do a package wide check.
- pub(crate) fn restart_for_package(&self, package: String, target: Option<Target>) {
+ pub(crate) fn restart_for_package(
+ &self,
+ package: Arc<PackageId>,
+ target: Option<Target>,
+ workspace_deps: Option<FxHashSet<Arc<PackageId>>>,
+ ) {
let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1;
self.sender
.send(StateChange::Restart {
generation,
- package: Some(package),
+ scope: FlycheckScope::Package { package, workspace_deps },
saved_file: None,
target,
})
@@ -213,8 +223,13 @@
#[derive(Debug)]
pub(crate) enum ClearDiagnosticsKind {
- All,
- OlderThan(DiagnosticsGeneration),
+ All(ClearScope),
+ OlderThan(DiagnosticsGeneration, ClearScope),
+}
+
+#[derive(Debug)]
+pub(crate) enum ClearScope {
+ Workspace,
Package(Arc<PackageId>),
}
@@ -275,10 +290,15 @@
DidFailToRestart(String),
}
+enum FlycheckScope {
+ Workspace,
+ Package { package: Arc<PackageId>, workspace_deps: Option<FxHashSet<Arc<PackageId>>> },
+}
+
enum StateChange {
Restart {
generation: DiagnosticsGeneration,
- package: Option<String>,
+ scope: FlycheckScope,
saved_file: Option<AbsPathBuf>,
target: Option<Target>,
},
@@ -298,6 +318,7 @@
/// or the project root of the project.
root: Arc<AbsPathBuf>,
sysroot_root: Option<AbsPathBuf>,
+ scope: FlycheckScope,
/// CargoHandle exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library
/// doesn't provide a way to read sub-process output without blocking, so we
@@ -343,6 +364,7 @@
config,
sysroot_root,
root: Arc::new(workspace_root),
+ scope: FlycheckScope::Workspace,
manifest_path,
command_handle: None,
command_receiver: None,
@@ -376,7 +398,7 @@
}
Event::RequestStateChange(StateChange::Restart {
generation,
- package,
+ scope,
saved_file,
target,
}) => {
@@ -389,11 +411,11 @@
}
}
+ let command = self.check_command(&scope, saved_file.as_deref(), target);
+ self.scope = scope;
self.generation = generation;
- let Some(command) =
- self.check_command(package.as_deref(), saved_file.as_deref(), target)
- else {
+ let Some(command) = command else {
continue;
};
@@ -435,19 +457,55 @@
tracing::trace!(flycheck_id = self.id, "clearing diagnostics");
// We finished without receiving any diagnostics.
// Clear everything for good measure
- self.send(FlycheckMessage::ClearDiagnostics {
- id: self.id,
- kind: ClearDiagnosticsKind::All,
- });
+ match &self.scope {
+ FlycheckScope::Workspace => {
+ self.send(FlycheckMessage::ClearDiagnostics {
+ id: self.id,
+ kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
+ });
+ }
+ FlycheckScope::Package { package, workspace_deps } => {
+ for pkg in
+ std::iter::once(package).chain(workspace_deps.iter().flatten())
+ {
+ self.send(FlycheckMessage::ClearDiagnostics {
+ id: self.id,
+ kind: ClearDiagnosticsKind::All(ClearScope::Package(
+ pkg.clone(),
+ )),
+ });
+ }
+ }
+ }
} else if res.is_ok() {
// We clear diagnostics for packages on
// `[CargoCheckMessage::CompilerArtifact]` but there seem to be setups where
// cargo may not report an artifact to our runner at all. To handle such
// cases, clear stale diagnostics when flycheck completes successfully.
- self.send(FlycheckMessage::ClearDiagnostics {
- id: self.id,
- kind: ClearDiagnosticsKind::OlderThan(self.generation),
- });
+ match &self.scope {
+ FlycheckScope::Workspace => {
+ self.send(FlycheckMessage::ClearDiagnostics {
+ id: self.id,
+ kind: ClearDiagnosticsKind::OlderThan(
+ self.generation,
+ ClearScope::Workspace,
+ ),
+ });
+ }
+ FlycheckScope::Package { package, workspace_deps } => {
+ for pkg in
+ std::iter::once(package).chain(workspace_deps.iter().flatten())
+ {
+ self.send(FlycheckMessage::ClearDiagnostics {
+ id: self.id,
+ kind: ClearDiagnosticsKind::OlderThan(
+ self.generation,
+ ClearScope::Package(pkg.clone()),
+ ),
+ });
+ }
+ }
+ }
}
self.clear_diagnostics_state();
@@ -475,7 +533,7 @@
);
self.send(FlycheckMessage::ClearDiagnostics {
id: self.id,
- kind: ClearDiagnosticsKind::Package(package_id),
+ kind: ClearDiagnosticsKind::All(ClearScope::Package(package_id)),
});
}
}
@@ -498,7 +556,9 @@
);
self.send(FlycheckMessage::ClearDiagnostics {
id: self.id,
- kind: ClearDiagnosticsKind::Package(package_id.clone()),
+ kind: ClearDiagnosticsKind::All(ClearScope::Package(
+ package_id.clone(),
+ )),
});
}
} else if self.diagnostics_received
@@ -507,7 +567,7 @@
self.diagnostics_received = DiagnosticsReceived::YesAndClearedForAll;
self.send(FlycheckMessage::ClearDiagnostics {
id: self.id,
- kind: ClearDiagnosticsKind::All,
+ kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
});
}
self.send(FlycheckMessage::AddDiagnostic {
@@ -548,7 +608,7 @@
/// return None.
fn check_command(
&self,
- package: Option<&str>,
+ scope: &FlycheckScope,
saved_file: Option<&AbsPath>,
target: Option<Target>,
) -> Option<Command> {
@@ -564,9 +624,9 @@
}
cmd.arg(command);
- match package {
- Some(pkg) => cmd.arg("-p").arg(pkg),
- None => cmd.arg("--workspace"),
+ match scope {
+ FlycheckScope::Workspace => cmd.arg("--workspace"),
+ FlycheckScope::Package { package, .. } => cmd.arg("-p").arg(&package.repr),
};
if let Some(tgt) = target {
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 89d6fb8..ce6644f 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -9,6 +9,7 @@
time::{Duration, Instant},
};
+use cargo_metadata::PackageId;
use crossbeam_channel::{Receiver, Sender, unbounded};
use hir::ChangeWithProcMacros;
use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
@@ -784,6 +785,7 @@
cargo_toml: package_data.manifest.clone(),
crate_id,
package: cargo.package_flag(package_data),
+ package_id: package_data.id.clone(),
target: target_data.name.clone(),
target_kind: target_data.kind,
required_features: target_data.required_features.clone(),
@@ -812,6 +814,27 @@
None
}
+ pub(crate) fn all_workspace_dependencies_for_package(
+ &self,
+ package: &Arc<PackageId>,
+ ) -> Option<FxHashSet<Arc<PackageId>>> {
+ for workspace in self.workspaces.iter() {
+ match &workspace.kind {
+ ProjectWorkspaceKind::Cargo { cargo, .. }
+ | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => {
+ let package = cargo.packages().find(|p| cargo[*p].id == *package)?;
+
+ return cargo[package]
+ .all_member_deps
+ .as_ref()
+ .map(|deps| deps.iter().map(|dep| cargo[*dep].id.clone()).collect());
+ }
+ _ => {}
+ }
+ }
+ None
+ }
+
pub(crate) fn file_exists(&self, file_id: FileId) -> bool {
self.vfs.read().0.exists(file_id)
}
diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs
index 68c91a6..87be09d 100644
--- a/crates/rust-analyzer/src/handlers/notification.rs
+++ b/crates/rust-analyzer/src/handlers/notification.rs
@@ -331,7 +331,7 @@
let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| {
let tgt_kind = it.target_kind();
let (tgt_name, root, package) = match it {
- TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package),
+ TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package_id),
_ => return None,
};
@@ -368,7 +368,13 @@
_ => false,
});
if let Some(idx) = package_workspace_idx {
- world.flycheck[idx].restart_for_package(package, target);
+ let workspace_deps =
+ world.all_workspace_dependencies_for_package(&package);
+ world.flycheck[idx].restart_for_package(
+ package,
+ target,
+ workspace_deps,
+ );
}
}
}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index c6762f3..3e80e8b 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -20,7 +20,7 @@
config::Config,
diagnostics::{DiagnosticsGeneration, NativeDiagnosticsFetchKind, fetch_native_diagnostics},
discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage},
- flycheck::{self, ClearDiagnosticsKind, FlycheckMessage},
+ flycheck::{self, ClearDiagnosticsKind, ClearScope, FlycheckMessage},
global_state::{
FetchBuildDataResponse, FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState,
file_id_to_url, url_to_file_id,
@@ -1042,17 +1042,22 @@
};
}
}
- FlycheckMessage::ClearDiagnostics { id, kind: ClearDiagnosticsKind::All } => {
- self.diagnostics.clear_check(id)
- }
FlycheckMessage::ClearDiagnostics {
id,
- kind: ClearDiagnosticsKind::OlderThan(generation),
+ kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
+ } => self.diagnostics.clear_check(id),
+ FlycheckMessage::ClearDiagnostics {
+ id,
+ kind: ClearDiagnosticsKind::All(ClearScope::Package(package_id)),
+ } => self.diagnostics.clear_check_for_package(id, package_id),
+ FlycheckMessage::ClearDiagnostics {
+ id,
+ kind: ClearDiagnosticsKind::OlderThan(generation, ClearScope::Workspace),
} => self.diagnostics.clear_check_older_than(id, generation),
FlycheckMessage::ClearDiagnostics {
id,
- kind: ClearDiagnosticsKind::Package(package_id),
- } => self.diagnostics.clear_check_for_package(id, package_id),
+ kind: ClearDiagnosticsKind::OlderThan(generation, ClearScope::Package(package_id)),
+ } => self.diagnostics.clear_check_older_than_for_package(id, package_id, generation),
FlycheckMessage::Progress { id, progress } => {
let (state, message) = match progress {
flycheck::Progress::DidStart => (Progress::Begin, None),
diff --git a/crates/rust-analyzer/src/target_spec.rs b/crates/rust-analyzer/src/target_spec.rs
index 7132e09..e532d15 100644
--- a/crates/rust-analyzer/src/target_spec.rs
+++ b/crates/rust-analyzer/src/target_spec.rs
@@ -2,12 +2,14 @@
use std::mem;
+use cargo_metadata::PackageId;
use cfg::{CfgAtom, CfgExpr};
use hir::sym;
use ide::{Cancellable, Crate, FileId, RunnableKind, TestId};
use project_model::project_json::Runnable;
use project_model::{CargoFeatures, ManifestPath, TargetKind};
use rustc_hash::FxHashSet;
+use triomphe::Arc;
use vfs::AbsPathBuf;
use crate::global_state::GlobalStateSnapshot;
@@ -52,6 +54,7 @@
pub(crate) workspace_root: AbsPathBuf,
pub(crate) cargo_toml: ManifestPath,
pub(crate) package: String,
+ pub(crate) package_id: Arc<PackageId>,
pub(crate) target: String,
pub(crate) target_kind: TargetKind,
pub(crate) crate_id: Crate,
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 978c50d..5fa0074 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -187,11 +187,19 @@
}
pub fn replace(buf: &mut String, from: char, to: &str) {
- if !buf.contains(from) {
+ let replace_count = buf.chars().filter(|&ch| ch == from).count();
+ if replace_count == 0 {
return;
}
- // FIXME: do this in place.
- *buf = buf.replace(from, to);
+ let from_len = from.len_utf8();
+ let additional = to.len().saturating_sub(from_len);
+ buf.reserve(additional * replace_count);
+
+ let mut end = buf.len();
+ while let Some(i) = buf[..end].rfind(from) {
+ buf.replace_range(i..i + from_len, to);
+ end = i;
+ }
}
#[must_use]
@@ -343,4 +351,34 @@
"fn main() {\n return 92;\n}\n"
);
}
+
+ #[test]
+ fn test_replace() {
+ #[track_caller]
+ fn test_replace(src: &str, from: char, to: &str, expected: &str) {
+ let mut s = src.to_owned();
+ replace(&mut s, from, to);
+ assert_eq!(s, expected, "from: {from:?}, to: {to:?}");
+ }
+
+ test_replace("", 'a', "b", "");
+ test_replace("", 'a', "😀", "");
+ test_replace("", '😀', "a", "");
+ test_replace("a", 'a', "b", "b");
+ test_replace("aa", 'a', "b", "bb");
+ test_replace("ada", 'a', "b", "bdb");
+ test_replace("a", 'a', "😀", "😀");
+ test_replace("😀", '😀', "a", "a");
+ test_replace("😀x", '😀', "a", "ax");
+ test_replace("y😀x", '😀', "a", "yax");
+ test_replace("a,b,c", ',', ".", "a.b.c");
+ test_replace("a,b,c", ',', "..", "a..b..c");
+ test_replace("a.b.c", '.', "..", "a..b..c");
+ test_replace("a.b.c", '.', "..", "a..b..c");
+ test_replace("a😀b😀c", '😀', ".", "a.b.c");
+ test_replace("a.b.c", '.', "😀", "a😀b😀c");
+ test_replace("a.b.c", '.', "😀😀", "a😀😀b😀😀c");
+ test_replace(".a.b.c.", '.', "()", "()a()b()c()");
+ test_replace(".a.b.c.", '.', "", "abc");
+ }
}
diff --git a/crates/syntax/src/syntax_error.rs b/crates/syntax/src/syntax_error.rs
index dc6130b..1c90289 100644
--- a/crates/syntax/src/syntax_error.rs
+++ b/crates/syntax/src/syntax_error.rs
@@ -42,3 +42,5 @@
self.0.fmt(f)
}
}
+
+impl std::error::Error for SyntaxError {}
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md
index 50dacd8..e78f1b4 100644
--- a/docs/book/src/configuration_generated.md
+++ b/docs/book/src/configuration_generated.md
@@ -1046,6 +1046,8 @@
Maximum length for inlay hints. Set to null to have an unlimited length.
+**Note:** This is mostly a hint, and we don't guarantee to strictly follow the limit.
+
## rust-analyzer.inlayHints.parameterHints.enable {#inlayHints.parameterHints.enable}
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index ad8708e..e35a159 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -6405,9 +6405,9 @@
}
},
"node_modules/tar-fs": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz",
- "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==",
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
+ "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
"dev": true,
"license": "MIT",
"optional": true,
diff --git a/editors/code/package.json b/editors/code/package.json
index 1d27a12..745e0da 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -2355,7 +2355,7 @@
"title": "Inlay Hints",
"properties": {
"rust-analyzer.inlayHints.maxLength": {
- "markdownDescription": "Maximum length for inlay hints. Set to null to have an unlimited length.",
+ "markdownDescription": "Maximum length for inlay hints. Set to null to have an unlimited length.\n\n**Note:** This is mostly a hint, and we don't guarantee to strictly follow the limit.",
"default": 25,
"type": [
"null",
diff --git a/rust-version b/rust-version
index 02b217f..1f90d4e 100644
--- a/rust-version
+++ b/rust-version
@@ -1 +1 @@
-21a19c297d4f5a03501d92ca251bd7a17073c08a
+f957826bff7a68b267ce75b1ea56352aed0cca0a