Implement next trait solver
diff --git a/Cargo.lock b/Cargo.lock
index 80a311a..f72e698 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -259,9 +259,9 @@
[[package]]
name = "chalk-derive"
-version = "0.103.0"
+version = "0.104.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb4899682de915ca7c0b025bdd0a3d34c75fe12184122fda6805a7baddaa293c"
+checksum = "9ea9b1e80910f66ae87c772247591432032ef3f6a67367ff17f8343db05beafa"
dependencies = [
"proc-macro2",
"quote",
@@ -271,9 +271,9 @@
[[package]]
name = "chalk-ir"
-version = "0.103.0"
+version = "0.104.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90a37d2ab99352b4caca135061e7b4ac67024b648c28ed0b787feec4bea4caed"
+checksum = "7047a516de16226cd17344d41a319d0ea1064bf9e60bd612ab341ab4a34bbfa8"
dependencies = [
"bitflags 2.9.1",
"chalk-derive",
@@ -281,9 +281,9 @@
[[package]]
name = "chalk-recursive"
-version = "0.103.0"
+version = "0.104.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c855be60e646664bc37c2496d3dc81ca5ef60520930e5e0f0057a0575aff6c19"
+checksum = "882959c242558cc686de7ff0aa59860295598d119e84a4b100215f44c3d606c4"
dependencies = [
"chalk-derive",
"chalk-ir",
@@ -294,9 +294,9 @@
[[package]]
name = "chalk-solve"
-version = "0.103.0"
+version = "0.104.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "477ac6cdfd2013e9f93b09b036c2b607a67b2e728f4777b8422d55a79e9e3a34"
+checksum = "72860086494ccfa05bbd3779a74babb8ace27da9a0cbabffa1315223c7290927"
dependencies = [
"chalk-derive",
"chalk-ir",
@@ -446,6 +446,17 @@
]
[[package]]
+name = "derive-where"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "derive_arbitrary"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -696,6 +707,7 @@
"indexmap",
"intern",
"itertools 0.14.0",
+ "ra-ap-rustc_type_ir",
"rustc-hash 2.1.1",
"smallvec",
"span",
@@ -705,6 +717,8 @@
"test-fixture",
"test-utils",
"tracing",
+ "tracing-subscriber",
+ "tracing-tree",
"triomphe",
"tt",
]
@@ -801,8 +815,11 @@
"project-model",
"query-group-macro",
"ra-ap-rustc_abi",
+ "ra-ap-rustc_ast_ir",
"ra-ap-rustc_index",
+ "ra-ap-rustc_next_trait_solver",
"ra-ap-rustc_pattern_analysis",
+ "ra-ap-rustc_type_ir",
"rustc-hash 2.1.1",
"rustc_apfloat",
"salsa",
@@ -1857,6 +1874,12 @@
]
[[package]]
+name = "ra-ap-rustc_ast_ir"
+version = "0.123.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc17e8ce797f2a8d03b838fbf166749b876164432ce81e37d283bf69e3cf80"
+
+[[package]]
name = "ra-ap-rustc_hashes"
version = "0.123.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1909,6 +1932,19 @@
]
[[package]]
+name = "ra-ap-rustc_next_trait_solver"
+version = "0.123.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14f7dfbdf1d045ff4e385e1efdfc3799379895e9c3f3b9b379a0bef4cb238441"
+dependencies = [
+ "derive-where",
+ "ra-ap-rustc_index",
+ "ra-ap-rustc_type_ir",
+ "ra-ap-rustc_type_ir_macros",
+ "tracing",
+]
+
+[[package]]
name = "ra-ap-rustc_parse_format"
version = "0.121.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1932,6 +1968,37 @@
]
[[package]]
+name = "ra-ap-rustc_type_ir"
+version = "0.123.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bc59fb10a922c38a24cb8a1494f799b0af30bd041acbea689378d3bf330534b"
+dependencies = [
+ "bitflags 2.9.1",
+ "derive-where",
+ "ena",
+ "indexmap",
+ "ra-ap-rustc_ast_ir",
+ "ra-ap-rustc_index",
+ "ra-ap-rustc_type_ir_macros",
+ "rustc-hash 2.1.1",
+ "smallvec",
+ "thin-vec",
+ "tracing",
+]
+
+[[package]]
+name = "ra-ap-rustc_type_ir_macros"
+version = "0.123.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58878914b6dac7499baeecc8dbb4b9d9dda88030e4ab90cd3b4e87523fbedafe"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 01a13a3..bb8444e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -94,6 +94,9 @@
ra-ap-rustc_index = { version = "0.123", default-features = false }
ra-ap-rustc_abi = { version = "0.123", default-features = false }
ra-ap-rustc_pattern_analysis = { version = "0.123", default-features = false }
+ra-ap-rustc_ast_ir = { version = "0.123", default-features = false }
+ra-ap-rustc_type_ir = { version = "0.123", default-features = false }
+ra-ap-rustc_next_trait_solver = { version = "0.123", default-features = false }
# local crates that aren't published to crates.io. These should not have versions.
@@ -108,10 +111,10 @@
bitflags = "2.9.1"
cargo_metadata = "0.21.0"
camino = "1.1.10"
-chalk-solve = { version = "0.103.0", default-features = false }
-chalk-ir = "0.103.0"
-chalk-recursive = { version = "0.103.0", default-features = false }
-chalk-derive = "0.103.0"
+chalk-solve = { version = "0.104.0", default-features = false }
+chalk-ir = "0.104.0"
+chalk-recursive = { version = "0.104.0", default-features = false }
+chalk-derive = "0.104.0"
crossbeam-channel = "0.5.15"
dissimilar = "1.0.10"
dot = "0.1.4"
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index d431f21..a0be69c 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -383,12 +383,17 @@
AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
- AsyncFnOnceOutput, sym::async_fn_once_output,async_fn_once_output, Target::AssocTy, GenericRequirement::None;
+ CallRefFuture, sym::call_ref_future, call_ref_future_ty, Target::AssocTy, GenericRequirement::None;
+ CallOnceFuture, sym::call_once_future, call_once_future_ty, Target::AssocTy, GenericRequirement::None;
+ AsyncFnOnceOutput, sym::async_fn_once_output, async_fn_once_output_ty, Target::AssocTy, GenericRequirement::None;
+
FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
CoroutineState, sym::coroutine_state, coroutine_state, Target::Enum, GenericRequirement::None;
Coroutine, sym::coroutine, coroutine_trait, Target::Trait, GenericRequirement::Minimum(1);
+ CoroutineReturn, sym::coroutine_return, coroutine_return_ty, Target::AssocTy, GenericRequirement::None;
+ CoroutineYield, sym::coroutine_yield, coroutine_yield_ty, Target::AssocTy, GenericRequirement::None;
Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None;
Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None;
diff --git a/crates/hir-def/src/signatures.rs b/crates/hir-def/src/signatures.rs
index 92e610b..598b2c0 100644
--- a/crates/hir-def/src/signatures.rs
+++ b/crates/hir-def/src/signatures.rs
@@ -395,7 +395,7 @@
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
- pub struct TraitFlags: u8 {
+ pub struct TraitFlags: u16 {
const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
const FUNDAMENTAL = 1 << 2;
const UNSAFE = 1 << 3;
@@ -403,6 +403,7 @@
const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 5;
const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 6;
const RUSTC_PAREN_SUGAR = 1 << 7;
+ const COINDUCTIVE = 1 << 8;
}
}
@@ -436,6 +437,9 @@
if attrs.by_key(sym::rustc_paren_sugar).exists() {
flags |= TraitFlags::RUSTC_PAREN_SUGAR;
}
+ if attrs.by_key(sym::rustc_coinductive).exists() {
+ flags |= TraitFlags::COINDUCTIVE;
+ }
let mut skip_array_during_method_dispatch =
attrs.by_key(sym::rustc_skip_array_during_method_dispatch).exists();
let mut skip_boxed_slice_during_method_dispatch = false;
diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml
index 7cc0a26..6418211 100644
--- a/crates/hir-ty/Cargo.toml
+++ b/crates/hir-ty/Cargo.toml
@@ -40,7 +40,14 @@
ra-ap-rustc_abi.workspace = true
ra-ap-rustc_index.workspace = true
ra-ap-rustc_pattern_analysis.workspace = true
+ra-ap-rustc_ast_ir.workspace = true
+ra-ap-rustc_type_ir.workspace = true
+ra-ap-rustc_next_trait_solver.workspace = true
+# These moved to dev deps if `setup_tracing` was a macro and dependents also
+# included these
+tracing-subscriber.workspace = true
+tracing-tree.workspace = true
# local deps
stdx.workspace = true
@@ -53,9 +60,6 @@
[dev-dependencies]
expect-test = "1.5.1"
-tracing.workspace = true
-tracing-subscriber.workspace = true
-tracing-tree.workspace = true
project-model.workspace = true
# local deps
diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs
index 26ca7fb..4ea0156 100644
--- a/crates/hir-ty/src/autoderef.rs
+++ b/crates/hir-ty/src/autoderef.rs
@@ -225,7 +225,9 @@
// Check that the type implements Deref at all
let trait_ref = projection.trait_ref(db);
let implements_goal: Goal = trait_ref.cast(Interner);
- table.try_obligation(implements_goal.clone())?;
+ if table.try_obligation(implements_goal.clone()).no_solution() {
+ return None;
+ }
table.register_obligation(implements_goal);
diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs
index 3ba7c93..f523a5e 100644
--- a/crates/hir-ty/src/chalk_db.rs
+++ b/crates/hir-ty/src/chalk_db.rs
@@ -1,47 +1,36 @@
//! The implementation of `RustIrDatabase` for Chalk, which provides information
//! about the code that Chalk needs.
-use core::ops;
-use std::{iter, ops::ControlFlow, sync::Arc};
+use std::sync::Arc;
-use hir_expand::name::Name;
-use intern::sym;
-use span::Edition;
use tracing::debug;
-use chalk_ir::{CanonicalVarKinds, cast::Caster, fold::shift::Shift};
-use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
+use chalk_ir::{cast::Caster, fold::shift::Shift};
+use chalk_solve::rust_ir::{self, WellKnownTrait};
use base_db::Crate;
use hir_def::{
- AssocItemId, BlockId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup,
- TypeAliasId, VariantId,
- hir::Movability,
+ AssocItemId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId,
+ VariantId,
lang_item::LangItem,
signatures::{ImplFlags, StructFlags, TraitFlags},
};
use crate::{
- AliasEq, AliasTy, BoundVar, DebruijnIndex, Interner, ProjectionTy, ProjectionTyExt,
- QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
- WhereClause,
- db::{HirDatabase, InternedCoroutine},
- from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
+ AliasEq, AliasTy, DebruijnIndex, Interner, ProjectionTyExt, QuantifiedWhereClause,
+ Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause,
+ db::HirDatabase,
+ from_assoc_type_id, from_chalk_trait_id,
generics::generics,
lower::LifetimeElisionKind,
- make_binders, make_single_type_binders,
+ make_binders,
mapping::{ToChalk, TypeAliasAsValue, from_chalk},
- method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TraitImpls, TyFingerprint},
to_assoc_type_id, to_chalk_trait_id,
- traits::ChalkContext,
- utils::ClosureSubst,
- wrap_empty_binders,
};
pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>;
pub(crate) type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>;
pub(crate) type AdtDatum = chalk_solve::rust_ir::AdtDatum<Interner>;
pub(crate) type ImplDatum = chalk_solve::rust_ir::ImplDatum<Interner>;
-pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>;
pub(crate) type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
pub(crate) type TraitId = chalk_ir::TraitId<Interner>;
@@ -52,551 +41,6 @@
pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>;
pub(crate) type Variances = chalk_ir::Variances<Interner>;
-impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
- fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> {
- self.db.associated_ty_data(from_assoc_type_id(id))
- }
- fn associated_ty_from_impl(
- &self,
- impl_id: chalk_ir::ImplId<Interner>,
- assoc_type_id: chalk_ir::AssocTypeId<Interner>,
- ) -> Option<rust_ir::AssociatedTyValueId<Interner>> {
- let alias_id = from_assoc_type_id(assoc_type_id);
- let trait_sig = self.db.type_alias_signature(alias_id);
- hir_def::ImplId::from_chalk(self.db, impl_id).impl_items(self.db).items.iter().find_map(
- |(name, item)| match item {
- AssocItemId::TypeAliasId(alias) if &trait_sig.name == name => {
- Some(TypeAliasAsValue(*alias).to_chalk(self.db))
- }
- _ => None,
- },
- )
- }
- fn trait_datum(&self, trait_id: TraitId) -> Arc<TraitDatum> {
- self.db.trait_datum(self.krate, trait_id)
- }
- fn adt_datum(&self, struct_id: AdtId) -> Arc<AdtDatum> {
- self.db.adt_datum(self.krate, struct_id)
- }
- fn adt_repr(&self, _struct_id: AdtId) -> Arc<rust_ir::AdtRepr<Interner>> {
- // FIXME: keep track of these
- Arc::new(rust_ir::AdtRepr { c: false, packed: false, int: None })
- }
- fn discriminant_type(&self, ty: chalk_ir::Ty<Interner>) -> chalk_ir::Ty<Interner> {
- if let chalk_ir::TyKind::Adt(id, _) = ty.kind(Interner)
- && let hir_def::AdtId::EnumId(e) = id.0
- {
- let enum_data = self.db.enum_signature(e);
- let ty = enum_data.repr.unwrap_or_default().discr_type();
- return chalk_ir::TyKind::Scalar(match ty {
- hir_def::layout::IntegerType::Pointer(is_signed) => match is_signed {
- true => chalk_ir::Scalar::Int(chalk_ir::IntTy::Isize),
- false => chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize),
- },
- hir_def::layout::IntegerType::Fixed(size, is_signed) => match is_signed {
- true => chalk_ir::Scalar::Int(match size {
- hir_def::layout::Integer::I8 => chalk_ir::IntTy::I8,
- hir_def::layout::Integer::I16 => chalk_ir::IntTy::I16,
- hir_def::layout::Integer::I32 => chalk_ir::IntTy::I32,
- hir_def::layout::Integer::I64 => chalk_ir::IntTy::I64,
- hir_def::layout::Integer::I128 => chalk_ir::IntTy::I128,
- }),
- false => chalk_ir::Scalar::Uint(match size {
- hir_def::layout::Integer::I8 => chalk_ir::UintTy::U8,
- hir_def::layout::Integer::I16 => chalk_ir::UintTy::U16,
- hir_def::layout::Integer::I32 => chalk_ir::UintTy::U32,
- hir_def::layout::Integer::I64 => chalk_ir::UintTy::U64,
- hir_def::layout::Integer::I128 => chalk_ir::UintTy::U128,
- }),
- },
- })
- .intern(Interner);
- }
- chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U8)).intern(Interner)
- }
- fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> {
- self.db.impl_datum(self.krate, impl_id)
- }
-
- fn fn_def_datum(
- &self,
- fn_def_id: chalk_ir::FnDefId<Interner>,
- ) -> Arc<rust_ir::FnDefDatum<Interner>> {
- self.db.fn_def_datum(from_chalk(self.db, fn_def_id))
- }
-
- fn impls_for_trait(
- &self,
- trait_id: TraitId,
- parameters: &[chalk_ir::GenericArg<Interner>],
- binders: &CanonicalVarKinds<Interner>,
- ) -> Vec<ImplId> {
- debug!("impls_for_trait {:?}", trait_id);
- let trait_: hir_def::TraitId = from_chalk_trait_id(trait_id);
-
- let ty: Ty = parameters[0].assert_ty_ref(Interner).clone();
-
- fn binder_kind(
- ty: &Ty,
- binders: &CanonicalVarKinds<Interner>,
- ) -> Option<chalk_ir::TyVariableKind> {
- if let TyKind::BoundVar(bv) = ty.kind(Interner) {
- let binders = binders.as_slice(Interner);
- if bv.debruijn == DebruijnIndex::INNERMOST
- && let chalk_ir::VariableKind::Ty(tk) = binders[bv.index].kind
- {
- return Some(tk);
- }
- }
- None
- }
-
- let self_ty_fp = TyFingerprint::for_trait_impl(&ty);
- let fps: &[TyFingerprint] = match binder_kind(&ty, binders) {
- Some(chalk_ir::TyVariableKind::Integer) => &ALL_INT_FPS,
- Some(chalk_ir::TyVariableKind::Float) => &ALL_FLOAT_FPS,
- _ => self_ty_fp.as_slice(),
- };
-
- let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
-
- let mut result = vec![];
- if fps.is_empty() {
- debug!("Unrestricted search for {:?} impls...", trait_);
- _ = self.for_trait_impls(trait_, self_ty_fp, |impls| {
- result.extend(impls.for_trait(trait_).map(id_to_chalk));
- ControlFlow::Continue(())
- });
- } else {
- _ =
- self.for_trait_impls(trait_, self_ty_fp, |impls| {
- result.extend(fps.iter().flat_map(move |fp| {
- impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
- }));
- ControlFlow::Continue(())
- });
- };
-
- debug!("impls_for_trait returned {} impls", result.len());
- result
- }
-
- fn impl_provided_for(&self, auto_trait_id: TraitId, kind: &chalk_ir::TyKind<Interner>) -> bool {
- debug!("impl_provided_for {:?}, {:?}", auto_trait_id, kind);
-
- let trait_id = from_chalk_trait_id(auto_trait_id);
- let self_ty = kind.clone().intern(Interner);
- // We cannot filter impls by `TyFingerprint` for the following types:
- let self_ty_fp = match kind {
- // because we need to find any impl whose Self type is a ref with the same mutability
- // (we don't care about the inner type).
- TyKind::Ref(..) => None,
- // because we need to find any impl whose Self type is a tuple with the same arity.
- TyKind::Tuple(..) => None,
- _ => TyFingerprint::for_trait_impl(&self_ty),
- };
-
- let check_kind = |impl_id| {
- let impl_self_ty = self.db.impl_self_ty(impl_id);
- // NOTE(skip_binders): it's safe to skip binders here as we don't check substitutions.
- let impl_self_kind = impl_self_ty.skip_binders().kind(Interner);
-
- match (kind, impl_self_kind) {
- (TyKind::Adt(id_a, _), TyKind::Adt(id_b, _)) => id_a == id_b,
- (TyKind::AssociatedType(id_a, _), TyKind::AssociatedType(id_b, _)) => id_a == id_b,
- (TyKind::Scalar(scalar_a), TyKind::Scalar(scalar_b)) => scalar_a == scalar_b,
- (TyKind::Error, TyKind::Error)
- | (TyKind::Str, TyKind::Str)
- | (TyKind::Slice(_), TyKind::Slice(_))
- | (TyKind::Never, TyKind::Never)
- | (TyKind::Array(_, _), TyKind::Array(_, _)) => true,
- (TyKind::Tuple(arity_a, _), TyKind::Tuple(arity_b, _)) => arity_a == arity_b,
- (TyKind::OpaqueType(id_a, _), TyKind::OpaqueType(id_b, _)) => id_a == id_b,
- (TyKind::FnDef(id_a, _), TyKind::FnDef(id_b, _)) => id_a == id_b,
- (TyKind::Ref(id_a, _, _), TyKind::Ref(id_b, _, _))
- | (TyKind::Raw(id_a, _), TyKind::Raw(id_b, _)) => id_a == id_b,
- (TyKind::Closure(id_a, _), TyKind::Closure(id_b, _)) => id_a == id_b,
- (TyKind::Coroutine(id_a, _), TyKind::Coroutine(id_b, _))
- | (TyKind::CoroutineWitness(id_a, _), TyKind::CoroutineWitness(id_b, _)) => {
- id_a == id_b
- }
- (TyKind::Foreign(id_a), TyKind::Foreign(id_b)) => id_a == id_b,
- (_, _) => false,
- }
- };
-
- if let Some(fp) = self_ty_fp {
- self.for_trait_impls(trait_id, self_ty_fp, |impls| {
- match impls.for_trait_and_self_ty(trait_id, fp).any(check_kind) {
- true => ControlFlow::Break(()),
- false => ControlFlow::Continue(()),
- }
- })
- } else {
- self.for_trait_impls(trait_id, self_ty_fp, |impls| {
- match impls.for_trait(trait_id).any(check_kind) {
- true => ControlFlow::Break(()),
- false => ControlFlow::Continue(()),
- }
- })
- }
- .is_break()
- }
-
- fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> {
- self.db.associated_ty_value(self.krate, id)
- }
-
- fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> {
- vec![]
- }
- fn local_impls_to_coherence_check(&self, _trait_id: TraitId) -> Vec<ImplId> {
- // We don't do coherence checking (yet)
- unimplemented!()
- }
- fn interner(&self) -> Interner {
- Interner
- }
- fn well_known_trait_id(
- &self,
- well_known_trait: WellKnownTrait,
- ) -> Option<chalk_ir::TraitId<Interner>> {
- let lang_item = lang_item_from_well_known_trait(well_known_trait);
- let trait_ = lang_item.resolve_trait(self.db, self.krate)?;
- Some(to_chalk_trait_id(trait_))
- }
- fn well_known_assoc_type_id(
- &self,
- assoc_type: rust_ir::WellKnownAssocType,
- ) -> Option<chalk_ir::AssocTypeId<Interner>> {
- let lang_item = match assoc_type {
- rust_ir::WellKnownAssocType::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput,
- };
- let alias = lang_item.resolve_type_alias(self.db, self.krate)?;
- Some(to_assoc_type_id(alias))
- }
-
- fn program_clauses_for_env(
- &self,
- environment: &chalk_ir::Environment<Interner>,
- ) -> chalk_ir::ProgramClauses<Interner> {
- self.db.program_clauses_for_chalk_env(self.krate, self.block, environment.clone())
- }
-
- fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId<Interner>) -> Arc<OpaqueTyDatum> {
- let full_id = self.db.lookup_intern_impl_trait_id(id.into());
- let bound = match full_id {
- crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
- let datas = self
- .db
- .return_type_impl_traits(func)
- .expect("impl trait id without impl traits");
- let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders();
- let data = &datas.impl_traits[idx];
- let bound = OpaqueTyDatumBound {
- bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()),
- where_clauses: chalk_ir::Binders::empty(Interner, vec![]),
- };
- chalk_ir::Binders::new(binders, bound)
- }
- crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => {
- let datas = self
- .db
- .type_alias_impl_traits(alias)
- .expect("impl trait id without impl traits");
- let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders();
- let data = &datas.impl_traits[idx];
- let bound = OpaqueTyDatumBound {
- bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()),
- where_clauses: chalk_ir::Binders::empty(Interner, vec![]),
- };
- chalk_ir::Binders::new(binders, bound)
- }
- crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
- if let Some((future_trait, future_output)) =
- LangItem::Future.resolve_trait(self.db, self.krate).and_then(|trait_| {
- let alias = trait_
- .trait_items(self.db)
- .associated_type_by_name(&Name::new_symbol_root(sym::Output))?;
- Some((trait_, alias))
- })
- {
- // Making up Symbol’s value as variable is void: AsyncBlock<T>:
- //
- // |--------------------OpaqueTyDatum-------------------|
- // |-------------OpaqueTyDatumBound--------------|
- // for<T> <Self> [Future<Self>, Future::Output<Self> = T]
- // ^1 ^0 ^0 ^0 ^1
- let impl_bound = WhereClause::Implemented(TraitRef {
- trait_id: to_chalk_trait_id(future_trait),
- // Self type as the first parameter.
- substitution: Substitution::from1(
- Interner,
- TyKind::BoundVar(BoundVar {
- debruijn: DebruijnIndex::INNERMOST,
- index: 0,
- })
- .intern(Interner),
- ),
- });
- let mut binder = vec![];
- binder.push(crate::wrap_empty_binders(impl_bound));
- let sized_trait = LangItem::Sized.resolve_trait(self.db, self.krate);
- if let Some(sized_trait_) = sized_trait {
- let sized_bound = WhereClause::Implemented(TraitRef {
- trait_id: to_chalk_trait_id(sized_trait_),
- // Self type as the first parameter.
- substitution: Substitution::from1(
- Interner,
- TyKind::BoundVar(BoundVar {
- debruijn: DebruijnIndex::INNERMOST,
- index: 0,
- })
- .intern(Interner),
- ),
- });
- binder.push(crate::wrap_empty_binders(sized_bound));
- }
- let proj_bound = WhereClause::AliasEq(AliasEq {
- alias: AliasTy::Projection(ProjectionTy {
- associated_ty_id: to_assoc_type_id(future_output),
- // Self type as the first parameter.
- substitution: Substitution::from1(
- Interner,
- TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
- .intern(Interner),
- ),
- }),
- // The parameter of the opaque type.
- ty: TyKind::BoundVar(BoundVar { debruijn: DebruijnIndex::ONE, index: 0 })
- .intern(Interner),
- });
- binder.push(crate::wrap_empty_binders(proj_bound));
- let bound = OpaqueTyDatumBound {
- bounds: make_single_type_binders(binder),
- where_clauses: chalk_ir::Binders::empty(Interner, vec![]),
- };
- // The opaque type has 1 parameter.
- make_single_type_binders(bound)
- } else {
- // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback.
- let bound = OpaqueTyDatumBound {
- bounds: chalk_ir::Binders::empty(Interner, vec![]),
- where_clauses: chalk_ir::Binders::empty(Interner, vec![]),
- };
- // The opaque type has 1 parameter.
- make_single_type_binders(bound)
- }
- }
- };
-
- Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound })
- }
-
- fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> {
- // FIXME: actually provide the hidden type; it is relevant for auto traits
- TyKind::Error.intern(Interner)
- }
-
- // object safety was renamed to dyn-compatibility but still remains here in chalk.
- // This will be removed since we are going to migrate to next-gen trait solver.
- fn is_object_safe(&self, trait_id: chalk_ir::TraitId<Interner>) -> bool {
- let trait_ = from_chalk_trait_id(trait_id);
- crate::dyn_compatibility::dyn_compatibility(self.db, trait_).is_none()
- }
-
- fn closure_kind(
- &self,
- _closure_id: chalk_ir::ClosureId<Interner>,
- _substs: &chalk_ir::Substitution<Interner>,
- ) -> rust_ir::ClosureKind {
- // Fn is the closure kind that implements all three traits
- rust_ir::ClosureKind::Fn
- }
- fn closure_inputs_and_output(
- &self,
- _closure_id: chalk_ir::ClosureId<Interner>,
- substs: &chalk_ir::Substitution<Interner>,
- ) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> {
- let sig_ty = ClosureSubst(substs).sig_ty();
- let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr");
- let io = rust_ir::FnDefInputsAndOutputDatum {
- argument_types: sig.params().to_vec(),
- return_type: sig.ret().clone(),
- };
- chalk_ir::Binders::empty(Interner, io.shifted_in(Interner))
- }
- fn closure_upvars(
- &self,
- _closure_id: chalk_ir::ClosureId<Interner>,
- _substs: &chalk_ir::Substitution<Interner>,
- ) -> chalk_ir::Binders<chalk_ir::Ty<Interner>> {
- let ty = TyBuilder::unit();
- chalk_ir::Binders::empty(Interner, ty)
- }
- fn closure_fn_substitution(
- &self,
- _closure_id: chalk_ir::ClosureId<Interner>,
- _substs: &chalk_ir::Substitution<Interner>,
- ) -> chalk_ir::Substitution<Interner> {
- Substitution::empty(Interner)
- }
-
- fn trait_name(&self, trait_id: chalk_ir::TraitId<Interner>) -> String {
- let id = from_chalk_trait_id(trait_id);
- self.db.trait_signature(id).name.display(self.db, self.edition()).to_string()
- }
- fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String {
- let edition = self.edition();
- match adt_id {
- hir_def::AdtId::StructId(id) => {
- self.db.struct_signature(id).name.display(self.db, edition).to_string()
- }
- hir_def::AdtId::EnumId(id) => {
- self.db.enum_signature(id).name.display(self.db, edition).to_string()
- }
- hir_def::AdtId::UnionId(id) => {
- self.db.union_signature(id).name.display(self.db, edition).to_string()
- }
- }
- }
- fn adt_size_align(&self, _id: chalk_ir::AdtId<Interner>) -> Arc<rust_ir::AdtSizeAlign> {
- // FIXME
- Arc::new(rust_ir::AdtSizeAlign::from_one_zst(false))
- }
- fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId<Interner>) -> String {
- let id = self.db.associated_ty_data(from_assoc_type_id(assoc_ty_id)).name;
- self.db.type_alias_signature(id).name.display(self.db, self.edition()).to_string()
- }
- fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId<Interner>) -> String {
- format!("Opaque_{:?}", opaque_ty_id.0)
- }
- fn fn_def_name(&self, fn_def_id: chalk_ir::FnDefId<Interner>) -> String {
- format!("fn_{:?}", fn_def_id.0)
- }
- fn coroutine_datum(
- &self,
- id: chalk_ir::CoroutineId<Interner>,
- ) -> Arc<chalk_solve::rust_ir::CoroutineDatum<Interner>> {
- let InternedCoroutine(parent, expr) = self.db.lookup_intern_coroutine(id.into());
-
- // We fill substitution with unknown type, because we only need to know whether the generic
- // params are types or consts to build `Binders` and those being filled up are for
- // `resume_type`, `yield_type`, and `return_type` of the coroutine in question.
- let subst = TyBuilder::subst_for_coroutine(self.db, parent).fill_with_unknown().build();
-
- let len = subst.len(Interner);
- let input_output = rust_ir::CoroutineInputOutputDatum {
- resume_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 3))
- .intern(Interner),
- yield_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 2))
- .intern(Interner),
- return_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 1))
- .intern(Interner),
- // FIXME: calculate upvars
- upvars: vec![],
- };
-
- let it = subst
- .iter(Interner)
- .map(|it| it.constant(Interner).map(|c| c.data(Interner).ty.clone()));
- let input_output = crate::make_type_and_const_binders(it, input_output);
-
- let movability = match self.db.body(parent)[expr] {
- hir_def::hir::Expr::Closure {
- closure_kind: hir_def::hir::ClosureKind::Coroutine(movability),
- ..
- } => movability,
- _ => unreachable!("non coroutine expression interned as coroutine"),
- };
- let movability = match movability {
- Movability::Static => rust_ir::Movability::Static,
- Movability::Movable => rust_ir::Movability::Movable,
- };
-
- Arc::new(rust_ir::CoroutineDatum { movability, input_output })
- }
- fn coroutine_witness_datum(
- &self,
- id: chalk_ir::CoroutineId<Interner>,
- ) -> Arc<chalk_solve::rust_ir::CoroutineWitnessDatum<Interner>> {
- // FIXME: calculate inner types
- let inner_types =
- rust_ir::CoroutineWitnessExistential { types: wrap_empty_binders(vec![]) };
-
- let InternedCoroutine(parent, _) = self.db.lookup_intern_coroutine(id.into());
- // See the comment in `coroutine_datum()` for unknown types.
- let subst = TyBuilder::subst_for_coroutine(self.db, parent).fill_with_unknown().build();
- let it = subst
- .iter(Interner)
- .map(|it| it.constant(Interner).map(|c| c.data(Interner).ty.clone()));
- let inner_types = crate::make_type_and_const_binders(it, inner_types);
-
- Arc::new(rust_ir::CoroutineWitnessDatum { inner_types })
- }
-
- fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> {
- &self.db
- }
-}
-
-impl ChalkContext<'_> {
- fn edition(&self) -> Edition {
- self.krate.data(self.db).edition
- }
-
- fn for_trait_impls(
- &self,
- trait_id: hir_def::TraitId,
- self_ty_fp: Option<TyFingerprint>,
- mut f: impl FnMut(&TraitImpls) -> ControlFlow<()>,
- ) -> ControlFlow<()> {
- // Note: Since we're using `impls_for_trait` and `impl_provided_for`,
- // only impls where the trait can be resolved should ever reach Chalk.
- // `impl_datum` relies on that and will panic if the trait can't be resolved.
- let in_deps = self.db.trait_impls_in_deps(self.krate);
- let in_self = self.db.trait_impls_in_crate(self.krate);
- let trait_module = trait_id.module(self.db);
- let type_module = match self_ty_fp {
- Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db)),
- Some(TyFingerprint::ForeignType(type_id)) => {
- Some(from_foreign_def_id(type_id).module(self.db))
- }
- Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db)),
- _ => None,
- };
-
- let mut def_blocks =
- [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())];
-
- let block_impls = iter::successors(self.block, |&block_id| {
- cov_mark::hit!(block_local_impls);
- block_id.loc(self.db).module.containing_block()
- })
- .inspect(|&block_id| {
- // make sure we don't search the same block twice
- def_blocks.iter_mut().for_each(|block| {
- if *block == Some(block_id) {
- *block = None;
- }
- });
- })
- .filter_map(|block_id| self.db.trait_impls_in_block(block_id));
- f(&in_self)?;
- for it in in_deps.iter().map(ops::Deref::deref) {
- f(it)?;
- }
- for it in block_impls {
- f(&it)?;
- }
- for it in def_blocks.into_iter().flatten().filter_map(|it| self.db.trait_impls_in_block(it))
- {
- f(&it)?;
- }
- ControlFlow::Continue(())
- }
-}
-
impl chalk_ir::UnificationDatabase<Interner> for &dyn HirDatabase {
fn fn_def_variance(
&self,
@@ -610,15 +54,6 @@
}
}
-pub(crate) fn program_clauses_for_chalk_env_query(
- db: &dyn HirDatabase,
- krate: Crate,
- block: Option<BlockId>,
- environment: chalk_ir::Environment<Interner>,
-) -> chalk_ir::ProgramClauses<Interner> {
- chalk_solve::program_clauses_for_env(&ChalkContext { db, krate, block }, &environment)
-}
-
pub(crate) fn associated_ty_data_query(
db: &dyn HirDatabase,
type_alias: TypeAliasId,
@@ -749,31 +184,6 @@
})
}
-fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem {
- match trait_ {
- WellKnownTrait::Clone => LangItem::Clone,
- WellKnownTrait::CoerceUnsized => LangItem::CoerceUnsized,
- WellKnownTrait::Copy => LangItem::Copy,
- WellKnownTrait::DiscriminantKind => LangItem::DiscriminantKind,
- WellKnownTrait::DispatchFromDyn => LangItem::DispatchFromDyn,
- WellKnownTrait::Drop => LangItem::Drop,
- WellKnownTrait::Fn => LangItem::Fn,
- WellKnownTrait::FnMut => LangItem::FnMut,
- WellKnownTrait::FnOnce => LangItem::FnOnce,
- WellKnownTrait::AsyncFn => LangItem::AsyncFn,
- WellKnownTrait::AsyncFnMut => LangItem::AsyncFnMut,
- WellKnownTrait::AsyncFnOnce => LangItem::AsyncFnOnce,
- WellKnownTrait::Coroutine => LangItem::Coroutine,
- WellKnownTrait::Sized => LangItem::Sized,
- WellKnownTrait::Tuple => LangItem::Tuple,
- WellKnownTrait::Unpin => LangItem::Unpin,
- WellKnownTrait::Unsize => LangItem::Unsize,
- WellKnownTrait::Pointee => LangItem::PointeeTrait,
- WellKnownTrait::FnPtr => LangItem::FnPtrTrait,
- WellKnownTrait::Future => LangItem::Future,
- }
-}
-
pub(crate) fn adt_datum_query(
db: &dyn HirDatabase,
krate: Crate,
diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs
index 836cc96..8fa1aff 100644
--- a/crates/hir-ty/src/chalk_ext.rs
+++ b/crates/hir-ty/src/chalk_ext.rs
@@ -14,10 +14,9 @@
use crate::{
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds,
ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy,
- QuantifiedWhereClause, Substitution, 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, mapping::ToChalk, to_chalk_trait_id,
- utils::ClosureSubst,
+ 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,
};
pub trait TyExt {
@@ -371,7 +370,7 @@
value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
binders: CanonicalVarKinds::empty(Interner),
};
- db.trait_solve(crate_id, None, goal).is_some()
+ !db.trait_solve(crate_id, None, goal).no_solution()
}
fn equals_ctor(&self, other: &Ty) -> bool {
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 6449a4d..22b152f 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -11,7 +11,7 @@
use crate::{
Const, ConstScalar, Interner, MemoryMap, consteval::try_const_usize, db::HirDatabase,
- display::DisplayTarget, mir::pad16, test_db::TestDB,
+ display::DisplayTarget, mir::pad16, setup_tracing, test_db::TestDB,
};
use super::{
@@ -116,6 +116,7 @@
}
fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result<Const, ConstEvalError> {
+ let _tracing = setup_tracing();
let module_id = db.module_for_file(file_id.file_id(db));
let def_map = module_id.def_map(db);
let scope = &def_map[module_id.local_id].scope;
diff --git a/crates/hir-ty/src/consteval_nextsolver.rs b/crates/hir-ty/src/consteval_nextsolver.rs
new file mode 100644
index 0000000..da4aff5
--- /dev/null
+++ b/crates/hir-ty/src/consteval_nextsolver.rs
@@ -0,0 +1,256 @@
+//! Constant evaluation details
+// FIXME(next-solver): this should get removed as things get moved to rustc_type_ir from chalk_ir
+#![allow(unused)]
+
+use base_db::Crate;
+use hir_def::{
+ EnumVariantId, GeneralConstId,
+ expr_store::{Body, HygieneId, path::Path},
+ hir::{Expr, ExprId},
+ resolver::{Resolver, ValueNs},
+ type_ref::LiteralConstRef,
+};
+use hir_expand::Lookup;
+use rustc_type_ir::{
+ UnevaluatedConst,
+ inherent::{IntoKind, SliceLike},
+};
+use stdx::never;
+use triomphe::Arc;
+
+use crate::{
+ ConstScalar, Interner, MemoryMap, Substitution, TraitEnvironment,
+ consteval::ConstEvalError,
+ db::HirDatabase,
+ generics::Generics,
+ infer::InferenceContext,
+ next_solver::{
+ Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
+ ParamConst, SolverDefId, Ty, ValueConst,
+ mapping::{ChalkToNextSolver, convert_args_for_result, convert_binder_to_early_binder},
+ },
+};
+
+use super::mir::{interpret_mir, lower_to_mir, pad16};
+
+pub(crate) fn path_to_const<'a, 'g>(
+ db: &'a dyn HirDatabase,
+ resolver: &Resolver<'a>,
+ path: &Path,
+ args: impl FnOnce() -> &'g Generics,
+ expected_ty: Ty<'a>,
+) -> Option<Const<'a>> {
+ let interner = DbInterner::new_with(db, Some(resolver.krate()), None);
+ match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) {
+ Some(ValueNs::GenericParam(p)) => {
+ let args = args();
+ match args
+ .type_or_const_param(p.into())
+ .and_then(|(idx, p)| p.const_param().map(|p| (idx, p.clone())))
+ {
+ Some((idx, _param)) => {
+ Some(Const::new_param(interner, ParamConst { index: idx as u32 }))
+ }
+ None => {
+ never!(
+ "Generic list doesn't contain this param: {:?}, {:?}, {:?}",
+ args,
+ path,
+ p
+ );
+ None
+ }
+ }
+ }
+ Some(ValueNs::ConstId(c)) => {
+ let args = GenericArgs::new_from_iter(interner, []);
+ Some(Const::new(
+ interner,
+ rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new(
+ SolverDefId::ConstId(c),
+ args,
+ )),
+ ))
+ }
+ _ => None,
+ }
+}
+
+pub fn unknown_const<'db>(ty: Ty<'db>) -> Const<'db> {
+ Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed))
+}
+
+pub fn unknown_const_as_generic<'db>(ty: Ty<'db>) -> GenericArg<'db> {
+ unknown_const(ty).into()
+}
+
+/// Interns a constant scalar with the given type
+pub fn intern_const_ref<'a>(
+ db: &'a dyn HirDatabase,
+ value: &LiteralConstRef,
+ ty: Ty<'a>,
+ krate: Crate,
+) -> Const<'a> {
+ let interner = DbInterner::new_with(db, Some(krate), None);
+ let layout = db.layout_of_ty_ns(ty, TraitEnvironment::empty(krate));
+ let kind = match value {
+ LiteralConstRef::Int(i) => {
+ // FIXME: We should handle failure of layout better.
+ let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16);
+ rustc_type_ir::ConstKind::Value(ValueConst::new(
+ ty,
+ ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()),
+ ))
+ }
+ LiteralConstRef::UInt(i) => {
+ let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16);
+ rustc_type_ir::ConstKind::Value(ValueConst::new(
+ ty,
+ ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()),
+ ))
+ }
+ LiteralConstRef::Bool(b) => rustc_type_ir::ConstKind::Value(ValueConst::new(
+ ty,
+ ConstBytes(Box::new([*b as u8]), MemoryMap::default()),
+ )),
+ LiteralConstRef::Char(c) => rustc_type_ir::ConstKind::Value(ValueConst::new(
+ ty,
+ ConstBytes((*c as u32).to_le_bytes().into(), MemoryMap::default()),
+ )),
+ LiteralConstRef::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
+ };
+ Const::new(interner, kind)
+}
+
+/// Interns a possibly-unknown target usize
+pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const<'db> {
+ intern_const_ref(
+ db,
+ &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt),
+ Ty::new_uint(DbInterner::new_with(db, Some(krate), None), rustc_type_ir::UintTy::Usize),
+ krate,
+ )
+}
+
+pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<u128> {
+ let interner = DbInterner::new_with(db, None, None);
+ match (*c).kind() {
+ ConstKind::Param(_) => None,
+ ConstKind::Infer(_) => None,
+ ConstKind::Bound(_, _) => None,
+ ConstKind::Placeholder(_) => None,
+ ConstKind::Unevaluated(unevaluated_const) => {
+ let c = match unevaluated_const.def {
+ SolverDefId::ConstId(id) => GeneralConstId::ConstId(id),
+ SolverDefId::StaticId(id) => GeneralConstId::StaticId(id),
+ _ => unreachable!(),
+ };
+ let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice());
+ let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner);
+ try_const_usize(db, &ec)
+ }
+ ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().0, false))),
+ ConstKind::Error(_) => None,
+ ConstKind::Expr(_) => None,
+ }
+}
+
+pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<i128> {
+ let interner = DbInterner::new_with(db, None, None);
+ match (*c).kind() {
+ ConstKind::Param(_) => None,
+ ConstKind::Infer(_) => None,
+ ConstKind::Bound(_, _) => None,
+ ConstKind::Placeholder(_) => None,
+ ConstKind::Unevaluated(unevaluated_const) => {
+ let c = match unevaluated_const.def {
+ SolverDefId::ConstId(id) => GeneralConstId::ConstId(id),
+ SolverDefId::StaticId(id) => GeneralConstId::StaticId(id),
+ _ => unreachable!(),
+ };
+ let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice());
+ let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner);
+ try_const_isize(db, &ec)
+ }
+ ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().0, true))),
+ ConstKind::Error(_) => None,
+ ConstKind::Expr(_) => None,
+ }
+}
+
+pub(crate) fn const_eval_discriminant_variant(
+ db: &dyn HirDatabase,
+ variant_id: EnumVariantId,
+) -> Result<i128, ConstEvalError> {
+ let interner = DbInterner::new_with(db, None, None);
+ let def = variant_id.into();
+ let body = db.body(def);
+ let loc = variant_id.lookup(db);
+ if matches!(body[body.body_expr], Expr::Missing) {
+ let prev_idx = loc.index.checked_sub(1);
+ let value = match prev_idx {
+ Some(prev_idx) => {
+ 1 + db.const_eval_discriminant(
+ loc.parent.enum_variants(db).variants[prev_idx as usize].0,
+ )?
+ }
+ _ => 0,
+ };
+ return Ok(value);
+ }
+
+ let repr = db.enum_signature(loc.parent).repr;
+ let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed());
+
+ let mir_body = db.monomorphized_mir_body(
+ def,
+ Substitution::empty(Interner),
+ db.trait_environment_for_body(def),
+ )?;
+ let c = interpret_mir(db, mir_body, false, None)?.0?;
+ let c = c.to_nextsolver(interner);
+ let c = if is_signed {
+ try_const_isize(db, &c).unwrap()
+ } else {
+ try_const_usize(db, &c).unwrap() as i128
+ };
+ Ok(c)
+}
+
+// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should
+// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here
+// 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();
+ fn has_closure(body: &Body, expr: ExprId) -> bool {
+ if matches!(body[expr], Expr::Closure { .. }) {
+ return true;
+ }
+ let mut r = false;
+ body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx));
+ r
+ }
+ if has_closure(ctx.body, expr) {
+ // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic.
+ return unknown_const(infer[expr].clone().to_nextsolver(interner));
+ }
+ if let Expr::Path(p) = &ctx.body[expr] {
+ let resolver = &ctx.resolver;
+ if let Some(c) = path_to_const(
+ ctx.db,
+ resolver,
+ p,
+ || ctx.generics(),
+ infer[expr].to_nextsolver(interner),
+ ) {
+ return c;
+ }
+ }
+ if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr)
+ && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None)
+ {
+ return result.to_nextsolver(interner);
+ }
+ unknown_const(infer[expr].to_nextsolver(interner))
+}
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index b3d4684..18b8db2 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -17,7 +17,7 @@
use triomphe::Arc;
use crate::{
- Binders, Const, ImplTraitId, ImplTraits, InferenceResult, Interner, PolyFnSig, Substitution,
+ Binders, Const, ImplTraitId, ImplTraits, InferenceResult, PolyFnSig, Substitution,
TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, chalk_db,
consteval::ConstEvalError,
drop::DropGlue,
@@ -26,6 +26,7 @@
lower::{Diagnostics, GenericDefaults, GenericPredicates},
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
mir::{BorrowckResult, MirBody, MirLowerError},
+ traits::NextTraitSolveResult,
};
#[query_group::query_group]
@@ -295,24 +296,162 @@
) -> Ty;
#[salsa::invoke(crate::traits::trait_solve_query)]
+ #[salsa::transparent]
fn trait_solve(
&self,
krate: Crate,
block: Option<BlockId>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
- ) -> Option<crate::Solution>;
-
- #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)]
- fn program_clauses_for_chalk_env(
- &self,
- krate: Crate,
- block: Option<BlockId>,
- env: chalk_ir::Environment<Interner>,
- ) -> chalk_ir::ProgramClauses<Interner>;
+ ) -> NextTraitSolveResult;
#[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;
+
+ // next trait solver
+
+ #[salsa::invoke(crate::layout::layout_of_adt_ns_query)]
+ #[salsa::cycle(cycle_result = crate::layout::layout_of_adt_ns_cycle_result)]
+ fn layout_of_adt_ns<'db>(
+ &'db self,
+ def: AdtId,
+ args: crate::next_solver::GenericArgs<'db>,
+ trait_env: Arc<TraitEnvironment>,
+ ) -> Result<Arc<Layout>, LayoutError>;
+
+ #[salsa::invoke(crate::layout::layout_of_ty_ns_query)]
+ #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_ns_cycle_result)]
+ fn layout_of_ty_ns<'db>(
+ &'db self,
+ ty: crate::next_solver::Ty<'db>,
+ env: Arc<TraitEnvironment>,
+ ) -> Result<Arc<Layout>, LayoutError>;
+
+ #[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>>;
+
+ #[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>(
+ &'db self,
+ var: VariantId,
+ ) -> Arc<
+ 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,
+ def: FunctionId,
+ ) -> Option<Arc<crate::next_solver::EarlyBinder<'db, crate::lower_nextsolver::ImplTraits<'db>>>>;
+
+ #[salsa::invoke(crate::lower_nextsolver::type_alias_impl_traits)]
+ fn type_alias_impl_traits_ns<'db>(
+ &'db self,
+ def: TypeAliasId,
+ ) -> Option<Arc<crate::next_solver::EarlyBinder<'db, crate::lower_nextsolver::ImplTraits<'db>>>>;
+
+ #[salsa::invoke(crate::lower_nextsolver::generic_predicates_for_param_query)]
+ #[salsa::cycle(cycle_result = crate::lower_nextsolver::generic_predicates_for_param_cycle_result)]
+ fn generic_predicates_for_param_ns<'db>(
+ &'db self,
+ def: GenericDefId,
+ param_id: TypeOrConstParamId,
+ assoc_name: Option<Name>,
+ ) -> crate::lower_nextsolver::GenericPredicates<'db>;
+
+ #[salsa::invoke(crate::lower_nextsolver::generic_predicates_query)]
+ fn generic_predicates_ns<'db>(
+ &'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/drop.rs b/crates/hir-ty/src/drop.rs
index 5577be8..a7e942d 100644
--- a/crates/hir-ty/src/drop.rs
+++ b/crates/hir-ty/src/drop.rs
@@ -187,7 +187,7 @@
value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
binders: CanonicalVarKinds::empty(Interner),
};
- db.trait_solve(env.krate, env.block, goal).is_some()
+ db.trait_solve(env.krate, env.block, goal).certain()
}
pub(crate) fn has_drop_glue_cycle_result(
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs
index 6294d68..2d21947 100644
--- a/crates/hir-ty/src/dyn_compatibility.rs
+++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -16,8 +16,8 @@
use smallvec::SmallVec;
use crate::{
- AliasEq, AliasTy, Binders, BoundVar, CallableSig, GoalData, ImplTraitId, Interner, OpaqueTyId,
- ProjectionTyExt, Solution, Substitution, TraitRef, Ty, TyKind, WhereClause, all_super_traits,
+ AliasEq, AliasTy, Binders, BoundVar, CallableSig, DomainGoal, GoalData, ImplTraitId, Interner,
+ OpaqueTyId, ProjectionTyExt, Substitution, TraitRef, Ty, TyKind, WhereClause, all_super_traits,
db::HirDatabase,
from_assoc_type_id, from_chalk_trait_id,
generics::{generics, trait_self_param_idx},
@@ -510,6 +510,8 @@
trait_id: to_chalk_trait_id(unsize_did),
substitution: Substitution::from_iter(Interner, [self_ty.clone(), unsized_self_ty.clone()]),
});
+ let unsized_predicate =
+ Binders::empty(Interner, unsized_predicate.cast::<DomainGoal>(Interner));
let trait_predicate = WhereClause::Implemented(TraitRef {
trait_id: to_chalk_trait_id(trait_),
substitution: Substitution::from_iter(
@@ -518,18 +520,32 @@
.chain(placeholder_subst.iter(Interner).skip(1).cloned()),
),
});
+ let trait_predicate = Binders::empty(Interner, trait_predicate.cast::<DomainGoal>(Interner));
let generic_predicates = &*db.generic_predicates(func.into());
- let clauses = std::iter::once(unsized_predicate)
- .chain(std::iter::once(trait_predicate))
- .chain(generic_predicates.iter().map(|pred| {
- pred.clone().substitute(Interner, &placeholder_subst).into_value_and_skipped_binders().0
- }))
- .map(|pred| {
- pred.cast::<chalk_ir::ProgramClause<Interner>>(Interner).into_from_env_clause(Interner)
- });
- let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
+ let goals = std::iter::once(unsized_predicate).chain(std::iter::once(trait_predicate)).chain(
+ generic_predicates.iter().map(|pred| {
+ pred.clone()
+ .substitute(Interner, &placeholder_subst)
+ .map(|g| g.cast::<DomainGoal>(Interner))
+ }),
+ );
+ let clauses = chalk_ir::ProgramClauses::from_iter(
+ Interner,
+ goals.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<Interner> = chalk_ir::Environment { clauses };
let obligation = WhereClause::Implemented(TraitRef {
trait_id: to_chalk_trait_id(dispatch_from_dyn_did),
@@ -541,9 +557,8 @@
let mut table = chalk_solve::infer::InferenceTable::<Interner>::new();
let canonicalized = table.canonicalize(Interner, in_env);
- let solution = db.trait_solve(krate, None, canonicalized.quantified);
- matches!(solution, Some(Solution::Unique(_)))
+ db.trait_solve(krate, None, canonicalized.quantified).certain()
}
fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option<Ty> {
diff --git a/crates/hir-ty/src/dyn_compatibility/tests.rs b/crates/hir-ty/src/dyn_compatibility/tests.rs
index 5078e8c..4ffa455 100644
--- a/crates/hir-ty/src/dyn_compatibility/tests.rs
+++ b/crates/hir-ty/src/dyn_compatibility/tests.rs
@@ -56,18 +56,21 @@
continue;
};
let mut osvs = FxHashSet::default();
- _ = dyn_compatibility_with_callback(&db, trait_id, &mut |osv| {
- osvs.insert(match osv {
- DynCompatibilityViolation::SizedSelf => SizedSelf,
- DynCompatibilityViolation::SelfReferential => SelfReferential,
- DynCompatibilityViolation::Method(_, mvc) => Method(mvc),
- DynCompatibilityViolation::AssocConst(_) => AssocConst,
- DynCompatibilityViolation::GAT(_) => GAT,
- DynCompatibilityViolation::HasNonCompatibleSuperTrait(_) => {
- HasNonCompatibleSuperTrait
- }
+ let db = &db;
+ salsa::attach(db, || {
+ _ = dyn_compatibility_with_callback(db, trait_id, &mut |osv| {
+ osvs.insert(match osv {
+ DynCompatibilityViolation::SizedSelf => SizedSelf,
+ DynCompatibilityViolation::SelfReferential => SelfReferential,
+ DynCompatibilityViolation::Method(_, mvc) => Method(mvc),
+ DynCompatibilityViolation::AssocConst(_) => AssocConst,
+ DynCompatibilityViolation::GAT(_) => GAT,
+ DynCompatibilityViolation::HasNonCompatibleSuperTrait(_) => {
+ HasNonCompatibleSuperTrait
+ }
+ });
+ ControlFlow::Continue(())
});
- ControlFlow::Continue(())
});
assert_eq!(osvs, expected, "dyn-compatibility violations for `{name}` do not match;");
}
diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs
index f14872e..e6470e5 100644
--- a/crates/hir-ty/src/generics.rs
+++ b/crates/hir-ty/src/generics.rs
@@ -165,6 +165,21 @@
(parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params)
}
+ pub(crate) fn type_or_const_param(
+ &self,
+ param: TypeOrConstParamId,
+ ) -> Option<(usize, TypeOrConstParamData)> {
+ let idx = self.find_type_or_const_param(param)?;
+ self.iter().nth(idx).and_then(|p| {
+ let data = match p.1 {
+ GenericParamDataRef::TypeParamData(p) => p.clone().into(),
+ GenericParamDataRef::ConstParamData(p) => p.clone().into(),
+ _ => return None,
+ };
+ Some((idx, data))
+ })
+ }
+
pub fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option<usize> {
self.find_type_or_const_param(param)
}
@@ -272,7 +287,7 @@
}
}
-fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> {
+pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> {
let container = match def {
GenericDefId::FunctionId(it) => it.lookup(db).container,
GenericDefId::TypeAliasId(it) => it.lookup(db).container,
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 86345b2..7a11ec6 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -88,52 +88,54 @@
/// The entry point of type inference.
pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
- let _p = tracing::info_span!("infer_query").entered();
- let resolver = def.resolver(db);
- let body = db.body(def);
- let mut ctx = InferenceContext::new(db, def, &body, resolver);
+ crate::next_solver::with_new_cache(|| {
+ let _p = tracing::info_span!("infer_query").entered();
+ let resolver = def.resolver(db);
+ let body = db.body(def);
+ let mut ctx = InferenceContext::new(db, def, &body, resolver);
- match def {
- DefWithBodyId::FunctionId(f) => {
- ctx.collect_fn(f);
- }
- DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)),
- DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)),
- DefWithBodyId::VariantId(v) => {
- ctx.return_ty = TyBuilder::builtin(
- match db.enum_signature(v.lookup(db).parent).variant_body_type() {
- hir_def::layout::IntegerType::Pointer(signed) => match signed {
- true => BuiltinType::Int(BuiltinInt::Isize),
- false => BuiltinType::Uint(BuiltinUint::Usize),
+ match def {
+ DefWithBodyId::FunctionId(f) => {
+ ctx.collect_fn(f);
+ }
+ DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)),
+ DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)),
+ DefWithBodyId::VariantId(v) => {
+ ctx.return_ty = TyBuilder::builtin(
+ match db.enum_signature(v.lookup(db).parent).variant_body_type() {
+ hir_def::layout::IntegerType::Pointer(signed) => match signed {
+ true => BuiltinType::Int(BuiltinInt::Isize),
+ false => BuiltinType::Uint(BuiltinUint::Usize),
+ },
+ hir_def::layout::IntegerType::Fixed(size, signed) => match signed {
+ true => BuiltinType::Int(match size {
+ Integer::I8 => BuiltinInt::I8,
+ Integer::I16 => BuiltinInt::I16,
+ Integer::I32 => BuiltinInt::I32,
+ Integer::I64 => BuiltinInt::I64,
+ Integer::I128 => BuiltinInt::I128,
+ }),
+ false => BuiltinType::Uint(match size {
+ Integer::I8 => BuiltinUint::U8,
+ Integer::I16 => BuiltinUint::U16,
+ Integer::I32 => BuiltinUint::U32,
+ Integer::I64 => BuiltinUint::U64,
+ Integer::I128 => BuiltinUint::U128,
+ }),
+ },
},
- hir_def::layout::IntegerType::Fixed(size, signed) => match signed {
- true => BuiltinType::Int(match size {
- Integer::I8 => BuiltinInt::I8,
- Integer::I16 => BuiltinInt::I16,
- Integer::I32 => BuiltinInt::I32,
- Integer::I64 => BuiltinInt::I64,
- Integer::I128 => BuiltinInt::I128,
- }),
- false => BuiltinType::Uint(match size {
- Integer::I8 => BuiltinUint::U8,
- Integer::I16 => BuiltinUint::U16,
- Integer::I32 => BuiltinUint::U32,
- Integer::I64 => BuiltinUint::U64,
- Integer::I128 => BuiltinUint::U128,
- }),
- },
- },
- );
+ );
+ }
}
- }
- ctx.infer_body();
+ ctx.infer_body();
- ctx.infer_mut_body();
+ ctx.infer_mut_body();
- ctx.infer_closures();
+ ctx.infer_closures();
- Arc::new(ctx.resolve_all())
+ Arc::new(ctx.resolve_all())
+ })
}
pub(crate) fn infer_cycle_result(_: &dyn HirDatabase, _: DefWithBodyId) -> Arc<InferenceResult> {
@@ -144,6 +146,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 {
// 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
@@ -1077,14 +1080,22 @@
// Functions might be defining usage sites of TAITs.
// To define an TAITs, that TAIT must appear in the function's signatures.
// So, it suffices to check for params and return types.
- if self
- .return_ty
- .data(Interner)
- .flags
- .intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER))
- {
- tait_candidates.insert(self.return_ty.clone());
- }
+ fold_tys(
+ self.return_ty.clone(),
+ |ty, _| {
+ match ty.kind(Interner) {
+ TyKind::OpaqueType(..)
+ | TyKind::Alias(AliasTy::Opaque(..))
+ | TyKind::InferenceVar(..) => {
+ tait_candidates.insert(self.return_ty.clone());
+ }
+ _ => {}
+ }
+ ty
+ },
+ DebruijnIndex::INNERMOST,
+ );
+
self.make_tait_coercion_table(tait_candidates.iter());
}
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 8024c1a..d8fc20e 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -3,12 +3,13 @@
use std::{cmp, convert::Infallible, mem, ops::ControlFlow};
use chalk_ir::{
- BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind,
+ BoundVar, DebruijnIndex, FnSubst, GenericArg, Mutability, TyKind,
cast::Cast,
fold::{FallibleTypeFolder, Shift, TypeFoldable},
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
};
use either::Either;
+use hir_def::Lookup;
use hir_def::{
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
expr_store::path::Path,
@@ -19,8 +20,8 @@
item_tree::FieldsShape,
lang_item::LangItem,
resolver::ValueNs,
+ type_ref::TypeRefId,
};
-use hir_def::{Lookup, type_ref::TypeRefId};
use hir_expand::name::Name;
use intern::sym;
use rustc_hash::{FxHashMap, FxHashSet};
@@ -30,8 +31,8 @@
use crate::{
Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy,
- DynTyExt, FnAbi, FnPointer, FnSig, GenericArg, Interner, OpaqueTy, ProjectionTy,
- ProjectionTyExt, Substitution, Ty, TyBuilder, TyExt, WhereClause,
+ DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTy, ProjectionTyExt,
+ Substitution, Ty, TyBuilder, TyExt, WhereClause,
db::{HirDatabase, InternedClosure, InternedCoroutine},
error_lifetime, from_assoc_type_id, from_chalk_trait_id, from_placeholder_idx,
generics::Generics,
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 761a256..631a91b 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -13,14 +13,15 @@
use triomphe::Arc;
use crate::{
- Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Lifetime,
- Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
+ Canonical, DomainGoal, FnAbi, FnPointer, FnSig, InEnvironment, Interner, Lifetime,
+ Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
autoderef::{Autoderef, AutoderefKind},
db::HirDatabase,
infer::{
Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
TypeError, TypeMismatch,
},
+ traits::NextTraitSolveResult,
utils::ClosureSubst,
};
@@ -715,13 +716,18 @@
// solve `CoerceUnsized` and `Unsize` goals at this point and leaves the
// rest for later. Also, there's some logic about sized type variables.
// Need to find out in what cases this is necessary
- let solution = self
- .db
- .trait_solve(krate, self.trait_env.block, canonicalized.value.clone().cast(Interner))
- .ok_or(TypeError)?;
+ let solution = self.db.trait_solve(
+ krate,
+ self.trait_env.block,
+ canonicalized.value.clone().cast(Interner),
+ );
match solution {
- Solution::Unique(v) => {
+ // FIXME: this is a weaker guarantee than Chalk's `Guidance::Unique`
+ // was. Chalk's unique guidance at least guarantees that the real solution
+ // is some "subset" of the solutions matching the guidance, but the
+ // substs for `Certainty::No` don't have that same guarantee (I think).
+ NextTraitSolveResult::Certain(v) => {
canonicalized.apply_solution(
self,
Canonical {
@@ -731,13 +737,12 @@
},
);
}
- Solution::Ambig(Guidance::Definite(subst)) => {
- // FIXME need to record an obligation here
- canonicalized.apply_solution(self, subst)
+ // ...so, should think about how to get some actually get some guidance here
+ NextTraitSolveResult::Uncertain(..) | NextTraitSolveResult::NoSolution => {
+ return Err(TypeError);
}
- // FIXME actually we maybe should also accept unknown guidance here
- _ => return Err(TypeError),
- };
+ }
+
let unsize =
Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: to_ty.clone() };
let adjustments = match reborrow {
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 16fc2bf..261c023 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -278,6 +278,7 @@
}
}
+ #[tracing::instrument(level = "debug", skip(self, is_read), ret)]
fn infer_expr_inner(
&mut self,
tgt_expr: ExprId,
@@ -286,7 +287,9 @@
) -> Ty {
self.db.unwind_if_revision_cancelled();
- let ty = match &self.body[tgt_expr] {
+ let expr = &self.body[tgt_expr];
+ tracing::trace!(?expr);
+ let ty = match expr {
Expr::Missing => self.err_ty(),
&Expr::If { condition, then_branch, else_branch } => {
let expected = &expected.adjust_for_branches(&mut self.table);
@@ -1949,7 +1952,11 @@
sig.ret().clone(),
sig.is_varargs,
),
- None => ((self.err_ty(), Vec::new()), self.err_ty(), true),
+ None => {
+ let formal_receiver_ty = self.table.new_type_var();
+ let ret_ty = self.table.new_type_var();
+ ((formal_receiver_ty, Vec::new()), ret_ty, true)
+ }
};
self.unify(&formal_receiver_ty, &receiver_ty);
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index c077555..a709aeb 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -19,11 +19,13 @@
use super::{InferOk, InferResult, InferenceContext, TypeError};
use crate::{
AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal,
- GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, InferenceVar, Interner,
- Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution,
- TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause,
- consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts,
- to_chalk_trait_id, traits::FnTrait,
+ GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime,
+ OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment,
+ TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause,
+ consteval::unknown_const,
+ db::HirDatabase,
+ fold_generic_args, fold_tys_and_consts, to_chalk_trait_id,
+ traits::{FnTrait, NextTraitSolveResult},
};
impl InferenceContext<'_> {
@@ -116,9 +118,12 @@
// eagerly replace projections in the type; we may be getting types
// e.g. from where clauses where this hasn't happened yet
let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), Interner));
+ tracing::debug!("unifying {:?} {:?}", var, ty);
ctx.unify(var.assert_ty_ref(Interner), &ty);
} else {
- let _ = ctx.try_unify(var, &new_vars.apply(v.clone(), Interner));
+ let v = new_vars.apply(v.clone(), Interner);
+ tracing::debug!("try_unifying {:?} {:?}", var, v);
+ let _ = ctx.try_unify(var, &v);
}
}
}
@@ -326,6 +331,7 @@
/// type annotation (e.g. from a let type annotation, field type or function
/// call). `make_ty` handles this already, but e.g. for field types we need
/// to do it as well.
+ #[tracing::instrument(skip(self), ret)]
pub(crate) fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
@@ -333,12 +339,88 @@
fold_tys_and_consts(
ty,
|e, _| match e {
- Either::Left(ty) => Either::Left(match ty.kind(Interner) {
- TyKind::Alias(AliasTy::Projection(proj_ty)) => {
- self.normalize_projection_ty(proj_ty.clone())
- }
- _ => ty,
- }),
+ Either::Left(ty) => {
+ let ty = self.resolve_ty_shallow(&ty);
+ tracing::debug!(?ty);
+ Either::Left(match ty.kind(Interner) {
+ TyKind::Alias(AliasTy::Projection(proj_ty)) => {
+ let ty = self.normalize_projection_ty(proj_ty.clone());
+ self.resolve_ty_shallow(&ty)
+ }
+ TyKind::AssociatedType(id, subst) => {
+ if ty.data(Interner).flags.intersects(
+ chalk_ir::TypeFlags::HAS_TY_INFER
+ | chalk_ir::TypeFlags::HAS_CT_INFER,
+ ) {
+ return Either::Left(ty);
+ }
+ let var = self.new_type_var();
+ let proj_ty = chalk_ir::ProjectionTy {
+ associated_ty_id: *id,
+ substitution: subst.clone(),
+ };
+ let normalize = chalk_ir::Normalize {
+ alias: AliasTy::Projection(proj_ty),
+ ty: var.clone(),
+ };
+ let goal = chalk_ir::Goal::new(
+ Interner,
+ chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Normalize(
+ normalize,
+ )),
+ );
+ let in_env = InEnvironment::new(&self.trait_env.env, goal);
+
+ let canonicalized = {
+ let result =
+ self.var_unification_table.canonicalize(Interner, in_env);
+ let free_vars = result
+ .free_vars
+ .into_iter()
+ .map(|free_var| free_var.to_generic_arg(Interner))
+ .collect();
+ Canonicalized { value: result.quantified, free_vars }
+ };
+ let solution = self.db.trait_solve(
+ self.trait_env.krate,
+ self.trait_env.block,
+ canonicalized.value.clone(),
+ );
+ if let NextTraitSolveResult::Certain(canonical_subst) = solution {
+ // This is not great :) But let's just assert this for now and come back to it later.
+ if canonical_subst.value.subst.len(Interner) != 1 {
+ ty
+ } else {
+ let normalized = canonical_subst.value.subst.as_slice(Interner)
+ [0]
+ .assert_ty_ref(Interner);
+ match normalized.kind(Interner) {
+ TyKind::Alias(AliasTy::Projection(proj_ty)) => {
+ if id == &proj_ty.associated_ty_id
+ && subst == &proj_ty.substitution
+ {
+ ty
+ } else {
+ normalized.clone()
+ }
+ }
+ TyKind::AssociatedType(new_id, new_subst) => {
+ if new_id == id && new_subst == subst {
+ ty
+ } else {
+ normalized.clone()
+ }
+ }
+ _ => normalized.clone(),
+ }
+ }
+ } else {
+ ty
+ }
+ }
+ _ => ty,
+ })
+ }
Either::Right(c) => Either::Right(match &c.data(Interner).value {
chalk_ir::ConstValue::Concrete(cc) => match &cc.interned {
crate::ConstScalar::UnevaluatedConst(c_id, subst) => {
@@ -588,7 +670,6 @@
}
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
- #[tracing::instrument(skip_all)]
pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
let result = match self.try_unify(ty1, ty2) {
Ok(r) => r,
@@ -606,7 +687,7 @@
};
result.goals.iter().all(|goal| {
let canonicalized = self.canonicalize_with_free_vars(goal.clone());
- self.try_resolve_obligation(&canonicalized).is_some()
+ self.try_resolve_obligation(&canonicalized).certain()
})
}
@@ -633,6 +714,9 @@
/// If `ty` is a type variable with known type, returns that type;
/// otherwise, return ty.
pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty {
+ if !ty.data(Interner).flags.intersects(chalk_ir::TypeFlags::HAS_FREE_LOCAL_NAMES) {
+ return ty.clone();
+ }
self.resolve_obligations_as_possible();
self.var_unification_table.normalize_ty_shallow(Interner, ty).unwrap_or_else(|| ty.clone())
}
@@ -662,7 +746,8 @@
/// Checks an obligation without registering it. Useful mostly to check
/// whether a trait *might* be implemented before deciding to 'lock in' the
/// choice (during e.g. method resolution or deref).
- pub(crate) fn try_obligation(&mut self, goal: Goal) -> Option<Solution> {
+ #[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);
@@ -674,10 +759,45 @@
self.register_obligation_in_env(in_env)
}
+ #[tracing::instrument(level = "debug", skip(self))]
fn register_obligation_in_env(&mut self, goal: InEnvironment<Goal>) {
- let canonicalized = self.canonicalize_with_free_vars(goal);
+ match goal.goal.data(Interner) {
+ chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
+ chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }),
+ )) => {
+ if ty.inference_var(Interner).is_some() {
+ match alias {
+ chalk_ir::AliasTy::Opaque(opaque) => {
+ if self.unify(
+ &chalk_ir::TyKind::OpaqueType(
+ opaque.opaque_ty_id,
+ opaque.substitution.clone(),
+ )
+ .intern(Interner),
+ ty,
+ ) {
+ return;
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+ _ => {}
+ }
+ let canonicalized = {
+ let result = self.var_unification_table.canonicalize(Interner, goal);
+ let free_vars = result
+ .free_vars
+ .into_iter()
+ .map(|free_var| free_var.to_generic_arg(Interner))
+ .collect();
+ Canonicalized { value: result.quantified, free_vars }
+ };
+ tracing::debug!(?canonicalized);
let solution = self.try_resolve_obligation(&canonicalized);
- if matches!(solution, Some(Solution::Ambig(_))) {
+ tracing::debug!(?solution);
+ if solution.uncertain() {
self.pending_obligations.push(canonicalized);
}
}
@@ -694,7 +814,9 @@
mem::swap(&mut self.pending_obligations, &mut obligations);
for canonicalized in obligations.drain(..) {
+ tracing::debug!(obligation = ?canonicalized);
if !self.check_changed(&canonicalized) {
+ tracing::debug!("not changed");
self.pending_obligations.push(canonicalized);
continue;
}
@@ -799,37 +921,36 @@
})
}
+ #[tracing::instrument(level = "debug", skip(self))]
fn try_resolve_obligation(
&mut self,
canonicalized: &Canonicalized<InEnvironment<Goal>>,
- ) -> Option<chalk_solve::Solution<Interner>> {
+ ) -> NextTraitSolveResult {
let solution = self.db.trait_solve(
self.trait_env.krate,
self.trait_env.block,
canonicalized.value.clone(),
);
+ tracing::debug!(?solution, ?canonicalized);
match &solution {
- Some(Solution::Unique(canonical_subst)) => {
+ NextTraitSolveResult::Certain(v) => {
canonicalized.apply_solution(
self,
Canonical {
- binders: canonical_subst.binders.clone(),
- // FIXME: handle constraints
- value: canonical_subst.value.subst.clone(),
+ binders: v.binders.clone(),
+ // FIXME handle constraints
+ value: v.value.subst.clone(),
},
);
}
- Some(Solution::Ambig(Guidance::Definite(substs))) => {
- canonicalized.apply_solution(self, substs.clone());
+ // ...so, should think about how to get some actually get some guidance here
+ NextTraitSolveResult::Uncertain(v) => {
+ canonicalized.apply_solution(self, v.clone());
}
- Some(_) => {
- // FIXME use this when trying to resolve everything at the end
- }
- None => {
- // FIXME obligation cannot be fulfilled => diagnostic
- }
+ NextTraitSolveResult::NoSolution => {}
}
+
solution
}
@@ -896,7 +1017,10 @@
environment: trait_env.clone(),
};
let canonical = self.canonicalize(obligation.clone());
- if self.db.trait_solve(krate, self.trait_env.block, canonical.cast(Interner)).is_some()
+ if !self
+ .db
+ .trait_solve(krate, self.trait_env.block, canonical.cast(Interner))
+ .no_solution()
{
self.register_obligation(obligation.goal);
let return_ty = self.normalize_projection_ty(projection);
@@ -909,10 +1033,10 @@
environment: trait_env.clone(),
};
let canonical = self.canonicalize(obligation.clone());
- if self
+ if !self
.db
.trait_solve(krate, self.trait_env.block, canonical.cast(Interner))
- .is_some()
+ .no_solution()
{
return Some((fn_x, arg_tys, return_ty));
}
@@ -1032,7 +1156,7 @@
substitution: Substitution::from1(Interner, ty),
});
let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(sized_pred)).intern(Interner);
- matches!(self.try_obligation(goal), Some(Solution::Unique(_)))
+ self.try_obligation(goal).certain()
}
}
diff --git a/crates/hir-ty/src/interner.rs b/crates/hir-ty/src/interner.rs
index fecb3f4..57ef552 100644
--- a/crates/hir-ty/src/interner.rs
+++ b/crates/hir-ty/src/interner.rs
@@ -2,11 +2,10 @@
//! representation of the various objects Chalk deals with (types, goals etc.).
use crate::{
- AliasTy, CanonicalVarKind, CanonicalVarKinds, ClosureId, Const, ConstData, ConstScalar,
- Constraint, Constraints, FnAbi, FnDefId, GenericArg, GenericArgData, Goal, GoalData, Goals,
- InEnvironment, Lifetime, LifetimeData, OpaqueTy, OpaqueTyId, ProgramClause, ProgramClauseData,
- ProgramClauses, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, Ty,
- TyData, TyKind, VariableKind, VariableKinds, chalk_db, tls,
+ AliasTy, CanonicalVarKind, CanonicalVarKinds, ClosureId, Const, ConstData, ConstScalar, FnAbi,
+ FnDefId, GenericArg, GenericArgData, Goal, GoalData, InEnvironment, Lifetime, LifetimeData,
+ OpaqueTy, OpaqueTyId, ProgramClause, ProjectionTy, QuantifiedWhereClause,
+ QuantifiedWhereClauses, Substitution, Ty, TyKind, VariableKind, chalk_db, tls,
};
use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variance};
use hir_def::TypeAliasId;
@@ -15,11 +14,19 @@
use std::fmt;
use triomphe::Arc;
+type TyData = chalk_ir::TyData<Interner>;
+type VariableKinds = chalk_ir::VariableKinds<Interner>;
+type Goals = chalk_ir::Goals<Interner>;
+type ProgramClauseData = chalk_ir::ProgramClauseData<Interner>;
+type Constraint = chalk_ir::Constraint<Interner>;
+type Constraints = chalk_ir::Constraints<Interner>;
+type ProgramClauses = chalk_ir::ProgramClauses<Interner>;
+
#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct Interner;
-#[derive(PartialEq, Eq, Hash)]
-pub struct InternedWrapper<T>(T);
+#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
+pub struct InternedWrapper<T>(pub(crate) T);
impl<T: fmt::Debug> fmt::Debug for InternedWrapper<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -27,6 +34,9 @@
}
}
+#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
+pub struct InternedWrapperNoDebug<T>(pub(crate) T);
+
impl<T> std::ops::Deref for InternedWrapper<T> {
type Target = T;
@@ -124,6 +134,7 @@
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt)))
+ .or_else(|| Some(fmt.write_str("ProjectionTy")))
}
fn debug_opaque_ty(opaque_ty: &OpaqueTy, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
index 107da6a..819bd58 100644
--- a/crates/hir-ty/src/layout.rs
+++ b/crates/hir-ty/src/layout.rs
@@ -2,33 +2,43 @@
use std::fmt;
-use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy};
use hir_def::{
- LocalFieldId, StructId,
- layout::{
- Float, Integer, LayoutCalculator, LayoutCalculatorError, LayoutData, Primitive,
- ReprOptions, Scalar, StructKind, TargetDataLayout, WrappingRange,
- },
+ AdtId, LocalFieldId, StructId,
+ layout::{LayoutCalculatorError, LayoutData},
};
use la_arena::{Idx, RawIdx};
-use rustc_abi::AddressSpace;
-use rustc_index::IndexVec;
+use rustc_abi::{
+ AddressSpace, Float, Integer, LayoutCalculator, Primitive, ReprOptions, Scalar, StructKind,
+ TargetDataLayout, WrappingRange,
+};
+use rustc_index::IndexVec;
+use rustc_type_ir::{
+ FloatTy, IntTy, UintTy,
+ inherent::{IntoKind, SliceLike},
+};
use triomphe::Arc;
use crate::{
- Interner, ProjectionTy, Substitution, TraitEnvironment, Ty,
- consteval::try_const_usize,
- db::{HirDatabase, InternedClosure},
- infer::normalize,
- utils::ClosureSubst,
+ TraitEnvironment,
+ consteval_nextsolver::try_const_usize,
+ db::HirDatabase,
+ next_solver::{
+ DbInterner, GenericArgs, ParamEnv, SolverDefId, Ty, TyKind, TypingMode,
+ infer::{DbInternerInferExt, traits::ObligationCause},
+ mapping::{ChalkToNextSolver, convert_binder_to_early_binder},
+ project::solve_normalize::deeply_normalize,
+ },
};
-pub(crate) use self::adt::layout_of_adt_cycle_result;
-pub use self::{adt::layout_of_adt_query, target::target_data_layout_query};
+pub(crate) use self::adt::{layout_of_adt_cycle_result, layout_of_adt_ns_cycle_result};
+pub use self::{
+ adt::{layout_of_adt_ns_query, layout_of_adt_query},
+ target::target_data_layout_query,
+};
-mod adt;
-mod target;
+pub(crate) mod adt;
+pub(crate) mod target;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RustcEnumVariantIdx(pub usize);
@@ -119,11 +129,12 @@
}
}
-fn layout_of_simd_ty(
- db: &dyn HirDatabase,
+// FIXME: move this to the `rustc_abi`.
+fn layout_of_simd_ty<'db>(
+ db: &'db dyn HirDatabase,
id: StructId,
repr_packed: bool,
- subst: &Substitution,
+ args: &GenericArgs<'db>,
env: Arc<TraitEnvironment>,
dl: &TargetDataLayout,
) -> Result<Arc<Layout>, LayoutError> {
@@ -132,18 +143,18 @@
// * #[repr(simd)] struct S([T; 4])
//
// where T is a primitive scalar (integer/float/pointer).
- let fields = db.field_types(id.into());
+ let fields = db.field_types_ns(id.into());
let mut fields = fields.iter();
let Some(TyKind::Array(e_ty, e_len)) = fields
.next()
.filter(|_| fields.next().is_none())
- .map(|f| f.1.clone().substitute(Interner, subst).kind(Interner).clone())
+ .map(|f| (*f.1).instantiate(DbInterner::new_with(db, None, None), args).kind())
else {
return Err(LayoutError::InvalidSimdType);
};
let e_len = try_const_usize(db, &e_len).ok_or(LayoutError::HasErrorConst)? as u64;
- let e_ly = db.layout_of_ty(e_ty, env)?;
+ let e_ly = db.layout_of_ty_ns(e_ty, env)?;
let cx = LayoutCx::new(dl);
Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?))
@@ -151,108 +162,122 @@
pub fn layout_of_ty_query(
db: &dyn HirDatabase,
- ty: Ty,
+ ty: crate::Ty,
trait_env: Arc<TraitEnvironment>,
) -> Result<Arc<Layout>, LayoutError> {
let krate = trait_env.krate;
+ let interner = DbInterner::new_with(db, Some(krate), trait_env.block);
+ db.layout_of_ty_ns(ty.to_nextsolver(interner), trait_env)
+}
+
+pub fn layout_of_ty_ns_query<'db>(
+ db: &'db dyn HirDatabase,
+ ty: Ty<'db>,
+ trait_env: Arc<TraitEnvironment>,
+) -> Result<Arc<Layout>, LayoutError> {
+ let krate = trait_env.krate;
+ let interner = DbInterner::new_with(db, Some(krate), trait_env.block);
let Ok(target) = db.target_data_layout(krate) else {
return Err(LayoutError::TargetLayoutNotAvailable);
};
let dl = &*target;
let cx = LayoutCx::new(dl);
- let ty = normalize(db, trait_env.clone(), ty);
- let kind = ty.kind(Interner);
- let result = match kind {
- TyKind::Adt(AdtId(def), subst) => {
- if let hir_def::AdtId::StructId(s) = def {
- let data = db.struct_signature(*s);
- let repr = data.repr.unwrap_or_default();
- if repr.simd() {
- return layout_of_simd_ty(db, *s, repr.packed(), subst, trait_env, &target);
+ let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis());
+ let cause = ObligationCause::dummy();
+ let ty = deeply_normalize(infer_ctxt.at(&cause, ParamEnv::empty()), ty).unwrap_or(ty);
+ let result = match ty.kind() {
+ TyKind::Adt(def, args) => {
+ match def.inner().id {
+ hir_def::AdtId::StructId(s) => {
+ let data = db.struct_signature(s);
+ let repr = data.repr.unwrap_or_default();
+ if repr.simd() {
+ return layout_of_simd_ty(db, s, repr.packed(), &args, trait_env, &target);
+ }
}
- };
- return db.layout_of_adt(*def, subst.clone(), trait_env);
+ _ => {}
+ }
+ return db.layout_of_adt_ns(def.inner().id, args, trait_env);
}
- TyKind::Scalar(s) => match s {
- chalk_ir::Scalar::Bool => Layout::scalar(
+ TyKind::Bool => Layout::scalar(
+ dl,
+ Scalar::Initialized {
+ value: Primitive::Int(Integer::I8, false),
+ valid_range: WrappingRange { start: 0, end: 1 },
+ },
+ ),
+ TyKind::Char => Layout::scalar(
+ dl,
+ Scalar::Initialized {
+ value: Primitive::Int(Integer::I32, false),
+ valid_range: WrappingRange { start: 0, end: 0x10FFFF },
+ },
+ ),
+ TyKind::Int(i) => Layout::scalar(
+ dl,
+ scalar_unit(
dl,
- Scalar::Initialized {
- value: Primitive::Int(Integer::I8, false),
- valid_range: WrappingRange { start: 0, end: 1 },
- },
- ),
- chalk_ir::Scalar::Char => Layout::scalar(
- dl,
- Scalar::Initialized {
- value: Primitive::Int(Integer::I32, false),
- valid_range: WrappingRange { start: 0, end: 0x10FFFF },
- },
- ),
- chalk_ir::Scalar::Int(i) => Layout::scalar(
- dl,
- scalar_unit(
- dl,
- Primitive::Int(
- match i {
- IntTy::Isize => dl.ptr_sized_integer(),
- IntTy::I8 => Integer::I8,
- IntTy::I16 => Integer::I16,
- IntTy::I32 => Integer::I32,
- IntTy::I64 => Integer::I64,
- IntTy::I128 => Integer::I128,
- },
- true,
- ),
+ Primitive::Int(
+ match i {
+ IntTy::Isize => dl.ptr_sized_integer(),
+ IntTy::I8 => Integer::I8,
+ IntTy::I16 => Integer::I16,
+ IntTy::I32 => Integer::I32,
+ IntTy::I64 => Integer::I64,
+ IntTy::I128 => Integer::I128,
+ },
+ true,
),
),
- chalk_ir::Scalar::Uint(i) => Layout::scalar(
+ ),
+ TyKind::Uint(i) => Layout::scalar(
+ dl,
+ scalar_unit(
dl,
- scalar_unit(
- dl,
- Primitive::Int(
- match i {
- UintTy::Usize => dl.ptr_sized_integer(),
- UintTy::U8 => Integer::I8,
- UintTy::U16 => Integer::I16,
- UintTy::U32 => Integer::I32,
- UintTy::U64 => Integer::I64,
- UintTy::U128 => Integer::I128,
- },
- false,
- ),
+ Primitive::Int(
+ match i {
+ UintTy::Usize => dl.ptr_sized_integer(),
+ UintTy::U8 => Integer::I8,
+ UintTy::U16 => Integer::I16,
+ UintTy::U32 => Integer::I32,
+ UintTy::U64 => Integer::I64,
+ UintTy::U128 => Integer::I128,
+ },
+ false,
),
),
- chalk_ir::Scalar::Float(f) => Layout::scalar(
+ ),
+ TyKind::Float(f) => Layout::scalar(
+ dl,
+ scalar_unit(
dl,
- scalar_unit(
- dl,
- Primitive::Float(match f {
- FloatTy::F16 => Float::F16,
- FloatTy::F32 => Float::F32,
- FloatTy::F64 => Float::F64,
- FloatTy::F128 => Float::F128,
- }),
- ),
+ Primitive::Float(match f {
+ FloatTy::F16 => Float::F16,
+ FloatTy::F32 => Float::F32,
+ FloatTy::F64 => Float::F64,
+ FloatTy::F128 => Float::F128,
+ }),
),
- },
- TyKind::Tuple(len, tys) => {
- let kind = if *len == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
+ ),
+ TyKind::Tuple(tys) => {
+ let kind =
+ if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
let fields = tys
- .iter(Interner)
- .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), trait_env.clone()))
+ .iter()
+ .map(|k| db.layout_of_ty_ns(k, trait_env.clone()))
.collect::<Result<Vec<_>, _>>()?;
let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
let fields = fields.iter().collect::<IndexVec<_, _>>();
cx.calc.univariant(&fields, &ReprOptions::default(), kind)?
}
TyKind::Array(element, count) => {
- let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64;
- let element = db.layout_of_ty(element.clone(), trait_env)?;
+ let count = try_const_usize(db, &count).ok_or(LayoutError::HasErrorConst)? as u64;
+ let element = db.layout_of_ty_ns(element, trait_env)?;
cx.calc.array_like::<_, _, ()>(&element, Some(count))?
}
TyKind::Slice(element) => {
- let element = db.layout_of_ty(element.clone(), trait_env)?;
+ let element = db.layout_of_ty_ns(element, trait_env)?;
cx.calc.array_like::<_, _, ()>(&element, None)?
}
TyKind::Str => {
@@ -260,18 +285,21 @@
cx.calc.array_like::<_, _, ()>(&Layout::scalar(dl, element), None)?
}
// Potentially-wide pointers.
- TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => {
+ TyKind::Ref(_, pointee, _) | TyKind::RawPtr(pointee, _) => {
let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
- if matches!(ty.kind(Interner), TyKind::Ref(..)) {
+ if matches!(ty.kind(), TyKind::Ref(..)) {
data_ptr.valid_range_mut().start = 1;
}
+ // FIXME(next-solver)
// let pointee = tcx.normalize_erasing_regions(param_env, pointee);
// if pointee.is_sized(tcx.at(DUMMY_SP), param_env) {
- // return Ok(tcx.mk_layout(LayoutData::scalar(cx, data_ptr)));
+ // return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
// }
- let mut unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone());
+ let unsized_part = struct_tail_erasing_lifetimes(db, pointee);
+ // FIXME(next-solver)
+ /*
if let TyKind::AssociatedType(id, subst) = unsized_part.kind(Interner) {
unsized_part = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
associated_ty_id: *id,
@@ -280,11 +308,12 @@
.intern(Interner);
}
unsized_part = normalize(db, trait_env, unsized_part);
- let metadata = match unsized_part.kind(Interner) {
+ */
+ let metadata = match unsized_part.kind() {
TyKind::Slice(_) | TyKind::Str => {
scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
}
- TyKind::Dyn(..) => {
+ TyKind::Dynamic(..) => {
let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
vtable.valid_range_mut().start = 1;
vtable
@@ -299,97 +328,110 @@
LayoutData::scalar_pair(dl, data_ptr, metadata)
}
TyKind::Never => LayoutData::never_type(dl),
- TyKind::FnDef(..) | TyKind::Dyn(_) | TyKind::Foreign(_) => {
- let sized = matches!(kind, TyKind::FnDef(..));
- LayoutData::unit(dl, sized)
- }
- TyKind::Function(_) => {
+ TyKind::FnDef(..) => LayoutData::unit(dl, true),
+ TyKind::Dynamic(..) | TyKind::Foreign(_) => LayoutData::unit(dl, false),
+ TyKind::FnPtr(..) => {
let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space));
ptr.valid_range_mut().start = 1;
Layout::scalar(dl, ptr)
}
- TyKind::OpaqueType(opaque_ty_id, _) => {
- let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
- match impl_trait_id {
- crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
- let infer = db.infer(func.into());
- return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env);
- }
- crate::ImplTraitId::TypeAliasImplTrait(..) => {
- return Err(LayoutError::NotImplemented);
- }
- crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
- return Err(LayoutError::NotImplemented);
+ TyKind::Alias(_, ty) => match ty.def_id {
+ SolverDefId::TypeAliasId(_) => {
+ return Err(LayoutError::HasPlaceholder);
+ }
+ SolverDefId::InternedOpaqueTyId(opaque) => {
+ let impl_trait_id = db.lookup_intern_impl_trait_id(opaque);
+ match impl_trait_id {
+ crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
+ let infer = db.infer(func.into());
+ return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env);
+ }
+ crate::ImplTraitId::TypeAliasImplTrait(..) => {
+ return Err(LayoutError::NotImplemented);
+ }
+ crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
+ return Err(LayoutError::NotImplemented);
+ }
}
}
- }
- TyKind::Closure(c, subst) => {
- let InternedClosure(def, _) = db.lookup_intern_closure((*c).into());
- let infer = db.infer(def);
- let (captures, _) = infer.closure_info(c);
+ _ => unreachable!(),
+ },
+ TyKind::Closure(c, args) => {
+ let id = match c {
+ SolverDefId::InternedClosureId(id) => id,
+ _ => unreachable!(),
+ };
+ let def = db.lookup_intern_closure(id);
+ let infer = db.infer(def.0);
+ let (captures, _) = infer.closure_info(&id.into());
let fields = captures
.iter()
.map(|it| {
- db.layout_of_ty(
- it.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()),
- trait_env.clone(),
- )
+ let ty =
+ convert_binder_to_early_binder(interner, it.ty.to_nextsolver(interner))
+ .instantiate(interner, args);
+ db.layout_of_ty_ns(ty, trait_env.clone())
})
.collect::<Result<Vec<_>, _>>()?;
let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
let fields = fields.iter().collect::<IndexVec<_, _>>();
cx.calc.univariant(&fields, &ReprOptions::default(), StructKind::AlwaysSized)?
}
- TyKind::Coroutine(_, _) | TyKind::CoroutineWitness(_, _) => {
+
+ TyKind::Coroutine(_, _)
+ | TyKind::CoroutineWitness(_, _)
+ | TyKind::CoroutineClosure(_, _) => {
return Err(LayoutError::NotImplemented);
}
- TyKind::Error => return Err(LayoutError::HasErrorType),
- TyKind::AssociatedType(id, subst) => {
- // Try again with `TyKind::Alias` to normalize the associated type.
- // Usually we should not try to normalize `TyKind::AssociatedType`, but layout calculation is used
- // in monomorphized MIR where this is okay. If outside monomorphization, this will lead to cycle,
- // which we will recover from with an error.
- let ty = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
- associated_ty_id: *id,
- substitution: subst.clone(),
- }))
- .intern(Interner);
- return db.layout_of_ty(ty, trait_env);
+
+ TyKind::Pat(_, _) | TyKind::UnsafeBinder(_) => {
+ return Err(LayoutError::NotImplemented);
}
- TyKind::Alias(_)
- | TyKind::Placeholder(_)
- | TyKind::BoundVar(_)
- | TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder),
+
+ TyKind::Error(_) => return Err(LayoutError::HasErrorType),
+ TyKind::Placeholder(_) | TyKind::Bound(..) | TyKind::Infer(..) | TyKind::Param(..) => {
+ return Err(LayoutError::HasPlaceholder);
+ }
};
Ok(Arc::new(result))
}
pub(crate) fn layout_of_ty_cycle_result(
_: &dyn HirDatabase,
- _: Ty,
+ _: crate::Ty,
_: Arc<TraitEnvironment>,
) -> Result<Arc<Layout>, LayoutError> {
Err(LayoutError::RecursiveTypeWithoutIndirection)
}
-fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty {
- match pointee.kind(Interner) {
- &TyKind::Adt(AdtId(hir_def::AdtId::StructId(i)), ref subst) => {
- let data = i.fields(db);
+pub(crate) fn layout_of_ty_ns_cycle_result<'db>(
+ _: &dyn HirDatabase,
+ _: Ty<'db>,
+ _: Arc<TraitEnvironment>,
+) -> Result<Arc<Layout>, LayoutError> {
+ Err(LayoutError::RecursiveTypeWithoutIndirection)
+}
+
+fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> {
+ match pointee.kind() {
+ TyKind::Adt(def, args) => {
+ let struct_id = match def.inner().id {
+ AdtId::StructId(id) => id,
+ _ => return pointee,
+ };
+ let data = struct_id.fields(db);
let mut it = data.fields().iter().rev();
match it.next() {
Some((f, _)) => {
- let last_field_ty = field_ty(db, i.into(), f, subst);
+ let last_field_ty = field_ty(db, struct_id.into(), f, &args);
struct_tail_erasing_lifetimes(db, last_field_ty)
}
None => pointee,
}
}
- TyKind::Tuple(_, subst) => {
- if let Some(last_field_ty) =
- subst.iter(Interner).last().and_then(|arg| arg.ty(Interner))
- {
- struct_tail_erasing_lifetimes(db, last_field_ty.clone())
+ TyKind::Tuple(tys) => {
+ if let Some(last_field_ty) = tys.iter().last() {
+ struct_tail_erasing_lifetimes(db, last_field_ty)
} else {
pointee
}
@@ -398,13 +440,13 @@
}
}
-fn field_ty(
- db: &dyn HirDatabase,
+fn field_ty<'a>(
+ db: &'a dyn HirDatabase,
def: hir_def::VariantId,
fd: LocalFieldId,
- subst: &Substitution,
-) -> Ty {
- db.field_types(def)[fd].clone().substitute(Interner, subst)
+ args: &GenericArgs<'a>,
+) -> Ty<'a> {
+ db.field_types_ns(def)[fd].instantiate(DbInterner::new_with(db, None, None), args)
}
fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs
index 3f310c2..2fa01b6 100644
--- a/crates/hir-ty/src/layout/adt.rs
+++ b/crates/hir-ty/src/layout/adt.rs
@@ -4,10 +4,10 @@
use hir_def::{
AdtId, VariantId,
- layout::{Integer, ReprOptions, TargetDataLayout},
signatures::{StructFlags, VariantFields},
};
use intern::sym;
+use rustc_abi::{Integer, ReprOptions, TargetDataLayout};
use rustc_index::IndexVec;
use smallvec::SmallVec;
use triomphe::Arc;
@@ -15,17 +15,26 @@
use crate::{
Substitution, TraitEnvironment,
db::HirDatabase,
- layout::{Layout, LayoutError, field_ty},
+ layout::{Layout, LayoutCx, LayoutError, field_ty},
+ next_solver::{DbInterner, GenericArgs, mapping::ChalkToNextSolver},
};
-use super::LayoutCx;
-
pub fn layout_of_adt_query(
db: &dyn HirDatabase,
def: AdtId,
subst: Substitution,
trait_env: Arc<TraitEnvironment>,
) -> Result<Arc<Layout>, LayoutError> {
+ let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block);
+ db.layout_of_adt_ns(def, subst.to_nextsolver(interner), trait_env)
+}
+
+pub fn layout_of_adt_ns_query<'db>(
+ db: &'db dyn HirDatabase,
+ def: AdtId,
+ args: GenericArgs<'db>,
+ trait_env: Arc<TraitEnvironment>,
+) -> Result<Arc<Layout>, LayoutError> {
let krate = trait_env.krate;
let Ok(target) = db.target_data_layout(krate) else {
return Err(LayoutError::TargetLayoutNotAvailable);
@@ -35,7 +44,7 @@
let handle_variant = |def: VariantId, var: &VariantFields| {
var.fields()
.iter()
- .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), trait_env.clone()))
+ .map(|(fd, _)| db.layout_of_ty_ns(field_ty(db, def, fd, &args), trait_env.clone()))
.collect::<Result<Vec<_>, _>>()
};
let (variants, repr, is_special_no_niche) = match def {
@@ -96,6 +105,24 @@
Ok(Arc::new(result))
}
+pub(crate) fn layout_of_adt_cycle_result(
+ _: &dyn HirDatabase,
+ _: AdtId,
+ _: Substitution,
+ _: Arc<TraitEnvironment>,
+) -> Result<Arc<Layout>, LayoutError> {
+ Err(LayoutError::RecursiveTypeWithoutIndirection)
+}
+
+pub(crate) fn layout_of_adt_ns_cycle_result<'db>(
+ _: &'db dyn HirDatabase,
+ _def: AdtId,
+ _args: GenericArgs<'db>,
+ _trait_env: Arc<TraitEnvironment>,
+) -> Result<Arc<Layout>, LayoutError> {
+ Err(LayoutError::RecursiveTypeWithoutIndirection)
+}
+
fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
let attrs = db.attrs(def.into());
let get = |name| {
@@ -120,15 +147,6 @@
(get(sym::rustc_layout_scalar_valid_range_start), get(sym::rustc_layout_scalar_valid_range_end))
}
-pub(crate) fn layout_of_adt_cycle_result(
- _: &dyn HirDatabase,
- _: AdtId,
- _: Substitution,
- _: Arc<TraitEnvironment>,
-) -> Result<Arc<Layout>, LayoutError> {
- Err(LayoutError::RecursiveTypeWithoutIndirection)
-}
-
/// Finds the appropriate Integer type and signedness for the given
/// signed discriminant range and `#[repr]` attribute.
/// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
index b3bc226..93f2e12 100644
--- a/crates/hir-ty/src/layout/tests.rs
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -11,6 +11,7 @@
Interner, Substitution,
db::HirDatabase,
layout::{Layout, LayoutError},
+ setup_tracing,
test_db::TestDB,
};
@@ -29,6 +30,7 @@
#[rust_analyzer::rust_fixture] ra_fixture: &str,
minicore: &str,
) -> Result<Arc<Layout>, LayoutError> {
+ let _tracing = setup_tracing();
let target_data_layout = current_machine_data_layout();
let ra_fixture = format!(
"//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\n{ra_fixture}",
@@ -97,6 +99,7 @@
#[rust_analyzer::rust_fixture] ra_fixture: &str,
minicore: &str,
) -> Result<Arc<Layout>, LayoutError> {
+ let _tracing = setup_tracing();
let target_data_layout = current_machine_data_layout();
let ra_fixture = format!(
"//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\nfn main(){{let goal = {{{ra_fixture}}};}}",
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index e787fd9..2b0dc93 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -21,6 +21,24 @@
#[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;
+
mod builder;
mod chalk_db;
mod chalk_ext;
@@ -29,13 +47,16 @@
mod inhabitedness;
mod interner;
mod lower;
+mod lower_nextsolver;
mod mapping;
+pub mod next_solver;
mod target_feature;
mod tls;
mod utils;
pub mod autoderef;
pub mod consteval;
+pub mod consteval_nextsolver;
pub mod db;
pub mod diagnostics;
pub mod display;
@@ -57,7 +78,7 @@
use std::hash::Hash;
use chalk_ir::{
- NoSolution,
+ NoSolution, VariableKinds,
fold::{Shift, TypeFoldable},
interner::HasInterner,
};
@@ -121,9 +142,9 @@
pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>;
pub type PlaceholderIndex = chalk_ir::PlaceholderIndex;
-pub type VariableKind = chalk_ir::VariableKind<Interner>;
-pub type VariableKinds = chalk_ir::VariableKinds<Interner>;
pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds<Interner>;
+
+pub(crate) type VariableKind = chalk_ir::VariableKind<Interner>;
/// Represents generic parameters and an item bound by them. When the item has parent, the binders
/// also contain the generic parameters for its parent. See chalk's documentation for details.
///
@@ -145,52 +166,45 @@
pub type Ty = chalk_ir::Ty<Interner>;
pub type TyKind = chalk_ir::TyKind<Interner>;
pub type TypeFlags = chalk_ir::TypeFlags;
-pub type DynTy = chalk_ir::DynTy<Interner>;
+pub(crate) type DynTy = chalk_ir::DynTy<Interner>;
pub type FnPointer = chalk_ir::FnPointer<Interner>;
-// pub type FnSubst = chalk_ir::FnSubst<Interner>; // a re-export so we don't lose the tuple constructor
-pub use chalk_ir::FnSubst;
-pub type ProjectionTy = chalk_ir::ProjectionTy<Interner>;
-pub type AliasTy = chalk_ir::AliasTy<Interner>;
-pub type OpaqueTy = chalk_ir::OpaqueTy<Interner>;
-pub type InferenceVar = chalk_ir::InferenceVar;
+pub(crate) use chalk_ir::FnSubst; // a re-export so we don't lose the tuple constructor
-pub type Lifetime = chalk_ir::Lifetime<Interner>;
-pub type LifetimeData = chalk_ir::LifetimeData<Interner>;
-pub type LifetimeOutlives = chalk_ir::LifetimeOutlives<Interner>;
+pub type AliasTy = chalk_ir::AliasTy<Interner>;
+
+pub type ProjectionTy = chalk_ir::ProjectionTy<Interner>;
+pub(crate) type OpaqueTy = chalk_ir::OpaqueTy<Interner>;
+pub(crate) type InferenceVar = chalk_ir::InferenceVar;
+
+pub(crate) type Lifetime = chalk_ir::Lifetime<Interner>;
+pub(crate) type LifetimeData = chalk_ir::LifetimeData<Interner>;
+pub(crate) type LifetimeOutlives = chalk_ir::LifetimeOutlives<Interner>;
+
+pub type ConstValue = chalk_ir::ConstValue<Interner>;
pub type Const = chalk_ir::Const<Interner>;
-pub type ConstData = chalk_ir::ConstData<Interner>;
-pub type ConstValue = chalk_ir::ConstValue<Interner>;
-pub type ConcreteConst = chalk_ir::ConcreteConst<Interner>;
+pub(crate) type ConstData = chalk_ir::ConstData<Interner>;
+pub(crate) type ConcreteConst = chalk_ir::ConcreteConst<Interner>;
-pub type ChalkTraitId = chalk_ir::TraitId<Interner>;
pub type TraitRef = chalk_ir::TraitRef<Interner>;
pub type QuantifiedWhereClause = Binders<WhereClause>;
-pub type QuantifiedWhereClauses = chalk_ir::QuantifiedWhereClauses<Interner>;
pub type Canonical<T> = chalk_ir::Canonical<T>;
-pub type FnSig = chalk_ir::FnSig<Interner>;
+pub(crate) type ChalkTraitId = chalk_ir::TraitId<Interner>;
+pub(crate) type QuantifiedWhereClauses = chalk_ir::QuantifiedWhereClauses<Interner>;
+
+pub(crate) type FnSig = chalk_ir::FnSig<Interner>;
pub type InEnvironment<T> = chalk_ir::InEnvironment<T>;
-pub type Environment = chalk_ir::Environment<Interner>;
-pub type DomainGoal = chalk_ir::DomainGoal<Interner>;
-pub type Goal = chalk_ir::Goal<Interner>;
pub type AliasEq = chalk_ir::AliasEq<Interner>;
-pub type Solution = chalk_solve::Solution<Interner>;
-pub type Constraint = chalk_ir::Constraint<Interner>;
-pub type Constraints = chalk_ir::Constraints<Interner>;
-pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>;
-pub type Guidance = chalk_solve::Guidance<Interner>;
pub type WhereClause = chalk_ir::WhereClause<Interner>;
-pub type CanonicalVarKind = chalk_ir::CanonicalVarKind<Interner>;
-pub type GoalData = chalk_ir::GoalData<Interner>;
-pub type Goals = chalk_ir::Goals<Interner>;
-pub type ProgramClauseData = chalk_ir::ProgramClauseData<Interner>;
-pub type ProgramClause = chalk_ir::ProgramClause<Interner>;
-pub type ProgramClauses = chalk_ir::ProgramClauses<Interner>;
-pub type TyData = chalk_ir::TyData<Interner>;
-pub type Variances = chalk_ir::Variances<Interner>;
+pub(crate) type DomainGoal = chalk_ir::DomainGoal<Interner>;
+pub(crate) type Goal = chalk_ir::Goal<Interner>;
+
+pub(crate) type CanonicalVarKind = chalk_ir::CanonicalVarKind<Interner>;
+pub(crate) type GoalData = chalk_ir::GoalData<Interner>;
+pub(crate) type ProgramClause = chalk_ir::ProgramClause<Interner>;
/// A constant can have reference to other things. Memory map job is holding
/// the necessary bits of memory of the const eval session to keep the constant
@@ -311,30 +325,11 @@
Binders::empty(Interner, value.shifted_in_from(Interner, DebruijnIndex::ONE))
}
-pub(crate) fn make_type_and_const_binders<T: HasInterner<Interner = Interner>>(
- which_is_const: impl Iterator<Item = Option<Ty>>,
- value: T,
-) -> Binders<T> {
- Binders::new(
- VariableKinds::from_iter(
- Interner,
- which_is_const.map(|x| {
- if let Some(ty) = x {
- chalk_ir::VariableKind::Const(ty)
- } else {
- chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
- }
- }),
- ),
- value,
- )
-}
-
pub(crate) fn make_single_type_binders<T: HasInterner<Interner = Interner>>(
value: T,
) -> Binders<T> {
Binders::new(
- VariableKinds::from_iter(
+ chalk_ir::VariableKinds::from_iter(
Interner,
std::iter::once(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)),
),
@@ -353,7 +348,7 @@
pub(crate) fn variable_kinds_from_iter(
db: &dyn HirDatabase,
iter: impl Iterator<Item = hir_def::GenericParamId>,
-) -> VariableKinds {
+) -> VariableKinds<Interner> {
VariableKinds::from_iter(
Interner,
iter.map(|x| match x {
@@ -918,7 +913,7 @@
let obligation =
InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone() };
let canonical = table.canonicalize(obligation.clone());
- if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() {
+ if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() {
table.register_obligation(obligation.goal);
let return_ty = table.normalize_projection_ty(projection);
for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
@@ -929,7 +924,7 @@
environment: trait_env.clone(),
};
let canonical = table.canonicalize(obligation.clone());
- if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() {
+ if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() {
let ret_ty = table.resolve_completely(return_ty);
let args_ty = table.resolve_completely(args_ty);
let params = args_ty
@@ -985,7 +980,7 @@
outer_binder: DebruijnIndex,
) -> std::ops::ControlFlow<Self::BreakTy> {
let has_placeholder_bits = TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER;
- let TyData { kind, flags } = ty.data(Interner);
+ let chalk_ir::TyData { kind, flags } = ty.data(Interner);
if let TyKind::Placeholder(idx) = kind {
self.collect(*idx);
@@ -1045,3 +1040,25 @@
pub(crate) struct DeclContext {
pub(crate) origin: DeclOrigin,
}
+
+pub fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
+ use std::env;
+ use std::sync::LazyLock;
+ use tracing_subscriber::{Registry, layer::SubscriberExt};
+ use tracing_tree::HierarchicalLayer;
+
+ static ENABLE: LazyLock<bool> = LazyLock::new(|| env::var("CHALK_DEBUG").is_ok());
+ if !*ENABLE {
+ return None;
+ }
+
+ let filter: tracing_subscriber::filter::Targets =
+ env::var("CHALK_DEBUG").ok().and_then(|it| it.parse().ok()).unwrap_or_default();
+ let layer = HierarchicalLayer::default()
+ .with_indent_lines(true)
+ .with_ansi(false)
+ .with_indent_amount(2)
+ .with_writer(std::io::stderr);
+ let subscriber = Registry::default().with(filter).with(layer);
+ Some(tracing::subscriber::set_default(subscriber))
+}
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index afee960..7b6b3c3 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -46,9 +46,9 @@
use triomphe::{Arc, ThinArc};
use crate::{
- AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DynTy, FnAbi, FnPointer, FnSig,
- FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, LifetimeData,
- LifetimeOutlives, PolyFnSig, ProgramClause, QuantifiedWhereClause, QuantifiedWhereClauses,
+ 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,
consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic},
@@ -81,7 +81,7 @@
}
}
-pub(crate) struct PathDiagnosticCallbackData(TypeRefId);
+pub(crate) struct PathDiagnosticCallbackData(pub(crate) TypeRefId);
#[derive(Debug, Clone)]
pub enum LifetimeElisionKind {
@@ -889,7 +889,7 @@
pub(crate) type Diagnostics = Option<ThinArc<(), TyLoweringDiagnostic>>;
-fn create_diagnostics(diagnostics: Vec<TyLoweringDiagnostic>) -> Diagnostics {
+pub(crate) fn create_diagnostics(diagnostics: Vec<TyLoweringDiagnostic>) -> Diagnostics {
(!diagnostics.is_empty()).then(|| ThinArc::from_header_and_iter((), diagnostics.into_iter()))
}
@@ -1105,8 +1105,9 @@
traits_in_scope
.push((tr.self_type_parameter(Interner).clone(), tr.hir_trait_id()));
}
- let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(Interner);
- clauses.push(program_clause.into_from_env_clause(Interner));
+ let program_clause: Binders<DomainGoal> =
+ pred.map(|pred| pred.into_from_env_goal(Interner).cast(Interner));
+ clauses.push(program_clause);
}
}
}
@@ -1119,7 +1120,10 @@
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(pred.cast::<ProgramClause>(Interner).into_from_env_clause(Interner));
+ clauses.push(Binders::empty(
+ Interner,
+ pred.cast::<DomainGoal>(Interner).into_from_env_goal(Interner),
+ ));
}
let subst = generics.placeholder_subst(db);
@@ -1128,15 +1132,30 @@
if let Some(implicitly_sized_clauses) =
implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver)
{
- clauses.extend(
- implicitly_sized_clauses.map(|pred| {
- pred.cast::<ProgramClause>(Interner).into_from_env_clause(Interner)
- }),
- );
+ clauses.extend(implicitly_sized_clauses.map(|pred| {
+ Binders::empty(
+ Interner,
+ pred.into_from_env_goal(Interner).cast::<DomainGoal>(Interner),
+ )
+ }));
};
}
- let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
+ 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)
}
@@ -1177,7 +1196,7 @@
}
/// Resolve the where clause(s) of an item with generics,
-/// except the ones inherited from the parent
+/// with a given filter
fn generic_predicates_filtered_by<F>(
db: &dyn HirDatabase,
def: GenericDefId,
diff --git a/crates/hir-ty/src/lower_nextsolver.rs b/crates/hir-ty/src/lower_nextsolver.rs
new file mode 100644
index 0000000..24bda43
--- /dev/null
+++ b/crates/hir-ty/src/lower_nextsolver.rs
@@ -0,0 +1,1609 @@
+//! Methods for lowering the HIR to types. There are two main cases here:
+//!
+//! - Lowering a type reference like `&usize` or `Option<foo::bar::Baz>` to a
+//! type: The entry point for this is `TyLoweringContext::lower_ty`.
+//! - Building the type for an item: This happens through the `ty` query.
+//!
+//! This usually involves resolving names, collecting generic arguments etc.
+#![allow(unused)]
+// FIXME(next-solver): this should get removed as things get moved to rustc_type_ir from chalk_ir
+pub(crate) mod path;
+
+use std::{
+ cell::OnceCell,
+ iter, mem,
+ ops::{self, Not as _},
+};
+
+use base_db::Crate;
+use either::Either;
+use hir_def::{
+ AdtId, AssocItemId, CallableDefId, ConstParamId, EnumVariantId, FunctionId, GenericDefId,
+ GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TypeAliasId,
+ TypeOrConstParamId, VariantId,
+ expr_store::{
+ ExpressionStore,
+ path::{GenericArg, Path},
+ },
+ hir::generics::{TypeOrConstParamData, WherePredicate},
+ lang_item::LangItem,
+ resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
+ signatures::{FunctionSignature, TraitFlags, TypeAliasFlags},
+ type_ref::{
+ ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier,
+ TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId,
+ },
+};
+use hir_expand::name::Name;
+use intern::sym;
+use la_arena::{Arena, ArenaMap, Idx};
+use path::{PathDiagnosticCallback, PathLoweringContext, builtin};
+use rustc_ast_ir::Mutability;
+use rustc_hash::FxHashSet;
+use rustc_pattern_analysis::Captures;
+use rustc_type_ir::{
+ AliasTyKind, ConstKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection,
+ ExistentialTraitRef, FnSig, OutlivesPredicate,
+ TyKind::{self},
+ TypeVisitableExt,
+ inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _},
+};
+use salsa::plumbing::AsId;
+use stdx::never;
+use triomphe::Arc;
+
+use crate::{
+ FnAbi, ImplTraitId, Interner, ParamKind, TyDefId, TyLoweringDiagnostic,
+ TyLoweringDiagnosticKind,
+ consteval_nextsolver::{intern_const_ref, path_to_const, unknown_const_as_generic},
+ db::HirDatabase,
+ generics::{Generics, generics, trait_self_param_idx},
+ lower::{Diagnostics, PathDiagnosticCallbackData, create_diagnostics},
+ next_solver::{
+ AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind,
+ BoundVarKind, BoundVarKinds, Clause, Const, DbInterner, EarlyBinder, EarlyParamRegion,
+ ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, TraitPredicate,
+ TraitRef, Ty, Tys, abi::Safety, generics::GenericParamDefKind, mapping::ChalkToNextSolver,
+ },
+};
+
+#[derive(PartialEq, Eq, Debug, Hash)]
+pub struct ImplTraits<'db> {
+ pub(crate) impl_traits: Arena<ImplTrait<'db>>,
+}
+
+#[derive(PartialEq, Eq, Debug, Hash)]
+pub(crate) struct ImplTrait<'db> {
+ pub(crate) predicates: Vec<Clause<'db>>,
+}
+
+pub(crate) type ImplTraitIdx<'db> = Idx<ImplTrait<'db>>;
+
+#[derive(Debug, Default)]
+struct ImplTraitLoweringState<'db> {
+ /// When turning `impl Trait` into opaque types, we have to collect the
+ /// bounds at the same time to get the IDs correct (without becoming too
+ /// complicated).
+ mode: ImplTraitLoweringMode,
+ // This is structured as a struct with fields and not as an enum because it helps with the borrow checker.
+ opaque_type_data: Arena<ImplTrait<'db>>,
+ param_and_variable_counter: u16,
+}
+impl<'db> ImplTraitLoweringState<'db> {
+ fn new(mode: ImplTraitLoweringMode) -> ImplTraitLoweringState<'db> {
+ Self { mode, opaque_type_data: Arena::new(), param_and_variable_counter: 0 }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub(crate) enum LifetimeElisionKind<'db> {
+ /// Create a new anonymous lifetime parameter and reference it.
+ ///
+ /// If `report_in_path`, report an error when encountering lifetime elision in a path:
+ /// ```compile_fail
+ /// struct Foo<'a> { x: &'a () }
+ /// async fn foo(x: Foo) {}
+ /// ```
+ ///
+ /// Note: the error should not trigger when the elided lifetime is in a pattern or
+ /// expression-position path:
+ /// ```
+ /// struct Foo<'a> { x: &'a () }
+ /// async fn foo(Foo { x: _ }: Foo<'_>) {}
+ /// ```
+ AnonymousCreateParameter { report_in_path: bool },
+
+ /// Replace all anonymous lifetimes by provided lifetime.
+ Elided(Region<'db>),
+
+ /// Give a hard error when either `&` or `'_` is written. Used to
+ /// rule out things like `where T: Foo<'_>`. Does not imply an
+ /// error on default object bounds (e.g., `Box<dyn Foo>`).
+ AnonymousReportError,
+
+ /// Resolves elided lifetimes to `'static` if there are no other lifetimes in scope,
+ /// otherwise give a warning that the previous behavior of introducing a new early-bound
+ /// lifetime is a bug and will be removed (if `only_lint` is enabled).
+ StaticIfNoLifetimeInScope { only_lint: bool },
+
+ /// Signal we cannot find which should be the anonymous lifetime.
+ ElisionFailure,
+
+ /// Infer all elided lifetimes.
+ Infer,
+}
+
+impl<'db> LifetimeElisionKind<'db> {
+ #[inline]
+ pub(crate) fn for_const(
+ interner: DbInterner<'db>,
+ const_parent: ItemContainerId,
+ ) -> LifetimeElisionKind<'db> {
+ match const_parent {
+ ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => {
+ LifetimeElisionKind::Elided(Region::new_static(interner))
+ }
+ ItemContainerId::ImplId(_) => {
+ LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: true }
+ }
+ ItemContainerId::TraitId(_) => {
+ LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: false }
+ }
+ }
+ }
+
+ #[inline]
+ pub(crate) fn for_fn_params(data: &FunctionSignature) -> LifetimeElisionKind<'db> {
+ LifetimeElisionKind::AnonymousCreateParameter { report_in_path: data.is_async() }
+ }
+
+ #[inline]
+ pub(crate) fn for_fn_ret(interner: DbInterner<'db>) -> LifetimeElisionKind<'db> {
+ // FIXME: We should use the elided lifetime here, or `ElisionFailure`.
+ LifetimeElisionKind::Elided(Region::error(interner))
+ }
+}
+
+#[derive(Debug)]
+pub(crate) struct TyLoweringContext<'db, 'a> {
+ pub db: &'db dyn HirDatabase,
+ interner: DbInterner<'db>,
+ resolver: &'a Resolver<'db>,
+ store: &'a ExpressionStore,
+ def: GenericDefId,
+ generics: OnceCell<Generics>,
+ in_binders: DebruijnIndex,
+ impl_trait_mode: ImplTraitLoweringState<'db>,
+ /// Tracks types with explicit `?Sized` bounds.
+ pub(crate) unsized_types: FxHashSet<Ty<'db>>,
+ pub(crate) diagnostics: Vec<TyLoweringDiagnostic>,
+ lifetime_elision: LifetimeElisionKind<'db>,
+}
+
+impl<'db, 'a> TyLoweringContext<'db, 'a> {
+ pub(crate) fn new(
+ db: &'db dyn HirDatabase,
+ resolver: &'a Resolver<'db>,
+ store: &'a ExpressionStore,
+ def: GenericDefId,
+ lifetime_elision: LifetimeElisionKind<'db>,
+ ) -> Self {
+ let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed);
+ let in_binders = DebruijnIndex::ZERO;
+ Self {
+ db,
+ interner: DbInterner::new_with(db, Some(resolver.krate()), None),
+ resolver,
+ def,
+ generics: Default::default(),
+ store,
+ in_binders,
+ impl_trait_mode,
+ unsized_types: FxHashSet::default(),
+ diagnostics: Vec::new(),
+ lifetime_elision,
+ }
+ }
+
+ pub(crate) fn with_debruijn<T>(
+ &mut self,
+ debruijn: DebruijnIndex,
+ f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T,
+ ) -> T {
+ let old_debruijn = mem::replace(&mut self.in_binders, debruijn);
+ let result = f(self);
+ self.in_binders = old_debruijn;
+ result
+ }
+
+ pub(crate) fn with_shifted_in<T>(
+ &mut self,
+ debruijn: DebruijnIndex,
+ f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T,
+ ) -> T {
+ self.with_debruijn(self.in_binders.shifted_in(debruijn.as_u32()), f)
+ }
+
+ pub(crate) fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self {
+ Self { impl_trait_mode: ImplTraitLoweringState::new(impl_trait_mode), ..self }
+ }
+
+ pub(crate) fn impl_trait_mode(&mut self, impl_trait_mode: ImplTraitLoweringMode) -> &mut Self {
+ self.impl_trait_mode = ImplTraitLoweringState::new(impl_trait_mode);
+ self
+ }
+
+ pub(crate) fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) {
+ self.diagnostics.push(TyLoweringDiagnostic { source: type_ref, kind });
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
+pub(crate) enum ImplTraitLoweringMode {
+ /// `impl Trait` gets lowered into an opaque type that doesn't unify with
+ /// anything except itself. This is used in places where values flow 'out',
+ /// i.e. for arguments of the function we're currently checking, and return
+ /// types of functions we're calling.
+ Opaque,
+ /// `impl Trait` is disallowed and will be an error.
+ #[default]
+ Disallowed,
+}
+
+impl<'db, 'a> TyLoweringContext<'db, 'a> {
+ pub(crate) fn lower_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> {
+ self.lower_ty_ext(type_ref).0
+ }
+
+ pub(crate) fn lower_const(&mut self, const_ref: &ConstRef, const_type: Ty<'db>) -> Const<'db> {
+ let const_ref = &self.store[const_ref.expr];
+ match const_ref {
+ hir_def::hir::Expr::Path(path) => {
+ path_to_const(self.db, self.resolver, path, || self.generics(), const_type)
+ .unwrap_or_else(|| unknown_const(const_type))
+ }
+ hir_def::hir::Expr::Literal(literal) => intern_const_ref(
+ self.db,
+ &match *literal {
+ hir_def::hir::Literal::Float(_, _)
+ | hir_def::hir::Literal::String(_)
+ | hir_def::hir::Literal::ByteString(_)
+ | hir_def::hir::Literal::CString(_) => LiteralConstRef::Unknown,
+ hir_def::hir::Literal::Char(c) => LiteralConstRef::Char(c),
+ hir_def::hir::Literal::Bool(b) => LiteralConstRef::Bool(b),
+ hir_def::hir::Literal::Int(val, _) => LiteralConstRef::Int(val),
+ hir_def::hir::Literal::Uint(val, _) => LiteralConstRef::UInt(val),
+ },
+ const_type,
+ self.resolver.krate(),
+ ),
+ _ => unknown_const(const_type),
+ }
+ }
+
+ pub(crate) fn lower_path_as_const(&mut self, path: &Path, const_type: Ty<'db>) -> Const<'db> {
+ path_to_const(self.db, self.resolver, path, || self.generics(), const_type)
+ .unwrap_or_else(|| unknown_const(const_type))
+ }
+
+ fn generics(&self) -> &Generics {
+ self.generics.get_or_init(|| generics(self.db, self.def))
+ }
+
+ #[tracing::instrument(skip(self), ret)]
+ pub(crate) fn lower_ty_ext(&mut self, type_ref_id: TypeRefId) -> (Ty<'db>, Option<TypeNs>) {
+ let interner = self.interner;
+ let mut res = None;
+ let type_ref = &self.store[type_ref_id];
+ tracing::debug!(?type_ref);
+ let ty = match type_ref {
+ TypeRef::Never => Ty::new(interner, TyKind::Never),
+ TypeRef::Tuple(inner) => {
+ let inner_tys = inner.iter().map(|&tr| self.lower_ty(tr));
+ Ty::new_tup_from_iter(interner, inner_tys)
+ }
+ TypeRef::Path(path) => {
+ let (ty, res_) =
+ self.lower_path(path, PathId::from_type_ref_unchecked(type_ref_id));
+ res = res_;
+ ty
+ }
+ &TypeRef::TypeParam(type_param_id) => {
+ res = Some(TypeNs::GenericParam(type_param_id));
+
+ let generics = self.generics();
+ let (idx, data) =
+ generics.type_or_const_param(type_param_id.into()).expect("matching generics");
+ let type_data = match data {
+ TypeOrConstParamData::TypeParamData(ty) => ty,
+ _ => unreachable!(),
+ };
+ Ty::new_param(
+ self.interner,
+ idx as u32,
+ type_data
+ .name
+ .as_ref()
+ .map_or_else(|| sym::MISSING_NAME.clone(), |d| d.symbol().clone()),
+ )
+ }
+ &TypeRef::RawPtr(inner, mutability) => {
+ let inner_ty = self.lower_ty(inner);
+ Ty::new(interner, TyKind::RawPtr(inner_ty, lower_mutability(mutability)))
+ }
+ TypeRef::Array(array) => {
+ let inner_ty = self.lower_ty(array.ty);
+ let const_len = self.lower_const(&array.len, Ty::new_usize(interner));
+ Ty::new_array_with_const_len(interner, inner_ty, const_len)
+ }
+ &TypeRef::Slice(inner) => {
+ let inner_ty = self.lower_ty(inner);
+ Ty::new_slice(interner, inner_ty)
+ }
+ TypeRef::Reference(ref_) => {
+ let inner_ty = self.lower_ty(ref_.ty);
+ // FIXME: It should infer the eldided lifetimes instead of stubbing with error
+ let lifetime = ref_
+ .lifetime
+ .map_or_else(|| Region::error(interner), |lr| self.lower_lifetime(lr));
+ Ty::new_ref(interner, lifetime, inner_ty, lower_mutability(ref_.mutability))
+ }
+ TypeRef::Placeholder => Ty::new_error(interner, ErrorGuaranteed),
+ TypeRef::Fn(fn_) => {
+ let substs = self.with_shifted_in(
+ DebruijnIndex::from_u32(1),
+ |ctx: &mut TyLoweringContext<'_, '_>| {
+ Tys::new_from_iter(
+ interner,
+ fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)),
+ )
+ },
+ );
+ Ty::new_fn_ptr(
+ interner,
+ Binder::dummy(FnSig {
+ abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol),
+ safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe },
+ c_variadic: fn_.is_varargs,
+ inputs_and_output: substs,
+ }),
+ )
+ }
+ TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds),
+ TypeRef::ImplTrait(bounds) => {
+ match self.impl_trait_mode.mode {
+ ImplTraitLoweringMode::Opaque => {
+ let origin = match self.resolver.generic_def() {
+ Some(GenericDefId::FunctionId(it)) => Either::Left(it),
+ Some(GenericDefId::TypeAliasId(it)) => Either::Right(it),
+ _ => panic!(
+ "opaque impl trait lowering must be in function or type alias"
+ ),
+ };
+
+ // this dance is to make sure the data is in the right
+ // place even if we encounter more opaque types while
+ // lowering the bounds
+ let idx = self
+ .impl_trait_mode
+ .opaque_type_data
+ .alloc(ImplTrait { predicates: Vec::default() });
+
+ // FIXME(next-solver): this from_raw/into_raw dance isn't nice, but it's minimal
+ let impl_trait_id = origin.either(
+ |f| ImplTraitId::ReturnTypeImplTrait(f, Idx::from_raw(idx.into_raw())),
+ |a| ImplTraitId::TypeAliasImplTrait(a, Idx::from_raw(idx.into_raw())),
+ );
+ let opaque_ty_id: SolverDefId =
+ self.db.intern_impl_trait_id(impl_trait_id).into();
+
+ // We don't want to lower the bounds inside the binders
+ // we're currently in, because they don't end up inside
+ // those binders. E.g. when we have `impl Trait<impl
+ // OtherTrait<T>>`, the `impl OtherTrait<T>` can't refer
+ // to the self parameter from `impl Trait`, and the
+ // bounds aren't actually stored nested within each
+ // other, but separately. So if the `T` refers to a type
+ // parameter of the outer function, it's just one binder
+ // away instead of two.
+ let actual_opaque_type_data = self
+ .with_debruijn(DebruijnIndex::ZERO, |ctx| {
+ ctx.lower_impl_trait(opaque_ty_id, bounds, self.resolver.krate())
+ });
+ self.impl_trait_mode.opaque_type_data[idx] = actual_opaque_type_data;
+
+ let args = GenericArgs::identity_for_item(self.interner, opaque_ty_id);
+ Ty::new_alias(
+ self.interner,
+ AliasTyKind::Opaque,
+ AliasTy::new_from_args(self.interner, opaque_ty_id, args),
+ )
+ }
+ ImplTraitLoweringMode::Disallowed => {
+ // FIXME: report error
+ Ty::new_error(self.interner, ErrorGuaranteed)
+ }
+ }
+ }
+ TypeRef::Error => Ty::new_error(self.interner, ErrorGuaranteed),
+ };
+ (ty, res)
+ }
+
+ /// This is only for `generic_predicates_for_param`, where we can't just
+ /// lower the self types of the predicates since that could lead to cycles.
+ /// So we just check here if the `type_ref` resolves to a generic param, and which.
+ fn lower_ty_only_param(&self, type_ref: TypeRefId) -> Option<TypeOrConstParamId> {
+ let type_ref = &self.store[type_ref];
+ let path = match type_ref {
+ TypeRef::Path(path) => path,
+ &TypeRef::TypeParam(idx) => return Some(idx.into()),
+ _ => return None,
+ };
+ if path.type_anchor().is_some() {
+ return None;
+ }
+ if path.segments().len() > 1 {
+ return None;
+ }
+ let resolution = match self.resolver.resolve_path_in_type_ns(self.db, path) {
+ Some((it, None, _)) => it,
+ _ => return None,
+ };
+ match resolution {
+ TypeNs::GenericParam(param_id) => Some(param_id.into()),
+ _ => None,
+ }
+ }
+
+ #[inline]
+ fn on_path_diagnostic_callback(type_ref: TypeRefId) -> PathDiagnosticCallback<'static, 'db> {
+ PathDiagnosticCallback {
+ data: Either::Left(PathDiagnosticCallbackData(type_ref)),
+ callback: |data, this, diag| {
+ let type_ref = data.as_ref().left().unwrap().0;
+ this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag))
+ },
+ }
+ }
+
+ #[inline]
+ fn at_path(&mut self, path_id: PathId) -> PathLoweringContext<'_, 'a, 'db> {
+ PathLoweringContext::new(
+ self,
+ Self::on_path_diagnostic_callback(path_id.type_ref()),
+ &self.store[path_id],
+ )
+ }
+
+ pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty<'db>, Option<TypeNs>) {
+ // Resolve the path (in type namespace)
+ if let Some(type_ref) = path.type_anchor() {
+ let (ty, res) = self.lower_ty_ext(type_ref);
+ let mut ctx = self.at_path(path_id);
+ return ctx.lower_ty_relative_path(ty, res);
+ }
+
+ let mut ctx = self.at_path(path_id);
+ let (resolution, remaining_index) = match ctx.resolve_path_in_type_ns() {
+ Some(it) => it,
+ None => return (Ty::new_error(self.interner, ErrorGuaranteed), None),
+ };
+
+ if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() {
+ // trait object type without dyn
+ let bound = TypeBound::Path(path_id, TraitBoundModifier::None);
+ let ty = self.lower_dyn_trait(&[bound]);
+ return (ty, None);
+ }
+
+ ctx.lower_partly_resolved_path(resolution, false)
+ }
+
+ fn lower_trait_ref_from_path(
+ &mut self,
+ path_id: PathId,
+ explicit_self_ty: Ty<'db>,
+ ) -> Option<(TraitRef<'db>, PathLoweringContext<'_, 'a, 'db>)> {
+ let mut ctx = self.at_path(path_id);
+ let resolved = match ctx.resolve_path_in_type_ns_fully()? {
+ // FIXME(trait_alias): We need to handle trait alias here.
+ TypeNs::TraitId(tr) => tr,
+ _ => return None,
+ };
+ Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty), ctx))
+ }
+
+ fn lower_trait_ref(
+ &mut self,
+ trait_ref: &HirTraitRef,
+ explicit_self_ty: Ty<'db>,
+ ) -> Option<TraitRef<'db>> {
+ self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0)
+ }
+
+ pub(crate) fn lower_where_predicate<'b>(
+ &'b mut self,
+ where_predicate: &'b WherePredicate,
+ ignore_bindings: bool,
+ generics: &Generics,
+ predicate_filter: PredicateFilter,
+ ) -> impl Iterator<Item = Clause<'db>> + use<'a, 'b, 'db> {
+ match where_predicate {
+ WherePredicate::ForLifetime { target, bound, .. }
+ | WherePredicate::TypeBound { target, bound } => {
+ if let PredicateFilter::SelfTrait = predicate_filter {
+ let target_type = &self.store[*target];
+ let self_type = 'is_self: {
+ if let TypeRef::Path(path) = target_type
+ && path.is_self_type()
+ {
+ break 'is_self true;
+ }
+ if let TypeRef::TypeParam(param) = target_type
+ && generics[param.local_id()].is_trait_self()
+ {
+ break 'is_self true;
+ }
+ false
+ };
+ if !self_type {
+ return Either::Left(Either::Left(iter::empty()));
+ }
+ }
+ let self_ty = self.lower_ty(*target);
+ Either::Left(Either::Right(self.lower_type_bound(bound, self_ty, ignore_bindings)))
+ }
+ &WherePredicate::Lifetime { bound, target } => {
+ Either::Right(iter::once(Clause(Predicate::new(
+ self.interner,
+ Binder::dummy(rustc_type_ir::PredicateKind::Clause(
+ rustc_type_ir::ClauseKind::RegionOutlives(OutlivesPredicate(
+ self.lower_lifetime(bound),
+ self.lower_lifetime(target),
+ )),
+ )),
+ ))))
+ }
+ }
+ .into_iter()
+ }
+
+ pub(crate) fn lower_type_bound<'b>(
+ &'b mut self,
+ bound: &'b TypeBound,
+ self_ty: Ty<'db>,
+ ignore_bindings: bool,
+ ) -> impl Iterator<Item = Clause<'db>> + use<'b, 'a, 'db> {
+ let interner = self.interner;
+ let mut assoc_bounds = None;
+ let mut clause = None;
+ match bound {
+ &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => {
+ // FIXME Don't silently drop the hrtb lifetimes here
+ if let Some((trait_ref, mut ctx)) = self.lower_trait_ref_from_path(path, self_ty) {
+ // FIXME(sized-hierarchy): Remove this bound modifications once we have implemented
+ // sized-hierarchy correctly.
+ let meta_sized = LangItem::MetaSized
+ .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate());
+ let pointee_sized = LangItem::PointeeSized
+ .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate());
+ if meta_sized.is_some_and(|it| SolverDefId::TraitId(it) == trait_ref.def_id) {
+ // Ignore this bound
+ } else if pointee_sized
+ .is_some_and(|it| SolverDefId::TraitId(it) == trait_ref.def_id)
+ {
+ // Regard this as `?Sized` bound
+ ctx.ty_ctx().unsized_types.insert(self_ty);
+ } else {
+ if !ignore_bindings {
+ assoc_bounds = ctx.assoc_type_bindings_from_type_bound(trait_ref);
+ }
+ clause = Some(Clause(Predicate::new(
+ interner,
+ Binder::dummy(rustc_type_ir::PredicateKind::Clause(
+ rustc_type_ir::ClauseKind::Trait(TraitPredicate {
+ trait_ref,
+ polarity: rustc_type_ir::PredicatePolarity::Positive,
+ }),
+ )),
+ )));
+ }
+ }
+ }
+ &TypeBound::Path(path, TraitBoundModifier::Maybe) => {
+ let sized_trait = LangItem::Sized.resolve_trait(self.db, self.resolver.krate());
+ // Don't lower associated type bindings as the only possible relaxed trait bound
+ // `?Sized` has no of them.
+ // If we got another trait here ignore the bound completely.
+ let trait_id =
+ self.lower_trait_ref_from_path(path, self_ty).map(|(trait_ref, _)| {
+ match trait_ref.def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => unreachable!(),
+ }
+ });
+ if trait_id == sized_trait {
+ self.unsized_types.insert(self_ty);
+ }
+ }
+ &TypeBound::Lifetime(l) => {
+ let lifetime = self.lower_lifetime(l);
+ clause = Some(Clause(Predicate::new(
+ self.interner,
+ Binder::dummy(rustc_type_ir::PredicateKind::Clause(
+ rustc_type_ir::ClauseKind::TypeOutlives(OutlivesPredicate(
+ self_ty, lifetime,
+ )),
+ )),
+ )));
+ }
+ TypeBound::Use(_) | TypeBound::Error => {}
+ }
+ clause.into_iter().chain(assoc_bounds.into_iter().flatten())
+ }
+
+ fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty<'db> {
+ let interner = self.interner;
+ // FIXME: we should never create non-existential predicates in the first place
+ // For now, use an error type so we don't run into dummy binder issues
+ let self_ty = Ty::new_error(interner, ErrorGuaranteed);
+ // INVARIANT: The principal trait bound, if present, must come first. Others may be in any
+ // order but should be in the same order for the same set but possibly different order of
+ // bounds in the input.
+ // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound.
+ // These invariants are utilized by `TyExt::dyn_trait()` and chalk.
+ let mut lifetime = None;
+ let bounds = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| {
+ let mut lowered_bounds: Vec<
+ rustc_type_ir::Binder<DbInterner<'db>, ExistentialPredicate<DbInterner<'db>>>,
+ > = Vec::new();
+ for b in bounds {
+ let db = ctx.db;
+ ctx.lower_type_bound(b, self_ty, false).for_each(|b| {
+ if let Some(bound) = b
+ .kind()
+ .map_bound(|c| match c {
+ rustc_type_ir::ClauseKind::Trait(t) => {
+ let id = t.def_id();
+ let id = match id {
+ SolverDefId::TraitId(id) => id,
+ _ => unreachable!(),
+ };
+ let is_auto =
+ db.trait_signature(id).flags.contains(TraitFlags::AUTO);
+ if is_auto {
+ Some(ExistentialPredicate::AutoTrait(t.def_id()))
+ } else {
+ Some(ExistentialPredicate::Trait(
+ ExistentialTraitRef::new_from_args(
+ interner,
+ t.def_id(),
+ GenericArgs::new_from_iter(
+ interner,
+ t.trait_ref.args.iter().skip(1),
+ ),
+ ),
+ ))
+ }
+ }
+ rustc_type_ir::ClauseKind::Projection(p) => {
+ Some(ExistentialPredicate::Projection(
+ ExistentialProjection::new_from_args(
+ interner,
+ p.def_id(),
+ GenericArgs::new_from_iter(
+ interner,
+ p.projection_term.args.iter().skip(1),
+ ),
+ p.term,
+ ),
+ ))
+ }
+ rustc_type_ir::ClauseKind::TypeOutlives(outlives_predicate) => {
+ lifetime = Some(outlives_predicate.1);
+ None
+ }
+ rustc_type_ir::ClauseKind::RegionOutlives(_)
+ | rustc_type_ir::ClauseKind::ConstArgHasType(_, _)
+ | rustc_type_ir::ClauseKind::WellFormed(_)
+ | rustc_type_ir::ClauseKind::ConstEvaluatable(_)
+ | rustc_type_ir::ClauseKind::HostEffect(_)
+ | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(),
+ })
+ .transpose()
+ {
+ lowered_bounds.push(bound);
+ }
+ })
+ }
+
+ let mut multiple_regular_traits = false;
+ let mut multiple_same_projection = false;
+ lowered_bounds.sort_unstable_by(|lhs, rhs| {
+ use std::cmp::Ordering;
+ match ((*lhs).skip_binder(), (*rhs).skip_binder()) {
+ (ExistentialPredicate::Trait(_), ExistentialPredicate::Trait(_)) => {
+ multiple_regular_traits = true;
+ // Order doesn't matter - we error
+ Ordering::Equal
+ }
+ (
+ ExistentialPredicate::AutoTrait(lhs_id),
+ ExistentialPredicate::AutoTrait(rhs_id),
+ ) => {
+ let lhs_id = match lhs_id {
+ SolverDefId::TraitId(id) => id,
+ _ => unreachable!(),
+ };
+ let rhs_id = match rhs_id {
+ SolverDefId::TraitId(id) => id,
+ _ => unreachable!(),
+ };
+ lhs_id.cmp(&rhs_id)
+ }
+ (ExistentialPredicate::Trait(_), _) => Ordering::Less,
+ (_, ExistentialPredicate::Trait(_)) => Ordering::Greater,
+ (ExistentialPredicate::AutoTrait(_), _) => Ordering::Less,
+ (_, ExistentialPredicate::AutoTrait(_)) => Ordering::Greater,
+ (
+ ExistentialPredicate::Projection(lhs),
+ ExistentialPredicate::Projection(rhs),
+ ) => {
+ let lhs_id = match lhs.def_id {
+ SolverDefId::TypeAliasId(id) => id,
+ _ => unreachable!(),
+ };
+ let rhs_id = match rhs.def_id {
+ SolverDefId::TypeAliasId(id) => id,
+ _ => unreachable!(),
+ };
+ // We only compare the `associated_ty_id`s. We shouldn't have
+ // multiple bounds for an associated type in the correct Rust code,
+ // and if we do, we error out.
+ if lhs_id == rhs_id {
+ multiple_same_projection = true;
+ }
+ lhs_id.as_id().index().cmp(&rhs_id.as_id().index())
+ }
+ }
+ });
+
+ if multiple_regular_traits || multiple_same_projection {
+ return None;
+ }
+
+ if !lowered_bounds.first().map_or(false, |b| {
+ matches!(
+ b.as_ref().skip_binder(),
+ ExistentialPredicate::Trait(_) | ExistentialPredicate::AutoTrait(_)
+ )
+ }) {
+ return None;
+ }
+
+ // As multiple occurrences of the same auto traits *are* permitted, we deduplicate the
+ // bounds. We shouldn't have repeated elements besides auto traits at this point.
+ lowered_bounds.dedup();
+
+ Some(BoundExistentialPredicates::new_from_iter(interner, lowered_bounds))
+ });
+
+ if let Some(bounds) = bounds {
+ let region = match lifetime {
+ Some(it) => match it.kind() {
+ rustc_type_ir::RegionKind::ReBound(db, var) => Region::new_bound(
+ self.interner,
+ db.shifted_out_to_binder(DebruijnIndex::from_u32(2)),
+ var,
+ ),
+ _ => it,
+ },
+ None => Region::new_static(self.interner),
+ };
+ Ty::new_dynamic(self.interner, bounds, region, rustc_type_ir::DynKind::Dyn)
+ } else {
+ // FIXME: report error
+ // (additional non-auto traits, associated type rebound, or no resolved trait)
+ Ty::new_error(self.interner, ErrorGuaranteed)
+ }
+ }
+
+ fn lower_impl_trait(
+ &mut self,
+ def_id: SolverDefId,
+ bounds: &[TypeBound],
+ krate: Crate,
+ ) -> ImplTrait<'db> {
+ let interner = self.interner;
+ cov_mark::hit!(lower_rpit);
+ let args = GenericArgs::identity_for_item(interner, def_id);
+ let self_ty = Ty::new_alias(
+ self.interner,
+ rustc_type_ir::AliasTyKind::Opaque,
+ AliasTy::new_from_args(interner, def_id, args),
+ );
+ let predicates = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| {
+ let mut predicates = Vec::new();
+ for b in bounds {
+ predicates.extend(ctx.lower_type_bound(b, self_ty, false));
+ }
+
+ if !ctx.unsized_types.contains(&self_ty) {
+ let sized_trait = LangItem::Sized.resolve_trait(self.db, krate);
+ let sized_clause = sized_trait.map(|trait_id| {
+ let trait_ref = TraitRef::new_from_args(
+ interner,
+ trait_id.into(),
+ GenericArgs::new_from_iter(interner, [self_ty.into()]),
+ );
+ Clause(Predicate::new(
+ interner,
+ Binder::dummy(rustc_type_ir::PredicateKind::Clause(
+ rustc_type_ir::ClauseKind::Trait(TraitPredicate {
+ trait_ref,
+ polarity: rustc_type_ir::PredicatePolarity::Positive,
+ }),
+ )),
+ ))
+ });
+ predicates.extend(sized_clause);
+ }
+ predicates.shrink_to_fit();
+ predicates
+ });
+ ImplTrait { predicates }
+ }
+
+ pub(crate) fn lower_lifetime(&self, lifetime: LifetimeRefId) -> Region<'db> {
+ match self.resolver.resolve_lifetime(&self.store[lifetime]) {
+ Some(resolution) => match resolution {
+ LifetimeNs::Static => Region::new_static(self.interner),
+ LifetimeNs::LifetimeParam(id) => {
+ let idx = match self.generics().lifetime_idx(id) {
+ None => return Region::error(self.interner),
+ Some(idx) => idx,
+ };
+ Region::new_early_param(self.interner, EarlyParamRegion { index: idx as u32 })
+ }
+ },
+ None => Region::error(self.interner),
+ }
+ }
+}
+
+pub(crate) fn lower_mutability(m: hir_def::type_ref::Mutability) -> Mutability {
+ match m {
+ hir_def::type_ref::Mutability::Shared => Mutability::Not,
+ hir_def::type_ref::Mutability::Mut => Mutability::Mut,
+ }
+}
+
+fn unknown_const(_ty: Ty<'_>) -> Const<'_> {
+ Const::new(DbInterner::conjure(), ConstKind::Error(ErrorGuaranteed))
+}
+
+pub(crate) fn impl_trait_query<'db>(
+ db: &'db dyn HirDatabase,
+ impl_id: ImplId,
+) -> Option<EarlyBinder<'db, TraitRef<'db>>> {
+ db.impl_trait_with_diagnostics_ns(impl_id).map(|it| it.0)
+}
+
+pub(crate) fn impl_trait_with_diagnostics_query<'db>(
+ db: &'db dyn HirDatabase,
+ impl_id: ImplId,
+) -> Option<(EarlyBinder<'db, TraitRef<'db>>, 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 },
+ );
+ let self_ty = db.impl_self_ty_ns(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)))
+}
+
+pub(crate) fn return_type_impl_traits<'db>(
+ db: &'db dyn HirDatabase,
+ def: hir_def::FunctionId,
+) -> Option<Arc<EarlyBinder<'db, ImplTraits<'db>>>> {
+ // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
+ let data = db.function_signature(def);
+ let resolver = def.resolver(db);
+ let mut ctx_ret =
+ TyLoweringContext::new(db, &resolver, &data.store, def.into(), LifetimeElisionKind::Infer)
+ .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
+ if let Some(ret_type) = data.ret_type {
+ let _ret = ctx_ret.lower_ty(ret_type);
+ }
+ let return_type_impl_traits =
+ ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data };
+ if return_type_impl_traits.impl_traits.is_empty() {
+ None
+ } else {
+ Some(Arc::new(EarlyBinder::bind(return_type_impl_traits)))
+ }
+}
+
+pub(crate) fn type_alias_impl_traits<'db>(
+ db: &'db dyn HirDatabase,
+ def: hir_def::TypeAliasId,
+) -> Option<Arc<EarlyBinder<'db, ImplTraits<'db>>>> {
+ let data = db.type_alias_signature(def);
+ let resolver = def.resolver(db);
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ &data.store,
+ def.into(),
+ LifetimeElisionKind::AnonymousReportError,
+ )
+ .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
+ if let Some(type_ref) = data.ty {
+ let _ty = ctx.lower_ty(type_ref);
+ }
+ let type_alias_impl_traits = ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data };
+ if type_alias_impl_traits.impl_traits.is_empty() {
+ None
+ } else {
+ Some(Arc::new(EarlyBinder::bind(type_alias_impl_traits)))
+ }
+}
+
+/// 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>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBinder<'db, Ty<'db>> {
+ let interner = DbInterner::new_with(db, None, None);
+ match def {
+ TyDefId::BuiltinType(it) => EarlyBinder::bind(builtin(interner, it)),
+ TyDefId::AdtId(it) => EarlyBinder::bind(Ty::new_adt(
+ interner,
+ AdtDef::new(it, interner),
+ GenericArgs::identity_for_item(interner, it.into()),
+ )),
+ TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics_ns(it).0,
+ }
+}
+
+pub(crate) fn type_for_type_alias_with_diagnostics_query<'db>(
+ db: &'db dyn HirDatabase,
+ t: TypeAliasId,
+) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) {
+ let type_alias_data = db.type_alias_signature(t);
+ let mut diags = None;
+ let resolver = t.resolver(db);
+ let interner = DbInterner::new_with(db, Some(resolver.krate()), None);
+ let inner = if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) {
+ EarlyBinder::bind(Ty::new_foreign(interner, t.into()))
+ } else {
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ &type_alias_data.store,
+ t.into(),
+ LifetimeElisionKind::AnonymousReportError,
+ )
+ .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
+ let res = EarlyBinder::bind(
+ type_alias_data
+ .ty
+ .map(|type_ref| ctx.lower_ty(type_ref))
+ .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)),
+ );
+ diags = create_diagnostics(ctx.diagnostics);
+ res
+ };
+ (inner, diags)
+}
+
+pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result<'db>(
+ db: &'db dyn HirDatabase,
+ _adt: TypeAliasId,
+) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) {
+ (EarlyBinder::bind(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)), None)
+}
+
+pub(crate) fn impl_self_ty_query<'db>(
+ db: &'db dyn HirDatabase,
+ impl_id: ImplId,
+) -> EarlyBinder<'db, Ty<'db>> {
+ db.impl_self_ty_with_diagnostics_ns(impl_id).0
+}
+
+pub(crate) fn impl_self_ty_with_diagnostics_query<'db>(
+ db: &'db dyn HirDatabase,
+ impl_id: ImplId,
+) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) {
+ let resolver = impl_id.resolver(db);
+ let interner = DbInterner::new_with(db, Some(resolver.krate()), None);
+
+ let impl_data = db.impl_signature(impl_id);
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ &impl_data.store,
+ impl_id.into(),
+ LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true },
+ );
+ let ty = ctx.lower_ty(impl_data.self_ty);
+ assert!(!ty.has_escaping_bound_vars());
+ (EarlyBinder::bind(ty), create_diagnostics(ctx.diagnostics))
+}
+
+pub(crate) fn impl_self_ty_with_diagnostics_cycle_result(
+ db: &dyn HirDatabase,
+ _impl_id: ImplId,
+) -> (EarlyBinder<'_, Ty<'_>>, Diagnostics) {
+ (EarlyBinder::bind(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)), None)
+}
+
+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
+}
+
+// returns None if def is a type arg
+pub(crate) fn const_param_ty_with_diagnostics_query<'db>(
+ db: &'db dyn HirDatabase,
+ def: ConstParamId,
+) -> (Ty<'db>, Diagnostics) {
+ let (parent_data, store) = db.generic_params_and_store(def.parent());
+ let data = &parent_data[def.local_id()];
+ let resolver = def.parent().resolver(db);
+ let interner = DbInterner::new_with(db, Some(resolver.krate()), None);
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ &store,
+ def.parent(),
+ LifetimeElisionKind::AnonymousReportError,
+ );
+ let ty = match data {
+ TypeOrConstParamData::TypeParamData(_) => {
+ never!();
+ Ty::new_error(interner, ErrorGuaranteed)
+ }
+ TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(d.ty),
+ };
+ (ty, create_diagnostics(ctx.diagnostics))
+}
+
+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
+}
+
+/// Build the type of all specific fields of a struct or enum variant.
+pub(crate) fn field_types_with_diagnostics_query<'db>(
+ db: &'db dyn HirDatabase,
+ variant_id: VariantId,
+) -> (Arc<ArenaMap<LocalFieldId, EarlyBinder<'db, Ty<'db>>>>, Diagnostics) {
+ let var_data = variant_id.fields(db);
+ let fields = var_data.fields();
+ if fields.is_empty() {
+ return (Arc::new(ArenaMap::default()), None);
+ }
+
+ let (resolver, def): (_, GenericDefId) = match variant_id {
+ VariantId::StructId(it) => (it.resolver(db), it.into()),
+ VariantId::UnionId(it) => (it.resolver(db), it.into()),
+ VariantId::EnumVariantId(it) => (it.resolver(db), it.lookup(db).parent.into()),
+ };
+ let mut res = ArenaMap::default();
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ &var_data.store,
+ def,
+ LifetimeElisionKind::AnonymousReportError,
+ );
+ for (field_id, field_data) in var_data.fields().iter() {
+ res.insert(field_id, EarlyBinder::bind(ctx.lower_ty(field_data.type_ref)));
+ }
+ (Arc::new(res), create_diagnostics(ctx.diagnostics))
+}
+
+/// This query exists only to be used when resolving short-hand associated types
+/// like `T::Item`.
+///
+/// See the analogous query in rustc and its comment:
+/// <https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46>
+/// This is a query mostly to handle cycles somewhat gracefully; e.g. the
+/// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but
+/// these are fine: `T: Foo<U::Item>, U: Foo<()>`.
+#[tracing::instrument(skip(db), ret)]
+pub(crate) fn generic_predicates_for_param_query<'db>(
+ db: &'db dyn HirDatabase,
+ def: GenericDefId,
+ param_id: TypeOrConstParamId,
+ assoc_name: Option<Name>,
+) -> GenericPredicates<'db> {
+ let generics = generics(db, def);
+ let interner = DbInterner::new_with(db, None, None);
+ let resolver = def.resolver(db);
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ generics.store(),
+ def,
+ LifetimeElisionKind::AnonymousReportError,
+ );
+
+ // we have to filter out all other predicates *first*, before attempting to lower them
+ let predicate = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred {
+ WherePredicate::ForLifetime { target, bound, .. }
+ | WherePredicate::TypeBound { target, bound, .. } => {
+ let invalid_target = { ctx.lower_ty_only_param(*target) != Some(param_id) };
+ if invalid_target {
+ // FIXME(sized-hierarchy): Revisit and adjust this properly once we have implemented
+ // sized-hierarchy correctly.
+ // If this is filtered out without lowering, `?Sized` or `PointeeSized` is not gathered into
+ // `ctx.unsized_types`
+ let lower = || -> bool {
+ match bound {
+ TypeBound::Path(_, TraitBoundModifier::Maybe) => true,
+ TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
+ let TypeRef::Path(path) = &ctx.store[path.type_ref()] else {
+ return false;
+ };
+ let Some(pointee_sized) =
+ LangItem::PointeeSized.resolve_trait(ctx.db, ctx.resolver.krate())
+ else {
+ return false;
+ };
+ // Lower the path directly with `Resolver` instead of PathLoweringContext`
+ // to prevent diagnostics duplications.
+ ctx.resolver.resolve_path_in_type_ns_fully(ctx.db, path).is_some_and(
+ |it| matches!(it, TypeNs::TraitId(tr) if tr == pointee_sized),
+ )
+ }
+ _ => false,
+ }
+ }();
+ if lower {
+ ctx.lower_where_predicate(pred, true, &generics, PredicateFilter::All)
+ .for_each(drop);
+ }
+ return false;
+ }
+
+ match bound {
+ &TypeBound::ForLifetime(_, path) | &TypeBound::Path(path, _) => {
+ // Only lower the bound if the trait could possibly define the associated
+ // type we're looking for.
+ let path = &ctx.store[path];
+
+ let Some(assoc_name) = &assoc_name else { return true };
+ let Some(TypeNs::TraitId(tr)) =
+ resolver.resolve_path_in_type_ns_fully(db, path)
+ else {
+ return false;
+ };
+
+ rustc_type_ir::elaborate::supertrait_def_ids(interner, tr.into()).any(|tr| {
+ let tr = match tr {
+ SolverDefId::TraitId(id) => id,
+ _ => unreachable!(),
+ };
+ tr.trait_items(db).items.iter().any(|(name, item)| {
+ matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name
+ })
+ })
+ }
+ TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false,
+ }
+ }
+ WherePredicate::Lifetime { .. } => false,
+ };
+ let mut predicates = 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() {
+ if predicate(pred, &mut ctx) {
+ predicates.extend(ctx.lower_where_predicate(
+ pred,
+ true,
+ maybe_parent_generics,
+ PredicateFilter::All,
+ ));
+ }
+ }
+ }
+
+ let args = GenericArgs::identity_for_item(interner, def.into());
+ if !args.is_empty() {
+ let explicitly_unsized_tys = ctx.unsized_types;
+ if let Some(implicitly_sized_predicates) =
+ implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &args, &resolver)
+ {
+ predicates.extend(implicitly_sized_predicates);
+ };
+ }
+ GenericPredicates(predicates.is_empty().not().then(|| predicates.into()))
+}
+
+pub(crate) fn generic_predicates_for_param_cycle_result(
+ _db: &dyn HirDatabase,
+ _def: GenericDefId,
+ _param_id: TypeOrConstParamId,
+ _assoc_name: Option<Name>,
+) -> GenericPredicates<'_> {
+ GenericPredicates(None)
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct GenericPredicates<'db>(Option<Arc<[Clause<'db>]>>);
+
+impl<'db> ops::Deref for GenericPredicates<'db> {
+ type Target = [Clause<'db>];
+
+ fn deref(&self) -> &Self::Target {
+ self.0.as_deref().unwrap_or(&[])
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub(crate) enum PredicateFilter {
+ SelfTrait,
+ All,
+}
+
+/// Resolve the where clause(s) of an item with generics.
+#[tracing::instrument(skip(db))]
+pub(crate) fn generic_predicates_query<'db>(
+ db: &'db dyn HirDatabase,
+ def: GenericDefId,
+) -> GenericPredicates<'db> {
+ generic_predicates_filtered_by(db, def, PredicateFilter::All, |_| true).0
+}
+
+pub(crate) fn generic_predicates_without_parent_query<'db>(
+ db: &'db dyn HirDatabase,
+ def: GenericDefId,
+) -> GenericPredicates<'db> {
+ generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def).0
+}
+
+/// Resolve the where clause(s) of an item with generics,
+/// except the ones inherited from the parent
+pub(crate) fn generic_predicates_without_parent_with_diagnostics_query<'db>(
+ db: &'db dyn HirDatabase,
+ def: GenericDefId,
+) -> (GenericPredicates<'db>, Diagnostics) {
+ generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def)
+}
+
+/// Resolve the where clause(s) of an item with generics,
+/// with a given filter
+#[tracing::instrument(skip(db, filter), ret)]
+pub(crate) fn generic_predicates_filtered_by<'db, F>(
+ db: &'db dyn HirDatabase,
+ def: GenericDefId,
+ predicate_filter: PredicateFilter,
+ filter: F,
+) -> (GenericPredicates<'db>, Diagnostics)
+where
+ F: Fn(GenericDefId) -> bool,
+{
+ let generics = generics(db, def);
+ let resolver = def.resolver(db);
+ let interner = DbInterner::new_with(db, Some(resolver.krate()), None);
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ generics.store(),
+ def,
+ LifetimeElisionKind::AnonymousReportError,
+ );
+
+ let mut predicates = 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() {
+ tracing::debug!(?pred);
+ if filter(maybe_parent_generics.def()) {
+ // We deliberately use `generics` and not `maybe_parent_generics` here. This is not a mistake!
+ // If we use the parent generics
+ predicates.extend(ctx.lower_where_predicate(
+ pred,
+ false,
+ maybe_parent_generics,
+ predicate_filter,
+ ));
+ }
+ }
+ }
+
+ 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 {
+ if filter(def_id) {
+ 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 GenericParamDefKind::Type = p.kind else {
+ continue;
+ };
+ let idx = idx as u32 + generics.parent_count as u32;
+ let param_ty = Ty::new_param(interner, idx, p.name.clone());
+ if explicitly_unsized_tys.contains(¶m_ty) {
+ continue;
+ }
+ let trait_ref = TraitRef::new_from_args(
+ interner,
+ sized_trait.into(),
+ GenericArgs::new_from_iter(interner, [param_ty.into()]),
+ );
+ let clause = Clause(Predicate::new(
+ interner,
+ Binder::dummy(rustc_type_ir::PredicateKind::Clause(
+ rustc_type_ir::ClauseKind::Trait(TraitPredicate {
+ trait_ref,
+ polarity: rustc_type_ir::PredicatePolarity::Positive,
+ }),
+ )),
+ ));
+ predicates.push(clause);
+ }
+ }
+
+ if let Some(g) = generics.parent {
+ generics = crate::next_solver::generics::generics(db, g.into());
+ def_id = g;
+ } else {
+ break;
+ }
+ }
+ }
+
+ (
+ GenericPredicates(predicates.is_empty().not().then(|| predicates.into())),
+ create_diagnostics(ctx.diagnostics),
+ )
+}
+
+/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound.
+/// Exception is Self of a trait def.
+fn implicitly_sized_clauses<'a, 'subst, 'db>(
+ db: &'db dyn HirDatabase,
+ def: GenericDefId,
+ explicitly_unsized_tys: &'a FxHashSet<Ty<'db>>,
+ args: &'subst GenericArgs<'db>,
+ resolver: &Resolver<'db>,
+) -> Option<impl Iterator<Item = Clause<'db>> + Captures<'a> + Captures<'subst>> {
+ let interner = DbInterner::new_with(db, Some(resolver.krate()), None);
+ let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate())?;
+
+ let trait_self_idx = trait_self_param_idx(db, def);
+
+ Some(
+ args.iter()
+ .enumerate()
+ .filter_map(
+ move |(idx, generic_arg)| {
+ if Some(idx) == trait_self_idx { None } else { Some(generic_arg) }
+ },
+ )
+ .filter_map(|generic_arg| generic_arg.as_type())
+ .filter(move |self_ty| !explicitly_unsized_tys.contains(self_ty))
+ .map(move |self_ty| {
+ let trait_ref = TraitRef::new_from_args(
+ interner,
+ sized_trait.into(),
+ GenericArgs::new_from_iter(interner, [self_ty.into()]),
+ );
+ Clause(Predicate::new(
+ interner,
+ Binder::dummy(rustc_type_ir::PredicateKind::Clause(
+ rustc_type_ir::ClauseKind::Trait(TraitPredicate {
+ trait_ref,
+ polarity: rustc_type_ir::PredicatePolarity::Positive,
+ }),
+ )),
+ ))
+ }),
+ )
+}
+
+pub(crate) fn make_binders<'db, T: rustc_type_ir::TypeVisitable<DbInterner<'db>>>(
+ interner: DbInterner<'db>,
+ generics: &Generics,
+ value: T,
+) -> Binder<'db, T> {
+ Binder::bind_with_vars(
+ value,
+ BoundVarKinds::new_from_iter(
+ interner,
+ generics.iter_id().map(|x| match x {
+ hir_def::GenericParamId::ConstParamId(_) => BoundVarKind::Const,
+ hir_def::GenericParamId::TypeParamId(_) => BoundVarKind::Ty(BoundTyKind::Anon),
+ hir_def::GenericParamId::LifetimeParamId(_) => {
+ BoundVarKind::Region(BoundRegionKind::Anon)
+ }
+ }),
+ ),
+ )
+}
+
+/// Checks if the provided generic arg matches its expected kind, then lower them via
+/// provided closures. Use unknown if there was kind mismatch.
+///
+pub(crate) fn lower_generic_arg<'a, 'db, T>(
+ db: &'db dyn HirDatabase,
+ kind_id: GenericParamId,
+ arg: &'a GenericArg,
+ this: &mut T,
+ store: &ExpressionStore,
+ for_type: impl FnOnce(&mut T, TypeRefId) -> Ty<'db> + 'a,
+ for_const: impl FnOnce(&mut T, &ConstRef, Ty<'db>) -> Const<'db> + 'a,
+ for_const_ty_path_fallback: impl FnOnce(&mut T, &Path, Ty<'db>) -> Const<'db> + 'a,
+ for_lifetime: impl FnOnce(&mut T, &LifetimeRefId) -> Region<'db> + 'a,
+) -> crate::next_solver::GenericArg<'db> {
+ let interner = DbInterner::new_with(db, None, None);
+ let kind = match kind_id {
+ GenericParamId::TypeParamId(_) => ParamKind::Type,
+ GenericParamId::ConstParamId(id) => {
+ let ty = db.const_param_ty(id);
+ ParamKind::Const(ty)
+ }
+ GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime,
+ };
+ match (arg, kind) {
+ (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, *type_ref).into(),
+ (GenericArg::Const(c), ParamKind::Const(c_ty)) => {
+ for_const(this, c, c_ty.to_nextsolver(interner)).into()
+ }
+ (GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => {
+ for_lifetime(this, lifetime_ref).into()
+ }
+ (GenericArg::Const(_), ParamKind::Type) => Ty::new_error(interner, ErrorGuaranteed).into(),
+ (GenericArg::Lifetime(_), ParamKind::Type) => {
+ Ty::new_error(interner, ErrorGuaranteed).into()
+ }
+ (GenericArg::Type(t), ParamKind::Const(c_ty)) => match &store[*t] {
+ TypeRef::Path(p) => {
+ for_const_ty_path_fallback(this, p, c_ty.to_nextsolver(interner)).into()
+ }
+ _ => unknown_const_as_generic(c_ty.to_nextsolver(interner)),
+ },
+ (GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => {
+ unknown_const(c_ty.to_nextsolver(interner)).into()
+ }
+ (GenericArg::Type(_), ParamKind::Lifetime) => Region::error(interner).into(),
+ (GenericArg::Const(_), ParamKind::Lifetime) => Region::error(interner).into(),
+ }
+}
+
+/// Build the signature of a callable item (function, struct or enum variant).
+pub(crate) fn callable_item_signature_query<'db>(
+ db: &'db dyn HirDatabase,
+ def: CallableDefId,
+) -> EarlyBinder<'db, PolyFnSig<'db>> {
+ 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 fn_sig_for_fn<'db>(
+ db: &'db dyn HirDatabase,
+ def: FunctionId,
+) -> EarlyBinder<'db, PolyFnSig<'db>> {
+ let data = db.function_signature(def);
+ let resolver = def.resolver(db);
+ let interner = DbInterner::new_with(db, Some(resolver.krate()), None);
+ let mut ctx_params = TyLoweringContext::new(
+ db,
+ &resolver,
+ &data.store,
+ def.into(),
+ LifetimeElisionKind::for_fn_params(&data),
+ );
+ 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(interner),
+ )
+ .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
+ ctx_ret.lower_ty(ret_type)
+ }
+ None => Ty::new_tup(interner, &[]),
+ };
+
+ let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret)));
+ // If/when we track late bound vars, we need to switch this to not be `dummy`
+ EarlyBinder::bind(rustc_type_ir::Binder::dummy(FnSig {
+ abi: data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol),
+ c_variadic: data.is_varargs(),
+ safety: if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe },
+ inputs_and_output,
+ }))
+}
+
+fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> {
+ let interner = DbInterner::new_with(db, None, None);
+ let args = GenericArgs::identity_for_item(interner, adt.into());
+ let ty = Ty::new_adt(interner, AdtDef::new(adt, interner), args);
+ EarlyBinder::bind(ty)
+}
+
+fn fn_sig_for_struct_constructor<'db>(
+ db: &'db dyn HirDatabase,
+ def: StructId,
+) -> EarlyBinder<'db, PolyFnSig<'db>> {
+ let field_tys = db.field_types_ns(def.into());
+ let params = field_tys.iter().map(|(_, ty)| ty.skip_binder());
+ let ret = type_for_adt(db, def.into()).skip_binder();
+
+ let inputs_and_output =
+ Tys::new_from_iter(DbInterner::new_with(db, None, None), params.chain(Some(ret)));
+ EarlyBinder::bind(Binder::dummy(FnSig {
+ abi: FnAbi::RustCall,
+ c_variadic: false,
+ safety: Safety::Safe,
+ inputs_and_output,
+ }))
+}
+
+fn fn_sig_for_enum_variant_constructor<'db>(
+ db: &'db dyn HirDatabase,
+ def: EnumVariantId,
+) -> EarlyBinder<'db, PolyFnSig<'db>> {
+ let field_tys = db.field_types_ns(def.into());
+ let params = field_tys.iter().map(|(_, ty)| ty.skip_binder());
+ let parent = def.lookup(db).parent;
+ let ret = type_for_adt(db, parent.into()).skip_binder();
+
+ let inputs_and_output =
+ Tys::new_from_iter(DbInterner::new_with(db, None, None), params.chain(Some(ret)));
+ EarlyBinder::bind(Binder::dummy(FnSig {
+ abi: FnAbi::RustCall,
+ c_variadic: false,
+ safety: Safety::Safe,
+ inputs_and_output,
+ }))
+}
+
+pub(crate) fn associated_type_by_name_including_super_traits<'db>(
+ db: &'db dyn HirDatabase,
+ trait_ref: TraitRef<'db>,
+ name: &Name,
+) -> Option<(TraitRef<'db>, TypeAliasId)> {
+ let interner = DbInterner::new_with(db, None, None);
+ rustc_type_ir::elaborate::supertraits(interner, Binder::dummy(trait_ref)).find_map(|t| {
+ let trait_id = match t.as_ref().skip_binder().def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => unreachable!(),
+ };
+ let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?;
+ Some((t.skip_binder(), assoc_type))
+ })
+}
diff --git a/crates/hir-ty/src/lower_nextsolver/path.rs b/crates/hir-ty/src/lower_nextsolver/path.rs
new file mode 100644
index 0000000..b95bb11
--- /dev/null
+++ b/crates/hir-ty/src/lower_nextsolver/path.rs
@@ -0,0 +1,1459 @@
+//! A wrapper around [`TyLoweringContext`] specifically for lowering paths.
+
+use std::ops::Deref;
+
+use either::Either;
+use hir_def::{
+ AssocItemId, GenericDefId, GenericParamId, Lookup, TraitId,
+ builtin_type::BuiltinType,
+ expr_store::{
+ ExpressionStore, HygieneId,
+ path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments},
+ },
+ hir::generics::{
+ GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance,
+ },
+ resolver::{ResolveValueResult, TypeNs, ValueNs},
+ signatures::TraitFlags,
+ type_ref::{TypeRef, TypeRefId},
+};
+use intern::sym;
+use rustc_hash::FxHashSet;
+use rustc_type_ir::{
+ AliasTerm, AliasTy, AliasTyKind, TypeVisitableExt,
+ inherent::{GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _},
+};
+use smallvec::{SmallVec, smallvec};
+use stdx::never;
+
+use crate::{
+ GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource,
+ PathLoweringDiagnostic, TyDefId, ValueTyDefId,
+ consteval_nextsolver::{unknown_const, unknown_const_as_generic},
+ db::HirDatabase,
+ generics::{Generics, generics},
+ lower::PathDiagnosticCallbackData,
+ lower_nextsolver::{LifetimeElisionKind, PredicateFilter, generic_predicates_filtered_by},
+ next_solver::{
+ AdtDef, Binder, Clause, Const, DbInterner, ErrorGuaranteed, Predicate, ProjectionPredicate,
+ Region, SolverDefId, TraitRef, Ty,
+ mapping::{ChalkToNextSolver, convert_binder_to_early_binder},
+ },
+ primitive,
+};
+
+use super::{
+ ImplTraitLoweringMode, TyLoweringContext, associated_type_by_name_including_super_traits,
+ const_param_ty_query, ty_query,
+};
+
+type CallbackData<'a> =
+ Either<PathDiagnosticCallbackData, crate::infer::diagnostics::PathDiagnosticCallbackData<'a>>;
+
+// We cannot use `&mut dyn FnMut()` because of lifetime issues, and we don't want to use `Box<dyn FnMut()>`
+// because of the allocation, so we create a lifetime-less callback, tailored for our needs.
+pub(crate) struct PathDiagnosticCallback<'a, 'db> {
+ pub(crate) data: CallbackData<'a>,
+ pub(crate) callback:
+ fn(&CallbackData<'_>, &mut TyLoweringContext<'db, '_>, PathLoweringDiagnostic),
+}
+
+pub(crate) struct PathLoweringContext<'a, 'b, 'db> {
+ ctx: &'a mut TyLoweringContext<'db, 'b>,
+ on_diagnostic: PathDiagnosticCallback<'a, 'db>,
+ path: &'a Path,
+ segments: PathSegments<'a>,
+ current_segment_idx: usize,
+ /// Contains the previous segment if `current_segment_idx == segments.len()`
+ current_or_prev_segment: PathSegment<'a>,
+}
+
+impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
+ #[inline]
+ pub(crate) fn new(
+ ctx: &'a mut TyLoweringContext<'db, 'b>,
+ on_diagnostic: PathDiagnosticCallback<'a, 'db>,
+ path: &'a Path,
+ ) -> Self {
+ let segments = path.segments();
+ let first_segment = segments.first().unwrap_or(PathSegment::MISSING);
+ Self {
+ ctx,
+ on_diagnostic,
+ path,
+ segments,
+ current_segment_idx: 0,
+ current_or_prev_segment: first_segment,
+ }
+ }
+
+ #[inline]
+ #[cold]
+ fn on_diagnostic(&mut self, diag: PathLoweringDiagnostic) {
+ (self.on_diagnostic.callback)(&self.on_diagnostic.data, self.ctx, diag);
+ }
+
+ #[inline]
+ pub(crate) fn ty_ctx(&mut self) -> &mut TyLoweringContext<'db, 'b> {
+ self.ctx
+ }
+
+ #[inline]
+ fn current_segment_u32(&self) -> u32 {
+ self.current_segment_idx as u32
+ }
+
+ #[inline]
+ fn skip_resolved_segment(&mut self) {
+ if !matches!(self.path, Path::LangItem(..)) {
+ // In lang items, the resolved "segment" is not one of the segments. Perhaps we should've put it
+ // point at -1, but I don't feel this is clearer.
+ self.current_segment_idx += 1;
+ }
+ self.update_current_segment();
+ }
+
+ #[inline]
+ fn update_current_segment(&mut self) {
+ self.current_or_prev_segment =
+ self.segments.get(self.current_segment_idx).unwrap_or(self.current_or_prev_segment);
+ }
+
+ #[inline]
+ pub(crate) fn ignore_last_segment(&mut self) {
+ self.segments = self.segments.strip_last();
+ }
+
+ #[inline]
+ pub(crate) fn set_current_segment(&mut self, segment: usize) {
+ self.current_segment_idx = segment;
+ self.current_or_prev_segment = self
+ .segments
+ .get(segment)
+ .expect("invalid segment passed to PathLoweringContext::set_current_segment()");
+ }
+
+ #[inline]
+ fn with_lifetime_elision<T>(
+ &mut self,
+ lifetime_elision: LifetimeElisionKind<'db>,
+ f: impl FnOnce(&mut PathLoweringContext<'_, '_, 'db>) -> T,
+ ) -> T {
+ let old_lifetime_elision =
+ std::mem::replace(&mut self.ctx.lifetime_elision, lifetime_elision);
+ let result = f(self);
+ self.ctx.lifetime_elision = old_lifetime_elision;
+ result
+ }
+
+ pub(crate) fn lower_ty_relative_path(
+ &mut self,
+ ty: Ty<'db>,
+ // We need the original resolution to lower `Self::AssocTy` correctly
+ res: Option<TypeNs>,
+ ) -> (Ty<'db>, Option<TypeNs>) {
+ let remaining_segments = self.segments.len() - self.current_segment_idx;
+ match remaining_segments {
+ 0 => (ty, res),
+ 1 => {
+ // resolve unselected assoc types
+ (self.select_associated_type(res), None)
+ }
+ _ => {
+ // FIXME report error (ambiguous associated type)
+ (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None)
+ }
+ }
+ }
+
+ fn prohibit_parenthesized_generic_args(&mut self) -> bool {
+ if let Some(generic_args) = self.current_or_prev_segment.args_and_bindings {
+ match generic_args.parenthesized {
+ GenericArgsParentheses::No => {}
+ GenericArgsParentheses::ReturnTypeNotation | GenericArgsParentheses::ParenSugar => {
+ let segment = self.current_segment_u32();
+ self.on_diagnostic(
+ PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment },
+ );
+ return true;
+ }
+ }
+ }
+ false
+ }
+
+ // When calling this, the current segment is the resolved segment (we don't advance it yet).
+ pub(crate) fn lower_partly_resolved_path(
+ &mut self,
+ resolution: TypeNs,
+ infer_args: bool,
+ ) -> (Ty<'db>, Option<TypeNs>) {
+ let remaining_segments = self.segments.skip(self.current_segment_idx + 1);
+ tracing::debug!(?remaining_segments);
+ let rem_seg_len = remaining_segments.len();
+ tracing::debug!(?rem_seg_len);
+
+ let ty = match resolution {
+ TypeNs::TraitId(trait_) => {
+ let ty = match remaining_segments.len() {
+ 1 => {
+ let trait_ref = self.lower_trait_ref_from_resolved_path(
+ trait_,
+ Ty::new_error(self.ctx.interner, ErrorGuaranteed),
+ );
+ tracing::debug!(?trait_ref);
+ self.skip_resolved_segment();
+ let segment = self.current_or_prev_segment;
+ let trait_id = match trait_ref.def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => unreachable!(),
+ };
+ let found =
+ trait_id.trait_items(self.ctx.db).associated_type_by_name(segment.name);
+
+ tracing::debug!(?found);
+ match found {
+ Some(associated_ty) => {
+ // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
+ // generic params. It's inefficient to splice the `Substitution`s, so we may want
+ // that method to optionally take parent `Substitution` as we already know them at
+ // this point (`trait_ref.substitution`).
+ let substitution = self.substs_from_path_segment(
+ associated_ty.into(),
+ false,
+ None,
+ true,
+ );
+ let args = crate::next_solver::GenericArgs::new_from_iter(
+ self.ctx.interner,
+ trait_ref
+ .args
+ .iter()
+ .chain(substitution.iter().skip(trait_ref.args.len())),
+ );
+ Ty::new_alias(
+ self.ctx.interner,
+ AliasTyKind::Projection,
+ AliasTy::new_from_args(
+ self.ctx.interner,
+ associated_ty.into(),
+ args,
+ ),
+ )
+ }
+ None => {
+ // FIXME: report error (associated type not found)
+ Ty::new_error(self.ctx.interner, ErrorGuaranteed)
+ }
+ }
+ }
+ 0 => {
+ // Trait object type without dyn; this should be handled in upstream. See
+ // `lower_path()`.
+ stdx::never!("unexpected fully resolved trait path");
+ Ty::new_error(self.ctx.interner, ErrorGuaranteed)
+ }
+ _ => {
+ // FIXME report error (ambiguous associated type)
+ Ty::new_error(self.ctx.interner, ErrorGuaranteed)
+ }
+ };
+ return (ty, None);
+ }
+ TypeNs::TraitAliasId(_) => {
+ // FIXME(trait_alias): Implement trait alias.
+ return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None);
+ }
+ TypeNs::GenericParam(param_id) => {
+ let generics = self.ctx.generics();
+ let idx = generics.type_or_const_param_idx(param_id.into());
+ match idx {
+ None => {
+ never!("no matching generics");
+ Ty::new_error(self.ctx.interner, ErrorGuaranteed)
+ }
+ Some(idx) => {
+ let (pidx, param) = generics.iter().nth(idx).unwrap();
+ assert_eq!(pidx, param_id.into());
+ let p = match param {
+ GenericParamDataRef::TypeParamData(p) => p,
+ _ => unreachable!(),
+ };
+ Ty::new_param(
+ self.ctx.interner,
+ idx as u32,
+ p.name
+ .as_ref()
+ .map_or_else(|| sym::MISSING_NAME.clone(), |p| p.symbol().clone()),
+ )
+ }
+ }
+ }
+ TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty_ns(impl_id).skip_binder(),
+ TypeNs::AdtSelfType(adt) => {
+ let args = crate::next_solver::GenericArgs::identity_for_item(
+ self.ctx.interner,
+ adt.into(),
+ );
+ Ty::new_adt(self.ctx.interner, AdtDef::new(adt, self.ctx.interner), args)
+ }
+
+ TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args),
+ TypeNs::BuiltinType(it) => self.lower_path_inner(it.into(), infer_args),
+ TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args),
+ // FIXME: report error
+ TypeNs::EnumVariantId(_) | TypeNs::ModuleId(_) => {
+ return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None);
+ }
+ };
+
+ tracing::debug!(?ty);
+
+ self.skip_resolved_segment();
+ self.lower_ty_relative_path(ty, Some(resolution))
+ }
+
+ fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) {
+ let mut prohibit_generics_on_resolved = |reason| {
+ if self.current_or_prev_segment.args_and_bindings.is_some() {
+ let segment = self.current_segment_u32();
+ self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+ segment,
+ reason,
+ });
+ }
+ };
+
+ match resolution {
+ TypeNs::SelfType(_) => {
+ prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
+ }
+ TypeNs::GenericParam(_) => {
+ prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam)
+ }
+ TypeNs::AdtSelfType(_) => {
+ prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
+ }
+ TypeNs::BuiltinType(_) => {
+ prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy)
+ }
+ TypeNs::ModuleId(_) => {
+ prohibit_generics_on_resolved(GenericArgsProhibitedReason::Module)
+ }
+ TypeNs::AdtId(_)
+ | TypeNs::EnumVariantId(_)
+ | TypeNs::TypeAliasId(_)
+ | TypeNs::TraitId(_)
+ | TypeNs::TraitAliasId(_) => {}
+ }
+ }
+
+ pub(crate) fn resolve_path_in_type_ns_fully(&mut self) -> Option<TypeNs> {
+ let (res, unresolved) = self.resolve_path_in_type_ns()?;
+ if unresolved.is_some() {
+ return None;
+ }
+ Some(res)
+ }
+
+ #[tracing::instrument(skip(self), ret)]
+ pub(crate) fn resolve_path_in_type_ns(&mut self) -> Option<(TypeNs, Option<usize>)> {
+ let (resolution, remaining_index, _, prefix_info) =
+ self.ctx.resolver.resolve_path_in_type_ns_with_prefix_info(self.ctx.db, self.path)?;
+
+ let segments = self.segments;
+ if segments.is_empty() || matches!(self.path, Path::LangItem(..)) {
+ // `segments.is_empty()` can occur with `self`.
+ return Some((resolution, remaining_index));
+ }
+
+ let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index {
+ None if prefix_info.enum_variant => {
+ (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2))
+ }
+ None => (segments.strip_last(), segments.len() - 1, None),
+ Some(i) => (segments.take(i - 1), i - 1, None),
+ };
+
+ self.current_segment_idx = resolved_segment_idx;
+ self.current_or_prev_segment =
+ segments.get(resolved_segment_idx).expect("should have resolved segment");
+
+ if matches!(self.path, Path::BarePath(..)) {
+ // Bare paths cannot have generics, so skip them as an optimization.
+ return Some((resolution, remaining_index));
+ }
+
+ for (i, mod_segment) in module_segments.iter().enumerate() {
+ if mod_segment.args_and_bindings.is_some() {
+ self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+ segment: i as u32,
+ reason: GenericArgsProhibitedReason::Module,
+ });
+ }
+ }
+
+ if let Some(enum_segment) = enum_segment
+ && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
+ && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
+ {
+ self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+ segment: (enum_segment + 1) as u32,
+ reason: GenericArgsProhibitedReason::EnumVariant,
+ });
+ }
+
+ self.handle_type_ns_resolution(&resolution);
+
+ Some((resolution, remaining_index))
+ }
+
+ pub(crate) fn resolve_path_in_value_ns(
+ &mut self,
+ hygiene_id: HygieneId,
+ ) -> Option<ResolveValueResult> {
+ let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info(
+ self.ctx.db,
+ self.path,
+ hygiene_id,
+ )?;
+
+ let segments = self.segments;
+ if segments.is_empty() || matches!(self.path, Path::LangItem(..)) {
+ // `segments.is_empty()` can occur with `self`.
+ return Some(res);
+ }
+
+ let (mod_segments, enum_segment, resolved_segment_idx) = match res {
+ ResolveValueResult::Partial(_, unresolved_segment, _) => {
+ (segments.take(unresolved_segment - 1), None, unresolved_segment - 1)
+ }
+ ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _)
+ if prefix_info.enum_variant =>
+ {
+ (segments.strip_last_two(), segments.len().checked_sub(2), segments.len() - 1)
+ }
+ ResolveValueResult::ValueNs(..) => (segments.strip_last(), None, segments.len() - 1),
+ };
+
+ self.current_segment_idx = resolved_segment_idx;
+ self.current_or_prev_segment =
+ segments.get(resolved_segment_idx).expect("should have resolved segment");
+
+ for (i, mod_segment) in mod_segments.iter().enumerate() {
+ if mod_segment.args_and_bindings.is_some() {
+ self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+ segment: i as u32,
+ reason: GenericArgsProhibitedReason::Module,
+ });
+ }
+ }
+
+ if let Some(enum_segment) = enum_segment
+ && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
+ && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
+ {
+ self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+ segment: (enum_segment + 1) as u32,
+ reason: GenericArgsProhibitedReason::EnumVariant,
+ });
+ }
+
+ match &res {
+ ResolveValueResult::ValueNs(resolution, _) => {
+ let resolved_segment_idx = self.current_segment_u32();
+ let resolved_segment = self.current_or_prev_segment;
+
+ let mut prohibit_generics_on_resolved = |reason| {
+ if resolved_segment.args_and_bindings.is_some() {
+ self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+ segment: resolved_segment_idx,
+ reason,
+ });
+ }
+ };
+
+ match resolution {
+ ValueNs::ImplSelf(_) => {
+ prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
+ }
+ // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not
+ // E0109 (generic arguments provided for a type that doesn't accept them) for
+ // consts and statics, presumably as a defense against future in which consts
+ // and statics can be generic, or just because it was easier for rustc implementors.
+ // That means we'll show the wrong error code. Because of us it's easier to do it
+ // this way :)
+ ValueNs::GenericParam(_) | ValueNs::ConstId(_) => {
+ prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const)
+ }
+ ValueNs::StaticId(_) => {
+ prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static)
+ }
+ ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {}
+ ValueNs::LocalBinding(_) => {}
+ }
+ }
+ ResolveValueResult::Partial(resolution, _, _) => {
+ self.handle_type_ns_resolution(resolution);
+ }
+ };
+ Some(res)
+ }
+
+ #[tracing::instrument(skip(self), ret)]
+ fn select_associated_type(&mut self, res: Option<TypeNs>) -> Ty<'db> {
+ let interner = self.ctx.interner;
+ let Some(res) = res else {
+ return Ty::new_error(self.ctx.interner, ErrorGuaranteed);
+ };
+ let segment = self.current_or_prev_segment;
+ let assoc_name = segment.name;
+ let db = self.ctx.db;
+ let def = self.ctx.def;
+ let mut search = |t: TraitRef<'db>| {
+ let trait_id = match t.def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => unreachable!(),
+ };
+ let mut checked_traits = FxHashSet::default();
+ let mut check_trait = |trait_id: TraitId| {
+ let name = &db.trait_signature(trait_id).name;
+ tracing::debug!(?trait_id, ?name);
+ if !checked_traits.insert(trait_id) {
+ return None;
+ }
+ let data = trait_id.trait_items(db);
+
+ tracing::debug!(?data.items);
+ for (name, assoc_id) in &data.items {
+ if let &AssocItemId::TypeAliasId(alias) = assoc_id {
+ if name != assoc_name {
+ continue;
+ }
+
+ // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
+ // generic params. It's inefficient to splice the `Substitution`s, so we may want
+ // that method to optionally take parent `Substitution` as we already know them at
+ // this point (`t.substitution`).
+ let substs = self.substs_from_path_segment(alias.into(), false, None, true);
+
+ let substs = crate::next_solver::GenericArgs::new_from_iter(
+ interner,
+ t.args.iter().chain(substs.iter().skip(t.args.len())),
+ );
+
+ return Some(Ty::new_alias(
+ interner,
+ AliasTyKind::Projection,
+ AliasTy::new(interner, alias.into(), substs),
+ ));
+ }
+ }
+ 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) {
+ return alias;
+ }
+ for pred in generic_predicates_filtered_by(
+ db,
+ GenericDefId::TraitId(trait_def_id),
+ PredicateFilter::SelfTrait,
+ |pred| pred == GenericDefId::TraitId(trait_def_id),
+ )
+ .0
+ .deref()
+ {
+ tracing::debug!(?pred);
+ let trait_id = match pred.kind().skip_binder() {
+ rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(),
+ _ => continue,
+ };
+ let trait_id = match trait_id {
+ SolverDefId::TraitId(trait_id) => trait_id,
+ _ => continue,
+ };
+ stack.push(trait_id);
+ }
+ tracing::debug!(?stack);
+ }
+
+ Ty::new_error(interner, ErrorGuaranteed)
+ };
+
+ match res {
+ TypeNs::SelfType(impl_id) => {
+ let trait_ref = db.impl_trait_ns(impl_id);
+ let Some(trait_ref) = trait_ref else {
+ return Ty::new_error(interner, ErrorGuaranteed);
+ };
+
+ // we're _in_ the impl -- the binders get added back later. Correct,
+ // but it would be nice to make this more explicit
+ search(trait_ref.skip_binder())
+ }
+ TypeNs::GenericParam(param_id) => {
+ // Handle `Self::Type` referring to own associated type in trait definitions
+ // This *must* be done first to avoid cycles with
+ // `generic_predicates_for_param`, but not sure that it's sufficient,
+ // see FIXME in `search`.
+ if let GenericDefId::TraitId(trait_id) = param_id.parent() {
+ let trait_name = &db.trait_signature(trait_id).name;
+ tracing::debug!(?trait_name);
+ let trait_generics = generics(db, trait_id.into());
+ tracing::debug!(?trait_generics);
+ if trait_generics[param_id.local_id()].is_trait_self() {
+ let args = crate::next_solver::GenericArgs::identity_for_item(
+ interner,
+ trait_id.into(),
+ );
+ let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args);
+ tracing::debug!(?args, ?trait_ref);
+ return search(trait_ref);
+ }
+ }
+
+ let predicates = db.generic_predicates_for_param_ns(
+ def,
+ param_id.into(),
+ Some(segment.name.clone()),
+ );
+ predicates
+ .iter()
+ .find_map(|pred| match (*pred).kind().skip_binder() {
+ rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate),
+ _ => None,
+ })
+ .map(|trait_predicate| {
+ let trait_ref = trait_predicate.trait_ref;
+ assert!(
+ !trait_ref.has_escaping_bound_vars(),
+ "FIXME unexpected higher-ranked trait bound"
+ );
+ search(trait_ref)
+ })
+ .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed))
+ }
+ _ => Ty::new_error(interner, ErrorGuaranteed),
+ }
+ }
+
+ fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> {
+ let generic_def = match typeable {
+ TyDefId::BuiltinType(builtinty) => return builtin(self.ctx.interner, builtinty),
+ TyDefId::AdtId(it) => it.into(),
+ TyDefId::TypeAliasId(it) => it.into(),
+ };
+ let args = self.substs_from_path_segment(generic_def, infer_args, None, false);
+ let ty = ty_query(self.ctx.db, typeable);
+ ty.instantiate(self.ctx.interner, args)
+ }
+
+ /// Collect generic arguments from a path into a `Substs`. See also
+ /// `create_substs_for_ast_path` and `def_to_ty` in rustc.
+ pub(crate) fn substs_from_path(
+ &mut self,
+ // Note that we don't call `db.value_type(resolved)` here,
+ // `ValueTyDefId` is just a convenient way to pass generics and
+ // special-case enum variants
+ resolved: ValueTyDefId,
+ infer_args: bool,
+ lowering_assoc_type_generics: bool,
+ ) -> crate::next_solver::GenericArgs<'db> {
+ let interner = self.ctx.interner;
+ let prev_current_segment_idx = self.current_segment_idx;
+ let prev_current_segment = self.current_or_prev_segment;
+
+ let generic_def = match resolved {
+ ValueTyDefId::FunctionId(it) => it.into(),
+ ValueTyDefId::StructId(it) => it.into(),
+ ValueTyDefId::UnionId(it) => it.into(),
+ ValueTyDefId::ConstId(it) => it.into(),
+ ValueTyDefId::StaticId(_) => {
+ return crate::next_solver::GenericArgs::new_from_iter(interner, []);
+ }
+ ValueTyDefId::EnumVariantId(var) => {
+ // the generic args for an enum variant may be either specified
+ // on the segment referring to the enum, or on the segment
+ // referring to the variant. So `Option::<T>::None` and
+ // `Option::None::<T>` are both allowed (though the former is
+ // FIXME: This isn't strictly correct, enum variants may be used not through the enum
+ // (via `use Enum::Variant`). The resolver returns whether they were, but we don't have its result
+ // available here. The worst that can happen is that we will show some confusing diagnostics to the user,
+ // if generics exist on the module and they don't match with the variant.
+ // preferred). See also `def_ids_for_path_segments` in rustc.
+ //
+ // `wrapping_sub(1)` will return a number which `get` will return None for if current_segment_idx<2.
+ // This simplifies the code a bit.
+ let penultimate_idx = self.current_segment_idx.wrapping_sub(1);
+ let penultimate = self.segments.get(penultimate_idx);
+ if let Some(penultimate) = penultimate
+ && self.current_or_prev_segment.args_and_bindings.is_none()
+ && penultimate.args_and_bindings.is_some()
+ {
+ self.current_segment_idx = penultimate_idx;
+ self.current_or_prev_segment = penultimate;
+ }
+ var.lookup(self.ctx.db).parent.into()
+ }
+ };
+ let result = self.substs_from_path_segment(
+ generic_def,
+ infer_args,
+ None,
+ lowering_assoc_type_generics,
+ );
+ self.current_segment_idx = prev_current_segment_idx;
+ self.current_or_prev_segment = prev_current_segment;
+ result
+ }
+
+ pub(crate) fn substs_from_path_segment(
+ &mut self,
+ def: GenericDefId,
+ infer_args: bool,
+ 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();
+
+ if let Some(args) = self.current_or_prev_segment.args_and_bindings
+ && args.parenthesized != GenericArgsParentheses::No
+ {
+ let prohibit_parens = match def {
+ GenericDefId::TraitId(trait_) => {
+ // RTN is prohibited anyways if we got here.
+ let is_rtn = args.parenthesized == GenericArgsParentheses::ReturnTypeNotation;
+ let is_fn_trait = self
+ .ctx
+ .db
+ .trait_signature(trait_)
+ .flags
+ .contains(TraitFlags::RUSTC_PAREN_SUGAR);
+ is_rtn || !is_fn_trait
+ }
+ _ => true,
+ };
+
+ if prohibit_parens {
+ let segment = self.current_segment_u32();
+ self.on_diagnostic(
+ PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment },
+ );
+
+ return unknown_subst(self.ctx.interner, def);
+ }
+
+ // `Fn()`-style generics are treated like functions for the purpose of lifetime elision.
+ lifetime_elision =
+ LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false };
+ }
+
+ 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,
+ )
+ }
+
+ pub(super) fn substs_from_args_and_bindings(
+ &mut self,
+ args_and_bindings: Option<&GenericArgs>,
+ def: GenericDefId,
+ infer_args: bool,
+ explicit_self_ty: Option<Ty<'db>>,
+ generics_source: PathGenericsSource,
+ lowering_assoc_type_generics: bool,
+ lifetime_elision: LifetimeElisionKind<'db>,
+ ) -> crate::next_solver::GenericArgs<'db> {
+ struct LowererCtx<'a, 'b, 'c, 'db> {
+ ctx: &'a mut PathLoweringContext<'b, 'c, 'db>,
+ generics_source: PathGenericsSource,
+ }
+
+ impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, '_, 'db> {
+ fn report_len_mismatch(
+ &mut self,
+ def: GenericDefId,
+ provided_count: u32,
+ expected_count: u32,
+ kind: IncorrectGenericsLenKind,
+ ) {
+ self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsLen {
+ generics_source: self.generics_source,
+ provided_count,
+ expected_count,
+ kind,
+ def,
+ });
+ }
+
+ fn report_arg_mismatch(
+ &mut self,
+ param_id: GenericParamId,
+ arg_idx: u32,
+ has_self_arg: bool,
+ ) {
+ self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsOrder {
+ generics_source: self.generics_source,
+ param_id,
+ arg_idx,
+ has_self_arg,
+ });
+ }
+
+ fn provided_kind(
+ &mut self,
+ param_id: GenericParamId,
+ param: GenericParamDataRef<'_>,
+ arg: &GenericArg,
+ ) -> crate::next_solver::GenericArg<'db> {
+ match (param, arg) {
+ (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => {
+ self.ctx.ctx.lower_lifetime(*lifetime).into()
+ }
+ (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => {
+ self.ctx.ctx.lower_ty(*type_ref).into()
+ }
+ (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => {
+ let GenericParamId::ConstParamId(const_id) = param_id else {
+ unreachable!("non-const param ID for const param");
+ };
+ self.ctx
+ .ctx
+ .lower_const(konst, const_param_ty_query(self.ctx.ctx.db, const_id))
+ .into()
+ }
+ _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"),
+ }
+ }
+
+ fn provided_type_like_const(
+ &mut self,
+ const_ty: Ty<'db>,
+ arg: TypeLikeConst<'_>,
+ ) -> crate::next_solver::Const<'db> {
+ match arg {
+ TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty),
+ TypeLikeConst::Infer => unknown_const(const_ty),
+ }
+ }
+
+ fn inferred_kind(
+ &mut self,
+ def: GenericDefId,
+ param_id: GenericParamId,
+ param: GenericParamDataRef<'_>,
+ infer_args: bool,
+ preceding_args: &[crate::next_solver::GenericArg<'db>],
+ ) -> crate::next_solver::GenericArg<'db> {
+ let default = || {
+ self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map(|default| {
+ convert_binder_to_early_binder(
+ self.ctx.ctx.interner,
+ default.to_nextsolver(self.ctx.ctx.interner),
+ )
+ .instantiate(self.ctx.ctx.interner, preceding_args)
+ })
+ };
+ match param {
+ GenericParamDataRef::LifetimeParamData(_) => {
+ Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed))
+ .into()
+ }
+ GenericParamDataRef::TypeParamData(param) => {
+ if !infer_args
+ && param.default.is_some()
+ && let Some(default) = default()
+ {
+ return default;
+ }
+ Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into()
+ }
+ GenericParamDataRef::ConstParamData(param) => {
+ if !infer_args
+ && param.default.is_some()
+ && let Some(default) = default()
+ {
+ return default;
+ }
+ let GenericParamId::ConstParamId(const_id) = param_id else {
+ unreachable!("non-const param ID for const param");
+ };
+ unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id))
+ }
+ }
+ }
+
+ fn parent_arg(
+ &mut self,
+ param_id: GenericParamId,
+ ) -> crate::next_solver::GenericArg<'db> {
+ match param_id {
+ GenericParamId::TypeParamId(_) => {
+ Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into()
+ }
+ GenericParamId::ConstParamId(const_id) => {
+ unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id))
+ }
+ GenericParamId::LifetimeParamId(_) => {
+ Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed))
+ .into()
+ }
+ }
+ }
+
+ fn report_elided_lifetimes_in_path(
+ &mut self,
+ def: GenericDefId,
+ expected_count: u32,
+ hard_error: bool,
+ ) {
+ self.ctx.on_diagnostic(PathLoweringDiagnostic::ElidedLifetimesInPath {
+ generics_source: self.generics_source,
+ def,
+ expected_count,
+ hard_error,
+ });
+ }
+
+ fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32) {
+ self.ctx.on_diagnostic(PathLoweringDiagnostic::ElisionFailure {
+ generics_source: self.generics_source,
+ def,
+ expected_count,
+ });
+ }
+
+ fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32) {
+ self.ctx.on_diagnostic(PathLoweringDiagnostic::MissingLifetime {
+ generics_source: self.generics_source,
+ def,
+ expected_count,
+ });
+ }
+ }
+
+ substs_from_args_and_bindings(
+ self.ctx.db,
+ self.ctx.store,
+ args_and_bindings,
+ def,
+ infer_args,
+ lifetime_elision,
+ lowering_assoc_type_generics,
+ explicit_self_ty,
+ &mut LowererCtx { ctx: self, generics_source },
+ )
+ }
+
+ pub(crate) fn lower_trait_ref_from_resolved_path(
+ &mut self,
+ resolved: TraitId,
+ explicit_self_ty: Ty<'db>,
+ ) -> TraitRef<'db> {
+ let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty);
+ TraitRef::new_from_args(self.ctx.interner, resolved.into(), args)
+ }
+
+ fn trait_ref_substs_from_path(
+ &mut self,
+ resolved: TraitId,
+ explicit_self_ty: Ty<'db>,
+ ) -> crate::next_solver::GenericArgs<'db> {
+ self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty), false)
+ }
+
+ pub(super) fn assoc_type_bindings_from_type_bound<'c>(
+ mut self,
+ trait_ref: TraitRef<'db>,
+ ) -> Option<impl Iterator<Item = Clause<'db>> + use<'a, 'b, 'c, 'db>> {
+ let interner = self.ctx.interner;
+ self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| {
+ args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| {
+ let found = associated_type_by_name_including_super_traits(
+ self.ctx.db,
+ trait_ref,
+ &binding.name,
+ );
+ let (super_trait_ref, associated_ty) = match found {
+ None => return SmallVec::new(),
+ Some(t) => t,
+ };
+ let args =
+ self.with_lifetime_elision(LifetimeElisionKind::AnonymousReportError, |this| {
+ // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
+ // generic params. It's inefficient to splice the `Substitution`s, so we may want
+ // that method to optionally take parent `Substitution` as we already know them at
+ // this point (`super_trait_ref.substitution`).
+ this.substs_from_args_and_bindings(
+ binding.args.as_ref(),
+ associated_ty.into(),
+ false, // this is not relevant
+ Some(super_trait_ref.self_ty()),
+ PathGenericsSource::AssocType {
+ segment: this.current_segment_u32(),
+ assoc_type: binding_idx as u32,
+ },
+ false,
+ this.ctx.lifetime_elision.clone(),
+ )
+ });
+ let args = crate::next_solver::GenericArgs::new_from_iter(
+ interner,
+ super_trait_ref.args.iter().chain(args.iter().skip(super_trait_ref.args.len())),
+ );
+ let projection_term =
+ AliasTerm::new_from_args(interner, associated_ty.into(), args);
+ let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
+ binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
+ );
+ if let Some(type_ref) = binding.type_ref {
+ 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);
+ }
+ }
+ }
+ for bound in binding.bounds.iter() {
+ predicates.extend(self.ctx.lower_type_bound(
+ bound,
+ Ty::new_alias(
+ self.ctx.interner,
+ AliasTyKind::Projection,
+ AliasTy::new_from_args(self.ctx.interner, associated_ty.into(), args),
+ ),
+ false,
+ ));
+ }
+ predicates
+ })
+ })
+ }
+}
+
+/// A const that were parsed like a type.
+pub(crate) enum TypeLikeConst<'a> {
+ Infer,
+ Path(&'a Path),
+}
+
+pub(crate) trait GenericArgsLowerer<'db> {
+ fn report_elided_lifetimes_in_path(
+ &mut self,
+ def: GenericDefId,
+ expected_count: u32,
+ hard_error: bool,
+ );
+
+ fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32);
+
+ fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32);
+
+ fn report_len_mismatch(
+ &mut self,
+ def: GenericDefId,
+ provided_count: u32,
+ expected_count: u32,
+ kind: IncorrectGenericsLenKind,
+ );
+
+ fn report_arg_mismatch(&mut self, param_id: GenericParamId, arg_idx: u32, has_self_arg: bool);
+
+ fn provided_kind(
+ &mut self,
+ param_id: GenericParamId,
+ param: GenericParamDataRef<'_>,
+ arg: &GenericArg,
+ ) -> crate::next_solver::GenericArg<'db>;
+
+ fn provided_type_like_const(&mut self, const_ty: Ty<'db>, arg: TypeLikeConst<'_>)
+ -> Const<'db>;
+
+ fn inferred_kind(
+ &mut self,
+ def: GenericDefId,
+ param_id: GenericParamId,
+ param: GenericParamDataRef<'_>,
+ infer_args: bool,
+ preceding_args: &[crate::next_solver::GenericArg<'db>],
+ ) -> crate::next_solver::GenericArg<'db>;
+
+ fn parent_arg(&mut self, param_id: GenericParamId) -> crate::next_solver::GenericArg<'db>;
+}
+
+/// Returns true if there was an error.
+fn check_generic_args_len<'db>(
+ args_and_bindings: Option<&GenericArgs>,
+ def: GenericDefId,
+ def_generics: &Generics,
+ infer_args: bool,
+ lifetime_elision: &LifetimeElisionKind<'db>,
+ lowering_assoc_type_generics: bool,
+ ctx: &mut impl GenericArgsLowerer<'db>,
+) -> bool {
+ let mut had_error = false;
+
+ let (mut provided_lifetimes_count, mut provided_types_and_consts_count) = (0usize, 0usize);
+ if let Some(args_and_bindings) = args_and_bindings {
+ let args_no_self = &args_and_bindings.args[usize::from(args_and_bindings.has_self_type)..];
+ for arg in args_no_self {
+ match arg {
+ GenericArg::Lifetime(_) => provided_lifetimes_count += 1,
+ GenericArg::Type(_) | GenericArg::Const(_) => provided_types_and_consts_count += 1,
+ }
+ }
+ }
+
+ let lifetime_args_len = def_generics.len_lifetimes_self();
+ if provided_lifetimes_count == 0 && lifetime_args_len > 0 && !lowering_assoc_type_generics {
+ // In generic associated types, we never allow inferring the lifetimes.
+ match lifetime_elision {
+ &LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => {
+ ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path);
+ had_error |= report_in_path;
+ }
+ LifetimeElisionKind::AnonymousReportError => {
+ ctx.report_missing_lifetime(def, lifetime_args_len as u32);
+ had_error = true
+ }
+ LifetimeElisionKind::ElisionFailure => {
+ ctx.report_elision_failure(def, lifetime_args_len as u32);
+ had_error = true;
+ }
+ LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => {
+ // FIXME: Check there are other lifetimes in scope, and error/lint.
+ }
+ LifetimeElisionKind::Elided(_) => {
+ ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, false);
+ }
+ LifetimeElisionKind::Infer => {
+ // Allow eliding lifetimes.
+ }
+ }
+ } else if lifetime_args_len != provided_lifetimes_count {
+ ctx.report_len_mismatch(
+ def,
+ provided_lifetimes_count as u32,
+ lifetime_args_len as u32,
+ IncorrectGenericsLenKind::Lifetimes,
+ );
+ had_error = true;
+ }
+
+ let defaults_count =
+ def_generics.iter_self_type_or_consts().filter(|(_, param)| param.has_default()).count();
+ let named_type_and_const_params_count = def_generics
+ .iter_self_type_or_consts()
+ .filter(|(_, param)| match param {
+ TypeOrConstParamData::TypeParamData(param) => {
+ param.provenance == TypeParamProvenance::TypeParamList
+ }
+ TypeOrConstParamData::ConstParamData(_) => true,
+ })
+ .count();
+ let expected_max = named_type_and_const_params_count;
+ let expected_min =
+ if infer_args { 0 } else { named_type_and_const_params_count - defaults_count };
+ if provided_types_and_consts_count < expected_min
+ || expected_max < provided_types_and_consts_count
+ {
+ ctx.report_len_mismatch(
+ def,
+ provided_types_and_consts_count as u32,
+ named_type_and_const_params_count as u32,
+ IncorrectGenericsLenKind::TypesAndConsts,
+ );
+ had_error = true;
+ }
+
+ had_error
+}
+
+pub(crate) fn substs_from_args_and_bindings<'db>(
+ db: &'db dyn HirDatabase,
+ store: &ExpressionStore,
+ args_and_bindings: Option<&GenericArgs>,
+ def: GenericDefId,
+ mut infer_args: bool,
+ lifetime_elision: LifetimeElisionKind<'db>,
+ lowering_assoc_type_generics: bool,
+ explicit_self_ty: Option<Ty<'db>>,
+ ctx: &mut impl GenericArgsLowerer<'db>,
+) -> crate::next_solver::GenericArgs<'db> {
+ let interner = DbInterner::new_with(db, None, None);
+
+ tracing::debug!(?args_and_bindings);
+
+ // Order is
+ // - Parent parameters
+ // - Optional Self parameter
+ // - Lifetime parameters
+ // - Type or Const parameters
+ let def_generics = generics(db, def);
+ let args_slice = args_and_bindings.map(|it| &*it.args).unwrap_or_default();
+
+ // We do not allow inference if there are specified args, i.e. we do not allow partial inference.
+ let has_non_lifetime_args =
+ args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_)));
+ infer_args &= !has_non_lifetime_args;
+
+ let had_count_error = check_generic_args_len(
+ args_and_bindings,
+ def,
+ &def_generics,
+ infer_args,
+ &lifetime_elision,
+ lowering_assoc_type_generics,
+ ctx,
+ );
+
+ let mut substs = Vec::with_capacity(def_generics.len());
+
+ substs.extend(def_generics.iter_parent_id().map(|id| ctx.parent_arg(id)));
+
+ let mut args = args_slice.iter().enumerate().peekable();
+ let mut params = def_generics.iter_self().peekable();
+
+ // If we encounter a type or const when we expect a lifetime, we infer the lifetimes.
+ // If we later encounter a lifetime, we know that the arguments were provided in the
+ // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be
+ // inferred, so we can use it for diagnostics later.
+ let mut force_infer_lt = None;
+
+ let has_self_arg = args_and_bindings.is_some_and(|it| it.has_self_type);
+ // First, handle `Self` parameter. Consume it from the args if provided, otherwise from `explicit_self_ty`,
+ // and lastly infer it.
+ if let Some(&(
+ self_param_id,
+ self_param @ GenericParamDataRef::TypeParamData(TypeParamData {
+ provenance: TypeParamProvenance::TraitSelf,
+ ..
+ }),
+ )) = params.peek()
+ {
+ let self_ty = if has_self_arg {
+ let (_, self_ty) = args.next().expect("has_self_type=true, should have Self type");
+ ctx.provided_kind(self_param_id, self_param, self_ty)
+ } else {
+ explicit_self_ty.map(|it| it.into()).unwrap_or_else(|| {
+ ctx.inferred_kind(def, self_param_id, self_param, infer_args, &substs)
+ })
+ };
+ params.next();
+ substs.push(self_ty);
+ }
+
+ loop {
+ // We're going to iterate through the generic arguments that the user
+ // provided, matching them with the generic parameters we expect.
+ // Mismatches can occur as a result of elided lifetimes, or for malformed
+ // input. We try to handle both sensibly.
+ match (args.peek(), params.peek()) {
+ (Some(&(arg_idx, arg)), Some(&(param_id, param))) => match (arg, param) {
+ (GenericArg::Type(_), GenericParamDataRef::TypeParamData(type_param))
+ if type_param.provenance == TypeParamProvenance::ArgumentImplTrait =>
+ {
+ // Do not allow specifying `impl Trait` explicitly. We already err at that, but if we won't handle it here
+ // we will handle it as if it was specified, instead of inferring it.
+ substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
+ params.next();
+ }
+ (GenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_))
+ | (GenericArg::Type(_), GenericParamDataRef::TypeParamData(_))
+ | (GenericArg::Const(_), GenericParamDataRef::ConstParamData(_)) => {
+ substs.push(ctx.provided_kind(param_id, param, arg));
+ args.next();
+ params.next();
+ }
+ (
+ GenericArg::Type(_) | GenericArg::Const(_),
+ GenericParamDataRef::LifetimeParamData(_),
+ ) => {
+ // We expected a lifetime argument, but got a type or const
+ // argument. That means we're inferring the lifetime.
+ substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
+ params.next();
+ force_infer_lt = Some((arg_idx as u32, param_id));
+ }
+ (GenericArg::Type(type_ref), GenericParamDataRef::ConstParamData(_)) => {
+ if let Some(konst) = type_looks_like_const(store, *type_ref) {
+ let GenericParamId::ConstParamId(param_id) = param_id else {
+ panic!("unmatching param kinds");
+ };
+ let const_ty = const_param_ty_query(db, param_id);
+ substs.push(ctx.provided_type_like_const(const_ty, konst).into());
+ args.next();
+ params.next();
+ } else {
+ // See the `_ => { ... }` branch.
+ if !had_count_error {
+ ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg);
+ }
+ while args.next().is_some() {}
+ }
+ }
+ _ => {
+ // We expected one kind of parameter, but the user provided
+ // another. This is an error. However, if we already know that
+ // the arguments don't match up with the parameters, we won't issue
+ // an additional error, as the user already knows what's wrong.
+ if !had_count_error {
+ ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg);
+ }
+
+ // We've reported the error, but we want to make sure that this
+ // problem doesn't bubble down and create additional, irrelevant
+ // errors. In this case, we're simply going to ignore the argument
+ // and any following arguments. The rest of the parameters will be
+ // inferred.
+ while args.next().is_some() {}
+ }
+ },
+
+ (Some(&(_, arg)), None) => {
+ // We should never be able to reach this point with well-formed input.
+ // There are two situations in which we can encounter this issue.
+ //
+ // 1. The number of arguments is incorrect. In this case, an error
+ // will already have been emitted, and we can ignore it.
+ // 2. We've inferred some lifetimes, which have been provided later (i.e.
+ // after a type or const). We want to throw an error in this case.
+ if !had_count_error {
+ assert!(
+ matches!(arg, GenericArg::Lifetime(_)),
+ "the only possible situation here is incorrect lifetime order"
+ );
+ let (provided_arg_idx, param_id) =
+ force_infer_lt.expect("lifetimes ought to have been inferred");
+ ctx.report_arg_mismatch(param_id, provided_arg_idx, has_self_arg);
+ }
+
+ break;
+ }
+
+ (None, Some(&(param_id, param))) => {
+ // If there are fewer arguments than parameters, it means we're inferring the remaining arguments.
+ let param = if let GenericParamId::LifetimeParamId(_) = param_id {
+ match &lifetime_elision {
+ LifetimeElisionKind::ElisionFailure
+ | LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }
+ | LifetimeElisionKind::AnonymousReportError => {
+ assert!(had_count_error);
+ ctx.inferred_kind(def, param_id, param, infer_args, &substs)
+ }
+ LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => {
+ Region::new_static(interner).into()
+ }
+ LifetimeElisionKind::Elided(lifetime) => (*lifetime).into(),
+ LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }
+ | LifetimeElisionKind::Infer => {
+ // FIXME: With `AnonymousCreateParameter`, we need to create a new lifetime parameter here
+ // (but this will probably be done in hir-def lowering instead).
+ ctx.inferred_kind(def, param_id, param, infer_args, &substs)
+ }
+ }
+ } else {
+ ctx.inferred_kind(def, param_id, param, infer_args, &substs)
+ };
+ substs.push(param);
+ params.next();
+ }
+
+ (None, None) => break,
+ }
+ }
+
+ crate::next_solver::GenericArgs::new_from_iter(interner, substs)
+}
+
+fn type_looks_like_const(
+ store: &ExpressionStore,
+ type_ref: TypeRefId,
+) -> Option<TypeLikeConst<'_>> {
+ // A path/`_` const will be parsed as a type, instead of a const, because when parsing/lowering
+ // in hir-def we don't yet know the expected argument kind. rustc does this a bit differently,
+ // when lowering to HIR it resolves the path, and if it doesn't resolve to the type namespace
+ // it is lowered as a const. Our behavior could deviate from rustc when the value is resolvable
+ // in both the type and value namespaces, but I believe we only allow more code.
+ let type_ref = &store[type_ref];
+ match type_ref {
+ TypeRef::Path(path) => Some(TypeLikeConst::Path(path)),
+ TypeRef::Placeholder => Some(TypeLikeConst::Infer),
+ _ => None,
+ }
+}
+
+fn unknown_subst<'db>(
+ interner: DbInterner<'db>,
+ def: impl Into<GenericDefId>,
+) -> crate::next_solver::GenericArgs<'db> {
+ let params = generics(interner.db(), def.into());
+ crate::next_solver::GenericArgs::new_from_iter(
+ interner,
+ params.iter_id().map(|id| match id {
+ GenericParamId::TypeParamId(_) => Ty::new_error(interner, ErrorGuaranteed).into(),
+ GenericParamId::ConstParamId(id) => {
+ unknown_const_as_generic(const_param_ty_query(interner.db(), id))
+ }
+ GenericParamId::LifetimeParamId(_) => {
+ crate::next_solver::Region::error(interner).into()
+ }
+ }),
+ )
+}
+
+pub(crate) fn builtin<'db>(interner: DbInterner<'db>, builtin: BuiltinType) -> Ty<'db> {
+ match builtin {
+ BuiltinType::Char => Ty::new(interner, rustc_type_ir::TyKind::Char),
+ BuiltinType::Bool => Ty::new_bool(interner),
+ BuiltinType::Str => Ty::new(interner, rustc_type_ir::TyKind::Str),
+ BuiltinType::Int(t) => {
+ let int_ty = match primitive::int_ty_from_builtin(t) {
+ chalk_ir::IntTy::Isize => rustc_type_ir::IntTy::Isize,
+ chalk_ir::IntTy::I8 => rustc_type_ir::IntTy::I8,
+ chalk_ir::IntTy::I16 => rustc_type_ir::IntTy::I16,
+ chalk_ir::IntTy::I32 => rustc_type_ir::IntTy::I32,
+ chalk_ir::IntTy::I64 => rustc_type_ir::IntTy::I64,
+ chalk_ir::IntTy::I128 => rustc_type_ir::IntTy::I128,
+ };
+ Ty::new_int(interner, int_ty)
+ }
+ BuiltinType::Uint(t) => {
+ let uint_ty = match primitive::uint_ty_from_builtin(t) {
+ chalk_ir::UintTy::Usize => rustc_type_ir::UintTy::Usize,
+ chalk_ir::UintTy::U8 => rustc_type_ir::UintTy::U8,
+ chalk_ir::UintTy::U16 => rustc_type_ir::UintTy::U16,
+ chalk_ir::UintTy::U32 => rustc_type_ir::UintTy::U32,
+ chalk_ir::UintTy::U64 => rustc_type_ir::UintTy::U64,
+ chalk_ir::UintTy::U128 => rustc_type_ir::UintTy::U128,
+ };
+ Ty::new_uint(interner, uint_ty)
+ }
+ BuiltinType::Float(t) => {
+ let float_ty = match primitive::float_ty_from_builtin(t) {
+ chalk_ir::FloatTy::F16 => rustc_type_ir::FloatTy::F16,
+ chalk_ir::FloatTy::F32 => rustc_type_ir::FloatTy::F32,
+ chalk_ir::FloatTy::F64 => rustc_type_ir::FloatTy::F64,
+ chalk_ir::FloatTy::F128 => rustc_type_ir::FloatTy::F128,
+ };
+ Ty::new_float(interner, float_ty)
+ }
+ }
+}
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index b22781e..4943815 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -16,22 +16,24 @@
use hir_expand::name::Name;
use intern::sym;
use rustc_hash::{FxHashMap, FxHashSet};
+use rustc_type_ir::inherent::{IntoKind, SliceLike};
use smallvec::{SmallVec, smallvec};
use stdx::never;
use triomphe::Arc;
use crate::{
AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData,
- Goal, Guidance, InEnvironment, Interner, Mutability, Scalar, Solution, Substitution,
- TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind,
- VariableKind, WhereClause,
+ Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef,
+ TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause,
autoderef::{self, AutoderefKind},
db::HirDatabase,
error_lifetime, from_chalk_trait_id, from_foreign_def_id,
infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable},
lang_items::is_box,
+ next_solver::SolverDefId,
primitive::{FloatTy, IntTy, UintTy},
to_chalk_trait_id,
+ traits::NextTraitSolveResult,
utils::all_super_traits,
};
@@ -43,6 +45,7 @@
Slice,
Array,
Never,
+ Ref(Mutability),
RawPtr(Mutability),
Scalar(Scalar),
// These can have user-defined impls:
@@ -88,7 +91,7 @@
TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability),
TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id),
TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?,
- TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty),
+ TyKind::Ref(mutability, _, _) => TyFingerprint::Ref(*mutability),
TyKind::Tuple(_, subst) => {
let first_ty = subst.interned().first().map(|arg| arg.assert_ty_ref(Interner));
match first_ty {
@@ -113,6 +116,94 @@
};
Some(fp)
}
+
+ /// Creates a TyFingerprint for looking up a trait impl.
+ pub fn for_trait_impl_ns<'db>(ty: &crate::next_solver::Ty<'db>) -> Option<TyFingerprint> {
+ use rustc_type_ir::TyKind;
+ let fp = match (*ty).kind() {
+ TyKind::Str => TyFingerprint::Str,
+ TyKind::Never => TyFingerprint::Never,
+ TyKind::Slice(..) => TyFingerprint::Slice,
+ TyKind::Array(..) => TyFingerprint::Array,
+ TyKind::Int(int) => TyFingerprint::Scalar(Scalar::Int(match int {
+ rustc_type_ir::IntTy::Isize => IntTy::Isize,
+ rustc_type_ir::IntTy::I8 => IntTy::I8,
+ rustc_type_ir::IntTy::I16 => IntTy::I16,
+ rustc_type_ir::IntTy::I32 => IntTy::I32,
+ rustc_type_ir::IntTy::I64 => IntTy::I64,
+ rustc_type_ir::IntTy::I128 => IntTy::I128,
+ })),
+ TyKind::Uint(uint) => TyFingerprint::Scalar(Scalar::Uint(match uint {
+ rustc_type_ir::UintTy::Usize => UintTy::Usize,
+ rustc_type_ir::UintTy::U8 => UintTy::U8,
+ rustc_type_ir::UintTy::U16 => UintTy::U16,
+ rustc_type_ir::UintTy::U32 => UintTy::U32,
+ rustc_type_ir::UintTy::U64 => UintTy::U64,
+ rustc_type_ir::UintTy::U128 => UintTy::U128,
+ })),
+ TyKind::Float(float) => TyFingerprint::Scalar(Scalar::Float(match float {
+ rustc_type_ir::FloatTy::F16 => FloatTy::F16,
+ rustc_type_ir::FloatTy::F32 => FloatTy::F32,
+ rustc_type_ir::FloatTy::F64 => FloatTy::F64,
+ rustc_type_ir::FloatTy::F128 => FloatTy::F128,
+ })),
+ TyKind::Bool => TyFingerprint::Scalar(Scalar::Bool),
+ TyKind::Char => TyFingerprint::Scalar(Scalar::Char),
+ TyKind::Adt(def, _) => TyFingerprint::Adt(def.inner().id),
+ TyKind::RawPtr(.., mutability) => match mutability {
+ rustc_ast_ir::Mutability::Mut => TyFingerprint::RawPtr(Mutability::Mut),
+ rustc_ast_ir::Mutability::Not => TyFingerprint::RawPtr(Mutability::Not),
+ },
+ TyKind::Foreign(def) => {
+ let SolverDefId::ForeignId(def) = def else { unreachable!() };
+ TyFingerprint::ForeignType(crate::to_foreign_def_id(def))
+ }
+ TyKind::Dynamic(bounds, _, _) => {
+ let trait_ref = bounds
+ .as_slice()
+ .iter()
+ .map(|b| (*b).skip_binder())
+ .filter_map(|b| match b {
+ rustc_type_ir::ExistentialPredicate::Trait(t) => Some(t.def_id),
+ _ => None,
+ })
+ .next()?;
+ let trait_id = match trait_ref {
+ SolverDefId::TraitId(id) => id,
+ _ => panic!("Bad GenericDefId in trait ref"),
+ };
+ TyFingerprint::Dyn(trait_id)
+ }
+ TyKind::Ref(_, _, mutability) => match mutability {
+ rustc_ast_ir::Mutability::Mut => TyFingerprint::Ref(Mutability::Mut),
+ rustc_ast_ir::Mutability::Not => TyFingerprint::Ref(Mutability::Not),
+ },
+ TyKind::Tuple(tys) => {
+ let first_ty = tys.as_slice().iter().next();
+ match first_ty {
+ Some(ty) => return TyFingerprint::for_trait_impl_ns(ty),
+ None => TyFingerprint::Unit,
+ }
+ }
+ TyKind::FnDef(_, _)
+ | TyKind::Closure(_, _)
+ | TyKind::Coroutine(..)
+ | TyKind::CoroutineWitness(..)
+ | TyKind::Pat(..)
+ | TyKind::CoroutineClosure(..) => TyFingerprint::Unnameable,
+ TyKind::FnPtr(sig, _) => {
+ TyFingerprint::Function(sig.inputs().skip_binder().len() as u32)
+ }
+ TyKind::Alias(..)
+ | TyKind::Placeholder(_)
+ | TyKind::Bound(..)
+ | TyKind::Infer(_)
+ | TyKind::Error(_)
+ | TyKind::Param(..)
+ | TyKind::UnsafeBinder(..) => return None,
+ };
+ Some(fp)
+ }
}
pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [
@@ -807,10 +898,13 @@
let wcs = crate::chalk_db::convert_where_clauses(db, impl_.into(), &impl_substs)
.into_iter()
- .map(|b| b.cast(Interner));
- let goal = crate::Goal::all(Interner, wcs);
- table.try_obligation(goal.clone())?;
- table.register_obligation(goal);
+ .map(|b| -> Goal { b.cast(Interner) });
+ for goal in wcs {
+ if table.try_obligation(goal.clone()).no_solution() {
+ return None;
+ }
+ table.register_obligation(goal);
+ }
Some((impl_.impl_items(db), table.resolve_completely(impl_substs)))
})
})
@@ -1313,7 +1407,7 @@
};
if !known_implemented {
let goal = generic_implements_goal(db, &table.trait_env, t, &canonical_self_ty);
- if db.trait_solve(krate, block, goal.cast(Interner)).is_none() {
+ if db.trait_solve(krate, block, goal.cast(Interner)).no_solution() {
continue 'traits;
}
}
@@ -1496,9 +1590,9 @@
let deref_chain = autoderef_method_receiver(&mut table, ty);
for (ty, adj) in deref_chain {
let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ty);
- if db
+ if !db
.trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner))
- .is_some()
+ .no_solution()
{
return Some(adj);
}
@@ -1692,7 +1786,7 @@
);
match solution {
- Some(Solution::Unique(canonical_subst)) => {
+ NextTraitSolveResult::Certain(canonical_subst) => {
canonicalized.apply_solution(
table,
Canonical {
@@ -1701,16 +1795,13 @@
},
);
}
- Some(Solution::Ambig(Guidance::Definite(substs))) => {
- canonicalized.apply_solution(table, substs);
- }
- Some(_) => (),
- None => return IsValidCandidate::No,
+ NextTraitSolveResult::Uncertain(..) => {}
+ NextTraitSolveResult::NoSolution => return IsValidCandidate::No,
}
}
for goal in goals {
- if table.try_obligation(goal).is_none() {
+ if table.try_obligation(goal).no_solution() {
return IsValidCandidate::No;
}
}
@@ -1726,9 +1817,7 @@
trait_: TraitId,
) -> bool {
let goal = generic_implements_goal(db, env, trait_, ty);
- let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner));
-
- solution.is_some()
+ !db.trait_solve(env.krate, env.block, goal.cast(Interner)).no_solution()
}
pub fn implements_trait_unique(
@@ -1738,9 +1827,7 @@
trait_: TraitId,
) -> bool {
let goal = generic_implements_goal(db, env, trait_, ty);
- let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner));
-
- matches!(solution, Some(crate::Solution::Unique(_)))
+ db.trait_solve(env.krate, env.block, goal.cast(Interner)).certain()
}
/// This creates Substs for a trait with the given Self type and type variables
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index c1f8696..eb5af58 100644
--- a/crates/hir-ty/src/mir/eval/tests.rs
+++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -5,7 +5,9 @@
use test_fixture::WithFixture;
use crate::display::DisplayTarget;
-use crate::{Interner, Substitution, db::HirDatabase, mir::MirLowerError, test_db::TestDB};
+use crate::{
+ Interner, Substitution, db::HirDatabase, mir::MirLowerError, setup_tracing, test_db::TestDB,
+};
use super::{MirEvalError, interpret_mir};
@@ -49,6 +51,7 @@
expected_stdout: &str,
expected_stderr: &str,
) {
+ let _tracing = setup_tracing();
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
let file_id = *file_ids.last().unwrap();
let x = eval_main(&db, file_id);
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index eb80e87..0bb8e6f 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -2163,7 +2163,9 @@
let _p = tracing::info_span!("mir_body_query", ?detail).entered();
let body = db.body(def);
let infer = db.infer(def);
- let mut result = lower_to_mir(db, def, &body, &infer, body.body_expr)?;
+ let mut result = crate::next_solver::with_new_cache(|| {
+ lower_to_mir(db, def, &body, &infer, body.body_expr)
+ })?;
result.shrink_to_fit();
Ok(Arc::new(result))
}
diff --git a/crates/hir-ty/src/next_solver.rs b/crates/hir-ty/src/next_solver.rs
new file mode 100644
index 0000000..21762aa
--- /dev/null
+++ b/crates/hir-ty/src/next_solver.rs
@@ -0,0 +1,46 @@
+//! Things relevant to the next trait solver.
+#![allow(unused, unreachable_pub)]
+
+pub mod abi;
+mod consts;
+mod def_id;
+pub mod fold;
+mod fulfill;
+mod generic_arg;
+pub mod generics;
+pub mod infer;
+//mod infer_new;
+pub mod interner;
+mod ir_print;
+pub mod mapping;
+mod opaques;
+pub mod predicate;
+pub(crate) mod project;
+mod region;
+mod solver;
+mod ty;
+pub mod util;
+
+pub use consts::*;
+pub use def_id::*;
+pub use generic_arg::*;
+//pub use infer_new::*;
+pub use interner::*;
+pub use opaques::*;
+pub use predicate::*;
+pub use region::*;
+pub use solver::*;
+pub use ty::*;
+
+pub type Binder<'db, T> = rustc_type_ir::Binder<DbInterner<'db>, T>;
+pub type EarlyBinder<'db, T> = rustc_type_ir::EarlyBinder<DbInterner<'db>, T>;
+pub type Canonical<'db, T> = rustc_type_ir::Canonical<DbInterner<'db>, T>;
+pub type CanonicalVarValues<'db> = rustc_type_ir::CanonicalVarValues<DbInterner<'db>>;
+pub type CanonicalVarKind<'db> = rustc_type_ir::CanonicalVarKind<DbInterner<'db>>;
+pub type CanonicalQueryInput<'db, V> = rustc_type_ir::CanonicalQueryInput<DbInterner<'db>, V>;
+pub type AliasTy<'db> = rustc_type_ir::AliasTy<DbInterner<'db>>;
+pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig<DbInterner<'db>>>;
+pub type TypingMode<'db> = rustc_type_ir::TypingMode<DbInterner<'db>>;
+
+pub type FxIndexMap<K, V> =
+ indexmap::IndexMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
diff --git a/crates/hir-ty/src/next_solver/abi.rs b/crates/hir-ty/src/next_solver/abi.rs
new file mode 100644
index 0000000..80d1ea4
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/abi.rs
@@ -0,0 +1,68 @@
+//! ABI-related things in the next-trait-solver.
+use rustc_type_ir::{error::TypeError, relate::Relate};
+
+use crate::FnAbi;
+
+use super::interner::DbInterner;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+pub enum Safety {
+ Unsafe,
+ Safe,
+}
+
+impl<'db> Relate<DbInterner<'db>> for Safety {
+ fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
+ _relation: &mut R,
+ a: Self,
+ b: Self,
+ ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
+ if a != b {
+ Err(TypeError::SafetyMismatch(rustc_type_ir::error::ExpectedFound::new(a, b)))
+ } else {
+ Ok(a)
+ }
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::Safety<DbInterner<'db>> for Safety {
+ fn safe() -> Self {
+ Self::Safe
+ }
+
+ fn is_safe(self) -> bool {
+ matches!(self, Safety::Safe)
+ }
+
+ fn prefix_str(self) -> &'static str {
+ match self {
+ Self::Unsafe => "unsafe ",
+ Self::Safe => "",
+ }
+ }
+}
+
+impl<'db> Relate<DbInterner<'db>> for FnAbi {
+ fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
+ _relation: &mut R,
+ a: Self,
+ b: Self,
+ ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
+ if a == b {
+ Ok(a)
+ } else {
+ Err(TypeError::AbiMismatch(rustc_type_ir::error::ExpectedFound::new(a, b)))
+ }
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::Abi<DbInterner<'db>> for FnAbi {
+ fn rust() -> Self {
+ FnAbi::Rust
+ }
+
+ fn is_rust(self) -> bool {
+ // TODO: rustc does not consider `RustCall` to be true here, but Chalk does
+ matches!(self, FnAbi::Rust | FnAbi::RustCall)
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/consts.rs b/crates/hir-ty/src/next_solver/consts.rs
new file mode 100644
index 0000000..5698ff2
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/consts.rs
@@ -0,0 +1,404 @@
+//! Things related to consts in the next-trait-solver.
+
+use std::hash::Hash;
+
+use intern::{Interned, Symbol};
+use rustc_ast_ir::try_visit;
+use rustc_ast_ir::visit::VisitorResult;
+use rustc_type_ir::{
+ BoundVar, FlagComputation, Flags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
+ TypeVisitable, WithCachedTypeInfo,
+ inherent::{IntoKind, PlaceholderLike},
+ relate::Relate,
+};
+
+use crate::{ConstScalar, MemoryMap, interner::InternedWrapperNoDebug};
+
+use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, Ty};
+
+pub type ConstKind<'db> = rustc_type_ir::ConstKind<DbInterner<'db>>;
+pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst<DbInterner<'db>>;
+
+#[salsa::interned(constructor = new_, debug)]
+pub struct Const<'db> {
+ #[returns(ref)]
+ kind_: InternedWrapperNoDebug<WithCachedTypeInfo<ConstKind<'db>>>,
+}
+
+impl<'db> Const<'db> {
+ pub fn new(interner: DbInterner<'db>, kind: ConstKind<'db>) -> Self {
+ let flags = FlagComputation::for_const_kind(&kind);
+ let cached = WithCachedTypeInfo {
+ internee: kind,
+ flags: flags.flags,
+ outer_exclusive_binder: flags.outer_exclusive_binder,
+ };
+ Const::new_(interner.db(), InternedWrapperNoDebug(cached))
+ }
+
+ pub fn inner(&self) -> &WithCachedTypeInfo<ConstKind<'db>> {
+ salsa::with_attached_database(|db| {
+ let inner = &self.kind_(db).0;
+ // SAFETY: The caller already has access to a `Const<'db>`, so borrowchecking will
+ // make sure that our returned value is valid for the lifetime `'db`.
+ unsafe { std::mem::transmute(inner) }
+ })
+ .unwrap()
+ }
+
+ pub fn error(interner: DbInterner<'db>) -> Self {
+ Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed))
+ }
+
+ pub fn new_param(interner: DbInterner<'db>, param: ParamConst) -> Self {
+ Const::new(interner, rustc_type_ir::ConstKind::Param(param))
+ }
+
+ pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst) -> Self {
+ Const::new(interner, ConstKind::Placeholder(placeholder))
+ }
+
+ pub fn is_ct_infer(&self) -> bool {
+ matches!(&self.inner().internee, ConstKind::Infer(_))
+ }
+
+ pub fn is_trivially_wf(self) -> bool {
+ match self.kind() {
+ ConstKind::Param(_) | ConstKind::Placeholder(_) | ConstKind::Bound(..) => true,
+ ConstKind::Infer(_)
+ | ConstKind::Unevaluated(..)
+ | ConstKind::Value(_)
+ | ConstKind::Error(_)
+ | ConstKind::Expr(_) => false,
+ }
+ }
+}
+
+impl<'db> std::fmt::Debug for InternedWrapperNoDebug<WithCachedTypeInfo<ConstKind<'db>>> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.0.internee.fmt(f)
+ }
+}
+
+pub type PlaceholderConst = Placeholder<rustc_type_ir::BoundVar>;
+
+#[derive(Copy, Clone, Hash, Eq, PartialEq)]
+pub struct ParamConst {
+ pub index: u32,
+}
+
+impl std::fmt::Debug for ParamConst {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "#{}", self.index)
+ }
+}
+
+/// A type-level constant value.
+///
+/// Represents a typed, fully evaluated constant.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct ValueConst<'db> {
+ pub(crate) ty: Ty<'db>,
+ pub(crate) value: Valtree<'db>,
+}
+
+impl<'db> ValueConst<'db> {
+ pub fn new(ty: Ty<'db>, bytes: ConstBytes) -> Self {
+ let value = Valtree::new(bytes);
+ ValueConst { ty, value }
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::ValueConst<DbInterner<'db>> for ValueConst<'db> {
+ fn ty(self) -> Ty<'db> {
+ self.ty
+ }
+
+ fn valtree(self) -> Valtree<'db> {
+ self.value
+ }
+}
+
+impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for ValueConst<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ self.ty.visit_with(visitor)
+ }
+}
+
+impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for ValueConst<'db> {
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ ValueConst { ty: self.ty.fold_with(folder), value: self.value }
+ }
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(ValueConst { ty: self.ty.try_fold_with(folder)?, value: self.value })
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ConstBytes(pub Box<[u8]>, pub MemoryMap);
+
+impl Hash for ConstBytes {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.0.hash(state)
+ }
+}
+
+#[salsa::interned(constructor = new_, debug)]
+pub struct Valtree<'db> {
+ #[returns(ref)]
+ bytes_: ConstBytes,
+}
+
+impl<'db> Valtree<'db> {
+ pub fn new(bytes: ConstBytes) -> Self {
+ salsa::with_attached_database(|db| unsafe {
+ // SAFETY: ¯\_(ツ)_/¯
+ std::mem::transmute(Valtree::new_(db, bytes))
+ })
+ .unwrap()
+ }
+
+ pub fn inner(&self) -> &ConstBytes {
+ salsa::with_attached_database(|db| {
+ let inner = self.bytes_(db);
+ // SAFETY: The caller already has access to a `Valtree<'db>`, so borrowchecking will
+ // make sure that our returned value is valid for the lifetime `'db`.
+ unsafe { std::mem::transmute(inner) }
+ })
+ .unwrap()
+ }
+}
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct ExprConst;
+
+impl rustc_type_ir::inherent::ParamLike for ParamConst {
+ fn index(self) -> u32 {
+ self.index
+ }
+}
+
+impl<'db> IntoKind for Const<'db> {
+ type Kind = ConstKind<'db>;
+
+ fn kind(self) -> Self::Kind {
+ self.inner().internee
+ }
+}
+
+impl<'db> TypeVisitable<DbInterner<'db>> for Const<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ visitor.visit_const(*self)
+ }
+}
+
+impl<'db> TypeSuperVisitable<DbInterner<'db>> for Const<'db> {
+ fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ match self.kind() {
+ ConstKind::Unevaluated(uv) => uv.visit_with(visitor),
+ ConstKind::Value(v) => v.visit_with(visitor),
+ ConstKind::Expr(e) => e.visit_with(visitor),
+ ConstKind::Error(e) => e.visit_with(visitor),
+
+ ConstKind::Param(_)
+ | ConstKind::Infer(_)
+ | ConstKind::Bound(..)
+ | ConstKind::Placeholder(_) => V::Result::output(),
+ }
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for Const<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ folder.try_fold_const(self)
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ folder.fold_const(self)
+ }
+}
+
+impl<'db> TypeSuperFoldable<DbInterner<'db>> for Const<'db> {
+ fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ let kind = match self.kind() {
+ ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?),
+ ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?),
+ ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?),
+
+ ConstKind::Param(_)
+ | ConstKind::Infer(_)
+ | ConstKind::Bound(..)
+ | ConstKind::Placeholder(_)
+ | ConstKind::Error(_) => return Ok(self),
+ };
+ if kind != self.kind() { Ok(Const::new(folder.cx(), kind)) } else { Ok(self) }
+ }
+ fn super_fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Self {
+ let kind = match self.kind() {
+ ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.fold_with(folder)),
+ ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)),
+ ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)),
+
+ ConstKind::Param(_)
+ | ConstKind::Infer(_)
+ | ConstKind::Bound(..)
+ | ConstKind::Placeholder(_)
+ | ConstKind::Error(_) => return self,
+ };
+ if kind != self.kind() { Const::new(folder.cx(), kind) } else { self }
+ }
+}
+
+impl<'db> Relate<DbInterner<'db>> for Const<'db> {
+ fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
+ relation: &mut R,
+ a: Self,
+ b: Self,
+ ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
+ relation.consts(a, b)
+ }
+}
+
+impl<'db> Flags for Const<'db> {
+ fn flags(&self) -> rustc_type_ir::TypeFlags {
+ self.inner().flags
+ }
+
+ fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
+ self.inner().outer_exclusive_binder
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::Const<DbInterner<'db>> for Const<'db> {
+ fn new_infer(interner: DbInterner<'db>, var: rustc_type_ir::InferConst) -> Self {
+ Const::new(interner, ConstKind::Infer(var))
+ }
+
+ fn new_var(interner: DbInterner<'db>, var: rustc_type_ir::ConstVid) -> Self {
+ Const::new(interner, ConstKind::Infer(rustc_type_ir::InferConst::Var(var)))
+ }
+
+ fn new_bound(
+ interner: DbInterner<'db>,
+ debruijn: rustc_type_ir::DebruijnIndex,
+ var: BoundVar,
+ ) -> Self {
+ Const::new(interner, ConstKind::Bound(debruijn, var))
+ }
+
+ fn new_anon_bound(
+ interner: DbInterner<'db>,
+ debruijn: rustc_type_ir::DebruijnIndex,
+ var: rustc_type_ir::BoundVar,
+ ) -> Self {
+ Const::new(interner, ConstKind::Bound(debruijn, var))
+ }
+
+ fn new_unevaluated(
+ interner: DbInterner<'db>,
+ uv: rustc_type_ir::UnevaluatedConst<DbInterner<'db>>,
+ ) -> Self {
+ Const::new(interner, ConstKind::Unevaluated(uv))
+ }
+
+ fn new_expr(interner: DbInterner<'db>, expr: ExprConst) -> Self {
+ Const::new(interner, ConstKind::Expr(expr))
+ }
+
+ fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self {
+ Const::new(interner, ConstKind::Error(guar))
+ }
+
+ fn new_placeholder(
+ interner: DbInterner<'db>,
+ param: <DbInterner<'db> as rustc_type_ir::Interner>::PlaceholderConst,
+ ) -> Self {
+ Const::new(interner, ConstKind::Placeholder(param))
+ }
+}
+
+impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderConst {
+ type Bound = rustc_type_ir::BoundVar;
+
+ fn universe(self) -> rustc_type_ir::UniverseIndex {
+ self.universe
+ }
+
+ fn var(self) -> rustc_type_ir::BoundVar {
+ self.bound
+ }
+
+ fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self {
+ Placeholder { universe: ui, bound: self.bound }
+ }
+
+ fn new(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self {
+ Placeholder { universe: ui, bound: var }
+ }
+ fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self {
+ Placeholder { universe: ui, bound: var }
+ }
+}
+
+impl<'db> TypeVisitable<DbInterner<'db>> for ExprConst {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ // Ensure we get back to this when we fill in the fields
+ let ExprConst = &self;
+ V::Result::output()
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for ExprConst {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(ExprConst)
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ ExprConst
+ }
+}
+
+impl<'db> Relate<DbInterner<'db>> for ExprConst {
+ fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
+ relation: &mut R,
+ a: Self,
+ b: Self,
+ ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
+ // Ensure we get back to this when we fill in the fields
+ let ExprConst = b;
+ Ok(a)
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::ExprConst<DbInterner<'db>> for ExprConst {
+ fn args(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs {
+ // Ensure we get back to this when we fill in the fields
+ let ExprConst = self;
+ GenericArgs::default()
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/def_id.rs b/crates/hir-ty/src/next_solver/def_id.rs
new file mode 100644
index 0000000..cfbc10e
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/def_id.rs
@@ -0,0 +1,96 @@
+//! Definition of `SolverDefId`
+
+use hir_def::{
+ AdtId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, StaticId, StructId,
+ TraitAliasId, TraitId, TypeAliasId, UnionId,
+};
+use rustc_type_ir::inherent;
+use stdx::impl_from;
+
+use crate::db::{InternedClosureId, InternedCoroutineId, InternedOpaqueTyId};
+
+use super::DbInterner;
+
+#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
+pub enum Ctor {
+ Struct(StructId),
+ Enum(EnumVariantId),
+}
+
+#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
+pub enum SolverDefId {
+ AdtId(AdtId),
+ ConstId(ConstId),
+ FunctionId(FunctionId),
+ ImplId(ImplId),
+ StaticId(StaticId),
+ TraitAliasId(TraitAliasId),
+ TraitId(TraitId),
+ TypeAliasId(TypeAliasId),
+ ForeignId(TypeAliasId),
+ InternedClosureId(InternedClosureId),
+ InternedCoroutineId(InternedCoroutineId),
+ InternedOpaqueTyId(InternedOpaqueTyId),
+ Ctor(Ctor),
+}
+
+impl_from!(
+ AdtId(StructId, EnumId, UnionId),
+ ConstId,
+ FunctionId,
+ ImplId,
+ StaticId,
+ TraitAliasId,
+ TraitId,
+ TypeAliasId,
+ InternedClosureId,
+ InternedCoroutineId,
+ InternedOpaqueTyId
+ for SolverDefId
+);
+
+impl From<GenericDefId> for SolverDefId {
+ fn from(value: GenericDefId) -> Self {
+ match value {
+ GenericDefId::AdtId(adt_id) => SolverDefId::AdtId(adt_id),
+ GenericDefId::ConstId(const_id) => SolverDefId::ConstId(const_id),
+ GenericDefId::FunctionId(function_id) => SolverDefId::FunctionId(function_id),
+ GenericDefId::ImplId(impl_id) => SolverDefId::ImplId(impl_id),
+ GenericDefId::StaticId(static_id) => SolverDefId::StaticId(static_id),
+ GenericDefId::TraitAliasId(trait_alias_id) => SolverDefId::TraitAliasId(trait_alias_id),
+ GenericDefId::TraitId(trait_id) => SolverDefId::TraitId(trait_id),
+ GenericDefId::TypeAliasId(type_alias_id) => SolverDefId::TypeAliasId(type_alias_id),
+ }
+ }
+}
+
+impl TryFrom<SolverDefId> for GenericDefId {
+ type Error = SolverDefId;
+
+ fn try_from(value: SolverDefId) -> Result<Self, Self::Error> {
+ Ok(match value {
+ SolverDefId::AdtId(adt_id) => GenericDefId::AdtId(adt_id),
+ SolverDefId::ConstId(const_id) => GenericDefId::ConstId(const_id),
+ SolverDefId::FunctionId(function_id) => GenericDefId::FunctionId(function_id),
+ SolverDefId::ImplId(impl_id) => GenericDefId::ImplId(impl_id),
+ SolverDefId::StaticId(static_id) => GenericDefId::StaticId(static_id),
+ SolverDefId::TraitAliasId(trait_alias_id) => GenericDefId::TraitAliasId(trait_alias_id),
+ SolverDefId::TraitId(trait_id) => GenericDefId::TraitId(trait_id),
+ SolverDefId::TypeAliasId(type_alias_id) => GenericDefId::TypeAliasId(type_alias_id),
+ SolverDefId::ForeignId(_) => return Err(value),
+ SolverDefId::InternedClosureId(_) => return Err(value),
+ SolverDefId::InternedCoroutineId(_) => return Err(value),
+ SolverDefId::InternedOpaqueTyId(_) => return Err(value),
+ SolverDefId::Ctor(_) => return Err(value),
+ })
+ }
+}
+
+impl<'db> inherent::DefId<DbInterner<'db>> for SolverDefId {
+ fn as_local(self) -> Option<SolverDefId> {
+ Some(self)
+ }
+ fn is_local(self) -> bool {
+ true
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/fold.rs b/crates/hir-ty/src/next_solver/fold.rs
new file mode 100644
index 0000000..3cc1e64
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/fold.rs
@@ -0,0 +1,129 @@
+//! Fold impls for the next-trait-solver.
+
+use rustc_type_ir::{
+ BoundVar, DebruijnIndex, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable,
+ TypeVisitableExt,
+ inherent::{IntoKind, Region as _},
+};
+
+use super::{
+ Binder, BoundRegion, BoundTy, Const, ConstKind, DbInterner, Predicate, Region, Ty, TyKind,
+};
+
+/// A delegate used when instantiating bound vars.
+///
+/// Any implementation must make sure that each bound variable always
+/// gets mapped to the same result. `BoundVarReplacer` caches by using
+/// a `DelayedMap` which does not cache the first few types it encounters.
+pub trait BoundVarReplacerDelegate<'db> {
+ fn replace_region(&mut self, br: BoundRegion) -> Region<'db>;
+ fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db>;
+ fn replace_const(&mut self, bv: BoundVar) -> Const<'db>;
+}
+
+/// A simple delegate taking 3 mutable functions. The used functions must
+/// always return the same result for each bound variable, no matter how
+/// frequently they are called.
+pub struct FnMutDelegate<'db, 'a> {
+ pub regions: &'a mut (dyn FnMut(BoundRegion) -> Region<'db> + 'a),
+ pub types: &'a mut (dyn FnMut(BoundTy) -> Ty<'db> + 'a),
+ pub consts: &'a mut (dyn FnMut(BoundVar) -> Const<'db> + 'a),
+}
+
+impl<'db, 'a> BoundVarReplacerDelegate<'db> for FnMutDelegate<'db, 'a> {
+ fn replace_region(&mut self, br: BoundRegion) -> Region<'db> {
+ (self.regions)(br)
+ }
+ fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> {
+ (self.types)(bt)
+ }
+ fn replace_const(&mut self, bv: BoundVar) -> Const<'db> {
+ (self.consts)(bv)
+ }
+}
+
+/// Replaces the escaping bound vars (late bound regions or bound types) in a type.
+pub(crate) struct BoundVarReplacer<'db, D> {
+ interner: DbInterner<'db>,
+ /// As with `RegionFolder`, represents the index of a binder *just outside*
+ /// the ones we have visited.
+ current_index: DebruijnIndex,
+
+ delegate: D,
+}
+
+impl<'db, D: BoundVarReplacerDelegate<'db>> BoundVarReplacer<'db, D> {
+ pub fn new(tcx: DbInterner<'db>, delegate: D) -> Self {
+ BoundVarReplacer { interner: tcx, current_index: DebruijnIndex::ZERO, delegate }
+ }
+}
+
+impl<'db, D> TypeFolder<DbInterner<'db>> for BoundVarReplacer<'db, D>
+where
+ D: BoundVarReplacerDelegate<'db>,
+{
+ fn cx(&self) -> DbInterner<'db> {
+ self.interner
+ }
+
+ fn fold_binder<T: TypeFoldable<DbInterner<'db>>>(
+ &mut self,
+ t: Binder<'db, T>,
+ ) -> Binder<'db, T> {
+ self.current_index.shift_in(1);
+ let t = t.super_fold_with(self);
+ self.current_index.shift_out(1);
+ t
+ }
+
+ fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
+ match t.kind() {
+ TyKind::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
+ let ty = self.delegate.replace_ty(bound_ty);
+ debug_assert!(!ty.has_vars_bound_above(DebruijnIndex::ZERO));
+ rustc_type_ir::shift_vars(self.interner, ty, self.current_index.as_u32())
+ }
+ _ => {
+ if !t.has_vars_bound_at_or_above(self.current_index) {
+ t
+ } else {
+ t.super_fold_with(self)
+ }
+ }
+ }
+ }
+
+ fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
+ match r.kind() {
+ RegionKind::ReBound(debruijn, br) if debruijn == self.current_index => {
+ let region = self.delegate.replace_region(br);
+ if let RegionKind::ReBound(debruijn1, br) = region.kind() {
+ // If the callback returns a bound region,
+ // that region should always use the INNERMOST
+ // debruijn index. Then we adjust it to the
+ // correct depth.
+ assert_eq!(debruijn1, DebruijnIndex::ZERO);
+ Region::new_bound(self.interner, debruijn, br)
+ } else {
+ region
+ }
+ }
+ _ => r,
+ }
+ }
+
+ fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
+ match ct.kind() {
+ ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
+ let ct = self.delegate.replace_const(bound_const);
+ debug_assert!(!ct.has_vars_bound_above(DebruijnIndex::ZERO));
+ rustc_type_ir::shift_vars(self.interner, ct, self.current_index.as_u32())
+ }
+ _ => ct.super_fold_with(self),
+ }
+ }
+
+ fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> {
+ if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/fulfill.rs b/crates/hir-ty/src/next_solver/fulfill.rs
new file mode 100644
index 0000000..007a674
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/fulfill.rs
@@ -0,0 +1,229 @@
+//! Fulfill loop for next-solver.
+
+use std::marker::PhantomData;
+use std::mem;
+use std::ops::ControlFlow;
+use std::vec::ExtractIf;
+
+use rustc_next_trait_solver::delegate::SolverDelegate;
+use rustc_next_trait_solver::solve::{
+ GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt,
+};
+use rustc_type_ir::Interner;
+use rustc_type_ir::inherent::Span as _;
+use rustc_type_ir::solve::{Certainty, NoSolution};
+
+use crate::next_solver::infer::InferCtxt;
+use crate::next_solver::infer::traits::{PredicateObligation, PredicateObligations};
+use crate::next_solver::{DbInterner, SolverContext, Span, TypingMode};
+
+type PendingObligations<'db> =
+ Vec<(PredicateObligation<'db>, Option<GoalStalledOn<DbInterner<'db>>>)>;
+
+/// A trait engine using the new trait solver.
+///
+/// This is mostly identical to how `evaluate_all` works inside of the
+/// solver, except that the requirements are slightly different.
+///
+/// Unlike `evaluate_all` it is possible to add new obligations later on
+/// and we also have to track diagnostics information by using `Obligation`
+/// instead of `Goal`.
+///
+/// It is also likely that we want to use slightly different datastructures
+/// here as this will have to deal with far more root goals than `evaluate_all`.
+pub struct FulfillmentCtxt<'db> {
+ obligations: ObligationStorage<'db>,
+
+ /// The snapshot in which this context was created. Using the context
+ /// outside of this snapshot leads to subtle bugs if the snapshot
+ /// gets rolled back. Because of this we explicitly check that we only
+ /// use the context in exactly this snapshot.
+ usable_in_snapshot: usize,
+}
+
+#[derive(Default, Debug)]
+struct ObligationStorage<'db> {
+ /// Obligations which resulted in an overflow in fulfillment itself.
+ ///
+ /// We cannot eagerly return these as error so we instead store them here
+ /// to avoid recomputing them each time `select_where_possible` is called.
+ /// This also allows us to return the correct `FulfillmentError` for them.
+ overflowed: Vec<PredicateObligation<'db>>,
+ pending: PendingObligations<'db>,
+}
+
+impl<'db> ObligationStorage<'db> {
+ fn register(
+ &mut self,
+ obligation: PredicateObligation<'db>,
+ stalled_on: Option<GoalStalledOn<DbInterner<'db>>>,
+ ) {
+ self.pending.push((obligation, stalled_on));
+ }
+
+ fn has_pending_obligations(&self) -> bool {
+ !self.pending.is_empty() || !self.overflowed.is_empty()
+ }
+
+ fn clone_pending(&self) -> PredicateObligations<'db> {
+ let mut obligations: PredicateObligations<'db> =
+ self.pending.iter().map(|(o, _)| o.clone()).collect();
+ obligations.extend(self.overflowed.iter().cloned());
+ obligations
+ }
+
+ fn drain_pending(
+ &mut self,
+ cond: impl Fn(&PredicateObligation<'db>) -> bool,
+ ) -> PendingObligations<'db> {
+ let (not_stalled, pending) =
+ mem::take(&mut self.pending).into_iter().partition(|(o, _)| cond(o));
+ self.pending = pending;
+ not_stalled
+ }
+
+ fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'db>) {
+ infcx.probe(|_| {
+ // IMPORTANT: we must not use solve any inference variables in the obligations
+ // as this is all happening inside of a probe. We use a probe to make sure
+ // we get all obligations involved in the overflow. We pretty much check: if
+ // we were to do another step of `select_where_possible`, which goals would
+ // change.
+ // FIXME: <https://github.com/Gankra/thin-vec/pull/66> is merged, this can be removed.
+ self.overflowed.extend(
+ self.pending
+ .extract_if(.., |(o, stalled_on)| {
+ let goal = o.as_goal();
+ let result = <&SolverContext<'db>>::from(infcx).evaluate_root_goal(
+ goal,
+ Span::dummy(),
+ stalled_on.take(),
+ );
+ matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))
+ })
+ .map(|(o, _)| o),
+ );
+ })
+ }
+}
+
+impl<'db> FulfillmentCtxt<'db> {
+ pub fn new(infcx: &InferCtxt<'db>) -> FulfillmentCtxt<'db> {
+ FulfillmentCtxt {
+ obligations: Default::default(),
+ usable_in_snapshot: infcx.num_open_snapshots(),
+ }
+ }
+}
+
+impl<'db> FulfillmentCtxt<'db> {
+ #[tracing::instrument(level = "trace", skip(self, infcx))]
+ pub(crate) fn register_predicate_obligation(
+ &mut self,
+ infcx: &InferCtxt<'db>,
+ obligation: PredicateObligation<'db>,
+ ) {
+ assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
+ self.obligations.register(obligation, None);
+ }
+
+ pub(crate) fn collect_remaining_errors(
+ &mut self,
+ infcx: &InferCtxt<'db>,
+ ) -> Vec<NextSolverError<'db>> {
+ self.obligations
+ .pending
+ .drain(..)
+ .map(|(obligation, _)| NextSolverError::Ambiguity(obligation))
+ .chain(self.obligations.overflowed.drain(..).map(NextSolverError::Overflow))
+ .collect()
+ }
+
+ pub(crate) fn select_where_possible(
+ &mut self,
+ infcx: &InferCtxt<'db>,
+ ) -> Vec<NextSolverError<'db>> {
+ assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
+ let mut errors = Vec::new();
+ loop {
+ let mut any_changed = false;
+ for (mut obligation, stalled_on) in self.obligations.drain_pending(|_| true) {
+ if obligation.recursion_depth >= infcx.interner.recursion_limit() {
+ self.obligations.on_fulfillment_overflow(infcx);
+ // Only return true errors that we have accumulated while processing.
+ return errors;
+ }
+
+ let goal = obligation.as_goal();
+ let delegate = <&SolverContext<'db>>::from(infcx);
+ if let Some(certainty) = delegate.compute_goal_fast_path(goal, Span::dummy()) {
+ match certainty {
+ Certainty::Yes => {}
+ Certainty::Maybe(_) => {
+ self.obligations.register(obligation, None);
+ }
+ }
+ continue;
+ }
+
+ let result = delegate.evaluate_root_goal(goal, Span::dummy(), stalled_on);
+ let GoalEvaluation { certainty, has_changed, stalled_on } = match result {
+ Ok(result) => result,
+ Err(NoSolution) => {
+ errors.push(NextSolverError::TrueError(obligation));
+ continue;
+ }
+ };
+
+ if has_changed == HasChanged::Yes {
+ // We increment the recursion depth here to track the number of times
+ // this goal has resulted in inference progress. This doesn't precisely
+ // model the way that we track recursion depth in the old solver due
+ // to the fact that we only process root obligations, but it is a good
+ // approximation and should only result in fulfillment overflow in
+ // pathological cases.
+ obligation.recursion_depth += 1;
+ any_changed = true;
+ }
+
+ match certainty {
+ Certainty::Yes => {}
+ Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on),
+ }
+ }
+
+ if !any_changed {
+ break;
+ }
+ }
+
+ errors
+ }
+
+ pub(crate) fn select_all_or_error(
+ &mut self,
+ infcx: &InferCtxt<'db>,
+ ) -> Vec<NextSolverError<'db>> {
+ let errors = self.select_where_possible(infcx);
+ if !errors.is_empty() {
+ return errors;
+ }
+
+ self.collect_remaining_errors(infcx)
+ }
+
+ fn has_pending_obligations(&self) -> bool {
+ self.obligations.has_pending_obligations()
+ }
+
+ fn pending_obligations(&self) -> PredicateObligations<'db> {
+ self.obligations.clone_pending()
+ }
+}
+
+#[derive(Debug)]
+pub enum NextSolverError<'db> {
+ TrueError(PredicateObligation<'db>),
+ Ambiguity(PredicateObligation<'db>),
+ Overflow(PredicateObligation<'db>),
+}
diff --git a/crates/hir-ty/src/next_solver/generic_arg.rs b/crates/hir-ty/src/next_solver/generic_arg.rs
new file mode 100644
index 0000000..85a7992
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/generic_arg.rs
@@ -0,0 +1,525 @@
+//! Things related to generic args in the next-trait-solver.
+
+use intern::{Interned, Symbol};
+use rustc_type_ir::{
+ ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSig, FnSigTys,
+ GenericArgKind, IntTy, Interner, TermKind, TyKind, TyVid, TypeFoldable, TypeVisitable,
+ Variance,
+ inherent::{GenericArg as _, GenericsOf, IntoKind, SliceLike, Term as _, Ty as _},
+ relate::{Relate, VarianceDiagInfo},
+};
+use smallvec::SmallVec;
+
+use crate::db::HirDatabase;
+
+use super::{
+ Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty, Tys,
+ generics::{GenericParamDef, GenericParamDefKind, Generics},
+ interned_vec_db,
+};
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub enum GenericArg<'db> {
+ Ty(Ty<'db>),
+ Lifetime(Region<'db>),
+ Const(Const<'db>),
+}
+
+impl<'db> std::fmt::Debug for GenericArg<'db> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Ty(t) => std::fmt::Debug::fmt(t, f),
+ Self::Lifetime(r) => std::fmt::Debug::fmt(r, f),
+ Self::Const(c) => std::fmt::Debug::fmt(c, f),
+ }
+ }
+}
+
+impl<'db> From<Term<'db>> for GenericArg<'db> {
+ fn from(value: Term<'db>) -> Self {
+ match value {
+ Term::Ty(ty) => GenericArg::Ty(ty),
+ Term::Const(c) => GenericArg::Const(c),
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub enum Term<'db> {
+ Ty(Ty<'db>),
+ Const(Const<'db>),
+}
+
+impl<'db> std::fmt::Debug for Term<'db> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Ty(t) => std::fmt::Debug::fmt(t, f),
+ Self::Const(c) => std::fmt::Debug::fmt(c, f),
+ }
+ }
+}
+
+impl<'db> Term<'db> {
+ pub fn expect_type(&self) -> Ty<'db> {
+ self.as_type().expect("expected a type, but found a const")
+ }
+
+ pub fn is_trivially_wf(&self, tcx: DbInterner<'db>) -> bool {
+ match self.kind() {
+ TermKind::Ty(ty) => ty.is_trivially_wf(tcx),
+ TermKind::Const(ct) => ct.is_trivially_wf(),
+ }
+ }
+}
+
+impl<'db> From<Ty<'db>> for GenericArg<'db> {
+ fn from(value: Ty<'db>) -> Self {
+ Self::Ty(value)
+ }
+}
+
+impl<'db> From<Region<'db>> for GenericArg<'db> {
+ fn from(value: Region<'db>) -> Self {
+ Self::Lifetime(value)
+ }
+}
+
+impl<'db> From<Const<'db>> for GenericArg<'db> {
+ fn from(value: Const<'db>) -> Self {
+ Self::Const(value)
+ }
+}
+
+impl<'db> IntoKind for GenericArg<'db> {
+ type Kind = GenericArgKind<DbInterner<'db>>;
+
+ fn kind(self) -> Self::Kind {
+ match self {
+ GenericArg::Ty(ty) => GenericArgKind::Type(ty),
+ GenericArg::Lifetime(region) => GenericArgKind::Lifetime(region),
+ GenericArg::Const(c) => GenericArgKind::Const(c),
+ }
+ }
+}
+
+impl<'db> TypeVisitable<DbInterner<'db>> for GenericArg<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ match self {
+ GenericArg::Lifetime(lt) => lt.visit_with(visitor),
+ GenericArg::Ty(ty) => ty.visit_with(visitor),
+ GenericArg::Const(ct) => ct.visit_with(visitor),
+ }
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for GenericArg<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ match self.kind() {
+ GenericArgKind::Lifetime(lt) => lt.try_fold_with(folder).map(Into::into),
+ GenericArgKind::Type(ty) => ty.try_fold_with(folder).map(Into::into),
+ GenericArgKind::Const(ct) => ct.try_fold_with(folder).map(Into::into),
+ }
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ match self.kind() {
+ GenericArgKind::Lifetime(lt) => lt.fold_with(folder).into(),
+ GenericArgKind::Type(ty) => ty.fold_with(folder).into(),
+ GenericArgKind::Const(ct) => ct.fold_with(folder).into(),
+ }
+ }
+}
+
+impl<'db> Relate<DbInterner<'db>> for GenericArg<'db> {
+ fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
+ relation: &mut R,
+ a: Self,
+ b: Self,
+ ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
+ match (a.kind(), b.kind()) {
+ (GenericArgKind::Lifetime(a_lt), GenericArgKind::Lifetime(b_lt)) => {
+ Ok(relation.relate(a_lt, b_lt)?.into())
+ }
+ (GenericArgKind::Type(a_ty), GenericArgKind::Type(b_ty)) => {
+ Ok(relation.relate(a_ty, b_ty)?.into())
+ }
+ (GenericArgKind::Const(a_ct), GenericArgKind::Const(b_ct)) => {
+ Ok(relation.relate(a_ct, b_ct)?.into())
+ }
+ (GenericArgKind::Lifetime(unpacked), x) => {
+ unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x)
+ }
+ (GenericArgKind::Type(unpacked), x) => {
+ unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x)
+ }
+ (GenericArgKind::Const(unpacked), x) => {
+ unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x)
+ }
+ }
+ }
+}
+
+interned_vec_db!(GenericArgs, GenericArg);
+
+impl<'db> rustc_type_ir::inherent::GenericArg<DbInterner<'db>> for GenericArg<'db> {}
+
+impl<'db> GenericArgs<'db> {
+ /// Creates an `GenericArgs` for generic parameter definitions,
+ /// by calling closures to obtain each kind.
+ /// The closures get to observe the `GenericArgs` as they're
+ /// being built, which can be used to correctly
+ /// replace defaults of generic parameters.
+ pub fn for_item<F>(
+ interner: DbInterner<'db>,
+ def_id: SolverDefId,
+ mut mk_kind: F,
+ ) -> GenericArgs<'db>
+ where
+ F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>,
+ {
+ let defs = interner.generics_of(def_id);
+ let count = defs.count();
+ let mut args = SmallVec::with_capacity(count);
+ Self::fill_item(&mut args, interner, defs, &mut mk_kind);
+ interner.mk_args(&args)
+ }
+
+ fn fill_item<F>(
+ args: &mut SmallVec<[GenericArg<'db>; 8]>,
+ interner: DbInterner<'_>,
+ defs: Generics,
+ mk_kind: &mut F,
+ ) where
+ F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>,
+ {
+ let self_len = defs.own_params.len() as u32;
+ if let Some(def_id) = defs.parent {
+ let parent_defs = interner.generics_of(def_id.into());
+ Self::fill_item(args, interner, parent_defs, mk_kind);
+ }
+ Self::fill_single(args, &defs, mk_kind);
+ }
+
+ fn fill_single<F>(args: &mut SmallVec<[GenericArg<'db>; 8]>, defs: &Generics, mk_kind: &mut F)
+ where
+ F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>,
+ {
+ let start_len = args.len();
+ args.reserve(defs.own_params.len());
+ for param in &defs.own_params {
+ let kind = mk_kind(¶m.name, args.len() as u32, param.kind, args);
+ args.push(kind);
+ }
+ }
+}
+
+impl<'db> rustc_type_ir::relate::Relate<DbInterner<'db>> for GenericArgs<'db> {
+ fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
+ relation: &mut R,
+ a: Self,
+ b: Self,
+ ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
+ let interner = relation.cx();
+ CollectAndApply::collect_and_apply(
+ std::iter::zip(a.iter(), b.iter()).map(|(a, b)| {
+ relation.relate_with_variance(
+ Variance::Invariant,
+ VarianceDiagInfo::default(),
+ a,
+ b,
+ )
+ }),
+ |g| GenericArgs::new_from_iter(interner, g.iter().cloned()),
+ )
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::GenericArgs<DbInterner<'db>> for GenericArgs<'db> {
+ fn as_closure(self) -> ClosureArgs<DbInterner<'db>> {
+ ClosureArgs { args: self }
+ }
+ fn as_coroutine(self) -> CoroutineArgs<DbInterner<'db>> {
+ CoroutineArgs { args: self }
+ }
+ fn as_coroutine_closure(self) -> CoroutineClosureArgs<DbInterner<'db>> {
+ CoroutineClosureArgs { args: self }
+ }
+ fn rebase_onto(
+ self,
+ interner: DbInterner<'db>,
+ source_def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId,
+ target: <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs,
+ ) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs {
+ let defs = interner.generics_of(source_def_id);
+ interner.mk_args_from_iter(target.iter().chain(self.iter().skip(defs.count())))
+ }
+
+ fn identity_for_item(
+ interner: DbInterner<'db>,
+ def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId,
+ ) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs {
+ Self::for_item(interner, def_id, |name, index, kind, _| mk_param(index, name, kind))
+ }
+
+ fn extend_with_error(
+ interner: DbInterner<'db>,
+ def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId,
+ original_args: &[<DbInterner<'db> as rustc_type_ir::Interner>::GenericArg],
+ ) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs {
+ Self::for_item(interner, def_id, |name, index, kind, _| {
+ if let Some(arg) = original_args.get(index as usize) {
+ *arg
+ } else {
+ error_for_param_kind(kind, interner)
+ }
+ })
+ }
+ fn type_at(self, i: usize) -> <DbInterner<'db> as rustc_type_ir::Interner>::Ty {
+ self.inner()
+ .get(i)
+ .and_then(|g| g.as_type())
+ .unwrap_or_else(|| Ty::new_error(DbInterner::conjure(), ErrorGuaranteed))
+ }
+
+ fn region_at(self, i: usize) -> <DbInterner<'db> as rustc_type_ir::Interner>::Region {
+ self.inner()
+ .get(i)
+ .and_then(|g| g.as_region())
+ .unwrap_or_else(|| Region::error(DbInterner::conjure()))
+ }
+
+ fn const_at(self, i: usize) -> <DbInterner<'db> as rustc_type_ir::Interner>::Const {
+ self.inner()
+ .get(i)
+ .and_then(|g| g.as_const())
+ .unwrap_or_else(|| Const::error(DbInterner::conjure()))
+ }
+
+ fn split_closure_args(self) -> rustc_type_ir::ClosureArgsParts<DbInterner<'db>> {
+ // FIXME: should use `ClosureSubst` when possible
+ match self.inner().as_slice() {
+ [parent_args @ .., sig_ty] => {
+ let interner = DbInterner::conjure();
+ // This is stupid, but the next solver expects the first input to actually be a tuple
+ let sig_ty = match sig_ty.expect_ty().kind() {
+ TyKind::FnPtr(sig_tys, header) => Ty::new(
+ interner,
+ TyKind::FnPtr(
+ sig_tys.map_bound(|s| {
+ let inputs = Ty::new_tup_from_iter(interner, s.inputs().iter());
+ let output = s.output();
+ FnSigTys {
+ inputs_and_output: Tys::new_from_iter(
+ interner,
+ [inputs, output],
+ ),
+ }
+ }),
+ header,
+ ),
+ ),
+ _ => unreachable!("sig_ty should be last"),
+ };
+ rustc_type_ir::ClosureArgsParts {
+ parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()),
+ closure_sig_as_fn_ptr_ty: sig_ty,
+ closure_kind_ty: Ty::new(interner, TyKind::Int(IntTy::I8)),
+ tupled_upvars_ty: Ty::new_unit(interner),
+ }
+ }
+ _ => {
+ unreachable!("unexpected closure sig");
+ }
+ }
+ }
+
+ fn split_coroutine_closure_args(
+ self,
+ ) -> rustc_type_ir::CoroutineClosureArgsParts<DbInterner<'db>> {
+ match self.inner().as_slice() {
+ [
+ parent_args @ ..,
+ closure_kind_ty,
+ signature_parts_ty,
+ tupled_upvars_ty,
+ coroutine_captures_by_ref_ty,
+ coroutine_witness_ty,
+ ] => rustc_type_ir::CoroutineClosureArgsParts {
+ parent_args: GenericArgs::new_from_iter(
+ DbInterner::conjure(),
+ parent_args.iter().cloned(),
+ ),
+ closure_kind_ty: closure_kind_ty.expect_ty(),
+ signature_parts_ty: signature_parts_ty.expect_ty(),
+ tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
+ coroutine_captures_by_ref_ty: coroutine_captures_by_ref_ty.expect_ty(),
+ coroutine_witness_ty: coroutine_witness_ty.expect_ty(),
+ },
+ _ => panic!("GenericArgs were likely not for a CoroutineClosure."),
+ }
+ }
+
+ fn split_coroutine_args(self) -> rustc_type_ir::CoroutineArgsParts<DbInterner<'db>> {
+ let interner = DbInterner::conjure();
+ match self.inner().as_slice() {
+ [parent_args @ .., resume_ty, yield_ty, return_ty] => {
+ rustc_type_ir::CoroutineArgsParts {
+ parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()),
+ kind_ty: Ty::new_unit(interner),
+ resume_ty: resume_ty.expect_ty(),
+ yield_ty: yield_ty.expect_ty(),
+ return_ty: return_ty.expect_ty(),
+ witness: Ty::new_unit(interner),
+ tupled_upvars_ty: Ty::new_unit(interner),
+ }
+ }
+ _ => panic!("GenericArgs were likely not for a Coroutine."),
+ }
+ }
+}
+
+pub fn mk_param<'db>(index: u32, name: &Symbol, kind: GenericParamDefKind) -> GenericArg<'db> {
+ let name = name.clone();
+ match kind {
+ GenericParamDefKind::Lifetime => {
+ Region::new_early_param(DbInterner::conjure(), EarlyParamRegion { index }).into()
+ }
+ GenericParamDefKind::Type => Ty::new_param(DbInterner::conjure(), index, name).into(),
+ GenericParamDefKind::Const => {
+ Const::new_param(DbInterner::conjure(), ParamConst { index }).into()
+ }
+ }
+}
+
+pub fn error_for_param_kind<'db>(
+ kind: GenericParamDefKind,
+ interner: DbInterner<'db>,
+) -> GenericArg<'db> {
+ match kind {
+ GenericParamDefKind::Lifetime => Region::error(interner).into(),
+ GenericParamDefKind::Type => Ty::new_error(interner, ErrorGuaranteed).into(),
+ GenericParamDefKind::Const => Const::error(interner).into(),
+ }
+}
+
+impl<'db> IntoKind for Term<'db> {
+ type Kind = TermKind<DbInterner<'db>>;
+
+ fn kind(self) -> Self::Kind {
+ match self {
+ Term::Ty(ty) => TermKind::Ty(ty),
+ Term::Const(c) => TermKind::Const(c),
+ }
+ }
+}
+
+impl<'db> From<Ty<'db>> for Term<'db> {
+ fn from(value: Ty<'db>) -> Self {
+ Self::Ty(value)
+ }
+}
+
+impl<'db> From<Const<'db>> for Term<'db> {
+ fn from(value: Const<'db>) -> Self {
+ Self::Const(value)
+ }
+}
+
+impl<'db> TypeVisitable<DbInterner<'db>> for Term<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ match self {
+ Term::Ty(ty) => ty.visit_with(visitor),
+ Term::Const(ct) => ct.visit_with(visitor),
+ }
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for Term<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ match self.kind() {
+ TermKind::Ty(ty) => ty.try_fold_with(folder).map(Into::into),
+ TermKind::Const(ct) => ct.try_fold_with(folder).map(Into::into),
+ }
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ match self.kind() {
+ TermKind::Ty(ty) => ty.fold_with(folder).into(),
+ TermKind::Const(ct) => ct.fold_with(folder).into(),
+ }
+ }
+}
+
+impl<'db> Relate<DbInterner<'db>> for Term<'db> {
+ fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
+ relation: &mut R,
+ a: Self,
+ b: Self,
+ ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
+ match (a.kind(), b.kind()) {
+ (TermKind::Ty(a_ty), TermKind::Ty(b_ty)) => Ok(relation.relate(a_ty, b_ty)?.into()),
+ (TermKind::Const(a_ct), TermKind::Const(b_ct)) => {
+ Ok(relation.relate(a_ct, b_ct)?.into())
+ }
+ (TermKind::Ty(unpacked), x) => {
+ unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x)
+ }
+ (TermKind::Const(unpacked), x) => {
+ unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x)
+ }
+ }
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::Term<DbInterner<'db>> for Term<'db> {}
+
+#[derive(Clone, Eq, PartialEq, Debug)]
+pub enum TermVid {
+ Ty(TyVid),
+ Const(ConstVid),
+}
+
+impl From<TyVid> for TermVid {
+ fn from(value: TyVid) -> Self {
+ TermVid::Ty(value)
+ }
+}
+
+impl From<ConstVid> for TermVid {
+ fn from(value: ConstVid) -> Self {
+ TermVid::Const(value)
+ }
+}
+
+impl<'db> DbInterner<'db> {
+ pub(super) fn mk_args(self, args: &[GenericArg<'db>]) -> GenericArgs<'db> {
+ GenericArgs::new_from_iter(self, args.iter().cloned())
+ }
+
+ pub(super) fn mk_args_from_iter<I, T>(self, iter: I) -> T::Output
+ where
+ I: Iterator<Item = T>,
+ T: rustc_type_ir::CollectAndApply<GenericArg<'db>, GenericArgs<'db>>,
+ {
+ T::collect_and_apply(iter, |xs| self.mk_args(xs))
+ }
+
+ pub(super) fn check_args_compatible(self, def_id: SolverDefId, args: GenericArgs<'db>) -> bool {
+ // TODO
+ true
+ }
+
+ pub(super) fn debug_assert_args_compatible(self, def_id: SolverDefId, args: GenericArgs<'db>) {
+ // TODO
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/generics.rs b/crates/hir-ty/src/next_solver/generics.rs
new file mode 100644
index 0000000..48f5e73
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/generics.rs
@@ -0,0 +1,147 @@
+//! Things related to generics in the next-trait-solver.
+
+use hir_def::{
+ ConstParamId, GenericDefId, GenericParamId, ItemContainerId, LifetimeParamId, Lookup,
+ TypeOrConstParamId, TypeParamId,
+ db::DefDatabase,
+ expr_store::ExpressionStore,
+ hir::generics::{
+ GenericParamDataRef, GenericParams, LifetimeParamData, LocalLifetimeParamId,
+ LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamData, TypeParamProvenance,
+ WherePredicate,
+ },
+};
+use hir_expand::name::Name;
+use intern::Symbol;
+use la_arena::Arena;
+use rustc_type_ir::inherent::Ty as _;
+use triomphe::Arc;
+
+use crate::{db::HirDatabase, generics::parent_generic_def, next_solver::Ty};
+
+use super::{Const, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId};
+
+use super::{DbInterner, GenericArg};
+
+pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics {
+ let mk_lt = |(index, (_, lt)): (usize, (_, &LifetimeParamData))| {
+ let name = lt.name.symbol().clone();
+ let index = index as u32;
+ let kind = GenericParamDefKind::Lifetime;
+ GenericParamDef { name, index, kind }
+ };
+ let mk_ty = |len_lt, (index, p): (usize, &TypeOrConstParamData)| {
+ let name = p
+ .name()
+ .map(|n| n.symbol().clone())
+ .unwrap_or_else(|| Name::missing().symbol().clone());
+ let index = (len_lt + index) as u32;
+ let kind = match p {
+ TypeOrConstParamData::TypeParamData(_) => GenericParamDefKind::Type,
+ TypeOrConstParamData::ConstParamData(_) => GenericParamDefKind::Const,
+ };
+ GenericParamDef { name, index, kind }
+ };
+ let own_params_for_generic_params = |params: &GenericParams| {
+ if params.trait_self_param().is_some() {
+ let len_lt = params.len_lifetimes() + 1;
+ params
+ .iter_type_or_consts()
+ .take(1)
+ .enumerate()
+ .map(|t| mk_ty(0, (t.0, t.1.1)))
+ .chain(params.iter_lt().enumerate().map(mk_lt))
+ .chain(
+ params
+ .iter_type_or_consts()
+ .skip(1)
+ .enumerate()
+ .map(|t| mk_ty(len_lt, (t.0, t.1.1))),
+ )
+ .collect()
+ } else {
+ let len_lt = params.len_lifetimes();
+ params
+ .iter_lt()
+ .enumerate()
+ .map(mk_lt)
+ .chain(
+ params.iter_type_or_consts().enumerate().map(|t| mk_ty(len_lt, (t.0, t.1.1))),
+ )
+ .collect()
+ }
+ };
+
+ let (parent, own_params) = match (def.try_into(), def) {
+ (Ok(def), _) => {
+ (parent_generic_def(db, def), own_params_for_generic_params(&db.generic_params(def)))
+ }
+ (_, SolverDefId::InternedOpaqueTyId(id)) => {
+ match db.lookup_intern_impl_trait_id(id) {
+ crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => {
+ // The opaque type itself does not have generics - only the parent function
+ (Some(GenericDefId::FunctionId(function_id)), vec![])
+ }
+ crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => (
+ None,
+ own_params_for_generic_params(
+ &db.generic_params(GenericDefId::TypeAliasId(type_alias_id)),
+ ),
+ ),
+ crate::ImplTraitId::AsyncBlockTypeImplTrait(def, _) => {
+ let param = TypeOrConstParamData::TypeParamData(TypeParamData {
+ name: None,
+ default: None,
+ provenance: TypeParamProvenance::TypeParamList,
+ });
+ // Yes, there is a parent but we don't include it in the generics
+ (None, vec![mk_ty(0, (0, ¶m))])
+ }
+ }
+ }
+ _ => panic!("No generics for {def:?}"),
+ };
+ let parent_generics = parent.map(|def| Box::new(generics(db, def.into())));
+
+ Generics {
+ parent,
+ parent_count: parent_generics.map_or(0, |g| g.parent_count + g.own_params.len()),
+ own_params,
+ }
+}
+
+#[derive(Debug)]
+pub struct Generics {
+ pub parent: Option<GenericDefId>,
+ pub parent_count: usize,
+ pub own_params: Vec<GenericParamDef>,
+}
+
+#[derive(Debug)]
+pub struct GenericParamDef {
+ pub(crate) name: Symbol,
+ //def_id: GenericDefId,
+ index: u32,
+ pub(crate) kind: GenericParamDefKind,
+}
+
+impl GenericParamDef {
+ /// Returns the index of the param on the self generics only
+ /// (i.e. not including parent generics)
+ pub fn index(&self) -> u32 {
+ self.index
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum GenericParamDefKind {
+ Lifetime,
+ Type,
+ Const,
+}
+
+impl<'db> rustc_type_ir::inherent::GenericsOf<DbInterner<'db>> for Generics {
+ fn count(&self) -> usize {
+ self.parent_count + self.own_params.len()
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/at.rs b/crates/hir-ty/src/next_solver/infer/at.rs
new file mode 100644
index 0000000..d64c7ed
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/at.rs
@@ -0,0 +1,333 @@
+//! A nice interface for working with the infcx. The basic idea is to
+//! do `infcx.at(cause, param_env)`, which sets the "cause" of the
+//! operation as well as the surrounding parameter environment. Then
+//! you can do something like `.sub(a, b)` or `.eq(a, b)` to create a
+//! subtype or equality relationship respectively. The first argument
+//! is always the "expected" output from the POV of diagnostics.
+//!
+//! Examples:
+//! ```ignore (fragment)
+//! infcx.at(cause, param_env).sub(a, b)
+//! // requires that `a <: b`, with `a` considered the "expected" type
+//!
+//! infcx.at(cause, param_env).sup(a, b)
+//! // requires that `b <: a`, with `a` considered the "expected" type
+//!
+//! infcx.at(cause, param_env).eq(a, b)
+//! // requires that `a == b`, with `a` considered the "expected" type
+//! ```
+//! For finer-grained control, you can also do use `trace`:
+//! ```ignore (fragment)
+//! infcx.at(...).trace(a, b).sub(&c, &d)
+//! ```
+//! This will set `a` and `b` as the "root" values for
+//! error-reporting, but actually operate on `c` and `d`. This is
+//! sometimes useful when the types of `c` and `d` are not traceable
+//! things. (That system should probably be refactored.)
+
+use rustc_type_ir::{
+ FnSig, GenericArgKind, TypingMode, Variance,
+ error::ExpectedFound,
+ inherent::{IntoKind, Span as _},
+ relate::{Relate, TypeRelation, solver_relating::RelateExt},
+};
+
+use crate::next_solver::{
+ AliasTerm, AliasTy, Binder, Const, DbInterner, GenericArg, Goal, ParamEnv,
+ PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Span, Term,
+ TraitRef, Ty,
+};
+
+use super::{
+ InferCtxt, InferOk, InferResult, TypeTrace, ValuePairs,
+ traits::{Obligation, ObligationCause},
+};
+
+/// Whether we should define opaque types or just treat them opaquely.
+///
+/// Currently only used to prevent predicate matching from matching anything
+/// against opaque types.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum DefineOpaqueTypes {
+ Yes,
+ No,
+}
+
+#[derive(Clone, Copy)]
+pub struct At<'a, 'db> {
+ pub infcx: &'a InferCtxt<'db>,
+ pub cause: &'a ObligationCause,
+ pub param_env: ParamEnv<'db>,
+}
+
+impl<'db> InferCtxt<'db> {
+ #[inline]
+ pub fn at<'a>(&'a self, cause: &'a ObligationCause, param_env: ParamEnv<'db>) -> At<'a, 'db> {
+ At { infcx: self, cause, param_env }
+ }
+
+ /// Forks the inference context, creating a new inference context with the same inference
+ /// variables in the same state. This can be used to "branch off" many tests from the same
+ /// common state.
+ pub fn fork(&self) -> Self {
+ Self {
+ interner: self.interner,
+ typing_mode: self.typing_mode,
+ inner: self.inner.clone(),
+ tainted_by_errors: self.tainted_by_errors.clone(),
+ universe: self.universe.clone(),
+ }
+ }
+
+ /// Forks the inference context, creating a new inference context with the same inference
+ /// variables in the same state, except possibly changing the intercrate mode. This can be
+ /// used to "branch off" many tests from the same common state. Used in negative coherence.
+ pub fn fork_with_typing_mode(&self, typing_mode: TypingMode<DbInterner<'db>>) -> Self {
+ // Unlike `fork`, this invalidates all cache entries as they may depend on the
+ // typing mode.
+
+ Self {
+ interner: self.interner,
+ typing_mode,
+ inner: self.inner.clone(),
+ tainted_by_errors: self.tainted_by_errors.clone(),
+ universe: self.universe.clone(),
+ }
+ }
+}
+
+pub trait ToTrace<'db>: Relate<DbInterner<'db>> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db>;
+}
+
+impl<'a, 'db> At<'a, 'db> {
+ /// Makes `actual <: expected`. For example, if type-checking a
+ /// call like `foo(x)`, where `foo: fn(i32)`, you might have
+ /// `sup(i32, x)`, since the "expected" type is the type that
+ /// appears in the signature.
+ pub fn sup<T>(
+ self,
+ define_opaque_types: DefineOpaqueTypes,
+ expected: T,
+ actual: T,
+ ) -> InferResult<'db, ()>
+ where
+ T: ToTrace<'db>,
+ {
+ RelateExt::relate(
+ self.infcx,
+ self.param_env,
+ expected,
+ Variance::Contravariant,
+ actual,
+ Span::dummy(),
+ )
+ .map(|goals| self.goals_to_obligations(goals))
+ }
+
+ /// Makes `expected <: actual`.
+ pub fn sub<T>(
+ self,
+ define_opaque_types: DefineOpaqueTypes,
+ expected: T,
+ actual: T,
+ ) -> InferResult<'db, ()>
+ where
+ T: ToTrace<'db>,
+ {
+ RelateExt::relate(
+ self.infcx,
+ self.param_env,
+ expected,
+ Variance::Covariant,
+ actual,
+ Span::dummy(),
+ )
+ .map(|goals| self.goals_to_obligations(goals))
+ }
+
+ /// Makes `expected == actual`.
+ pub fn eq<T>(
+ self,
+ define_opaque_types: DefineOpaqueTypes,
+ expected: T,
+ actual: T,
+ ) -> InferResult<'db, ()>
+ where
+ T: ToTrace<'db>,
+ {
+ self.eq_trace(
+ define_opaque_types,
+ ToTrace::to_trace(self.cause, expected, actual),
+ expected,
+ actual,
+ )
+ }
+
+ /// Makes `expected == actual`.
+ pub fn eq_trace<T>(
+ self,
+ define_opaque_types: DefineOpaqueTypes,
+ trace: TypeTrace<'db>,
+ expected: T,
+ actual: T,
+ ) -> InferResult<'db, ()>
+ where
+ T: Relate<DbInterner<'db>>,
+ {
+ RelateExt::relate(
+ self.infcx,
+ self.param_env,
+ expected,
+ Variance::Invariant,
+ actual,
+ Span::dummy(),
+ )
+ .map(|goals| self.goals_to_obligations(goals))
+ }
+
+ pub fn relate<T>(
+ self,
+ define_opaque_types: DefineOpaqueTypes,
+ expected: T,
+ variance: Variance,
+ actual: T,
+ ) -> InferResult<'db, ()>
+ where
+ T: ToTrace<'db>,
+ {
+ match variance {
+ Variance::Covariant => self.sub(define_opaque_types, expected, actual),
+ Variance::Invariant => self.eq(define_opaque_types, expected, actual),
+ Variance::Contravariant => self.sup(define_opaque_types, expected, actual),
+
+ // We could make this make sense but it's not readily
+ // exposed and I don't feel like dealing with it. Note
+ // that bivariance in general does a bit more than just
+ // *nothing*, it checks that the types are the same
+ // "modulo variance" basically.
+ Variance::Bivariant => panic!("Bivariant given to `relate()`"),
+ }
+ }
+
+ fn goals_to_obligations(&self, goals: Vec<Goal<'db, Predicate<'db>>>) -> InferOk<'db, ()> {
+ InferOk {
+ value: (),
+ obligations: goals
+ .into_iter()
+ .map(|goal| {
+ Obligation::new(
+ self.infcx.interner,
+ self.cause.clone(),
+ goal.param_env,
+ goal.predicate,
+ )
+ })
+ .collect(),
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for Ty<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for Region<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace { cause: cause.clone(), values: ValuePairs::Regions(ExpectedFound::new(a, b)) }
+ }
+}
+
+impl<'db> ToTrace<'db> for Const<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for GenericArg<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: match (a.kind(), b.kind()) {
+ (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => {
+ ValuePairs::Regions(ExpectedFound::new(a, b))
+ }
+ (GenericArgKind::Type(a), GenericArgKind::Type(b)) => {
+ ValuePairs::Terms(ExpectedFound::new(a.into(), b.into()))
+ }
+ (GenericArgKind::Const(a), GenericArgKind::Const(b)) => {
+ ValuePairs::Terms(ExpectedFound::new(a.into(), b.into()))
+ }
+ _ => panic!("relating different kinds: {a:?} {b:?}"),
+ },
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for Term<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace { cause: cause.clone(), values: ValuePairs::Terms(ExpectedFound::new(a, b)) }
+ }
+}
+
+impl<'db> ToTrace<'db> for TraitRef<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
+ }
+}
+
+impl<'db> ToTrace<'db> for AliasTy<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::Aliases(ExpectedFound::new(a.into(), b.into())),
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for AliasTerm<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace { cause: cause.clone(), values: ValuePairs::Aliases(ExpectedFound::new(a, b)) }
+ }
+}
+
+impl<'db> ToTrace<'db> for FnSig<DbInterner<'db>> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::PolySigs(ExpectedFound::new(Binder::dummy(a), Binder::dummy(b))),
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for PolyFnSig<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace { cause: cause.clone(), values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) }
+ }
+}
+
+impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a, b)),
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for PolyExistentialProjection<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::ExistentialProjection(ExpectedFound::new(a, b)),
+ }
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
new file mode 100644
index 0000000..0448f03
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
@@ -0,0 +1,106 @@
+//! This module contains code to instantiate new values into a
+//! `Canonical<'tcx, T>`.
+//!
+//! For an overview of what canonicalization is and how it fits into
+//! rustc, check out the [chapter in the rustc dev guide][c].
+//!
+//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
+
+use crate::next_solver::{
+ AliasTy, Binder, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Const, DbInterner, Goal,
+ ParamEnv, Predicate, PredicateKind, Region, Ty, TyKind,
+ fold::FnMutDelegate,
+ infer::{
+ DefineOpaqueTypes, InferCtxt, TypeTrace,
+ traits::{Obligation, PredicateObligations},
+ },
+};
+use rustc_type_ir::{
+ AliasRelationDirection, AliasTyKind, BoundVar, GenericArgKind, InferTy, TypeFoldable, Upcast,
+ Variance,
+ inherent::{IntoKind, SliceLike},
+ relate::{
+ Relate, TypeRelation, VarianceDiagInfo,
+ combine::{super_combine_consts, super_combine_tys},
+ },
+};
+
+pub trait CanonicalExt<'db, V> {
+ fn instantiate(&self, tcx: DbInterner<'db>, var_values: &CanonicalVarValues<'db>) -> V
+ where
+ V: TypeFoldable<DbInterner<'db>>;
+ fn instantiate_projected<T>(
+ &self,
+ tcx: DbInterner<'db>,
+ var_values: &CanonicalVarValues<'db>,
+ projection_fn: impl FnOnce(&V) -> T,
+ ) -> T
+ where
+ T: TypeFoldable<DbInterner<'db>>;
+}
+
+/// FIXME(-Znext-solver): This or public because it is shared with the
+/// new trait solver implementation. We should deduplicate canonicalization.
+impl<'db, V> CanonicalExt<'db, V> for Canonical<'db, V> {
+ /// Instantiate the wrapped value, replacing each canonical value
+ /// with the value given in `var_values`.
+ fn instantiate(&self, tcx: DbInterner<'db>, var_values: &CanonicalVarValues<'db>) -> V
+ where
+ V: TypeFoldable<DbInterner<'db>>,
+ {
+ self.instantiate_projected(tcx, var_values, |value| value.clone())
+ }
+
+ /// Allows one to apply a instantiation to some subset of
+ /// `self.value`. Invoke `projection_fn` with `self.value` to get
+ /// a value V that is expressed in terms of the same canonical
+ /// variables bound in `self` (usually this extracts from subset
+ /// of `self`). Apply the instantiation `var_values` to this value
+ /// V, replacing each of the canonical variables.
+ fn instantiate_projected<T>(
+ &self,
+ tcx: DbInterner<'db>,
+ var_values: &CanonicalVarValues<'db>,
+ projection_fn: impl FnOnce(&V) -> T,
+ ) -> T
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ assert_eq!(self.variables.len(), var_values.len());
+ let value = projection_fn(&self.value);
+ instantiate_value(tcx, var_values, value)
+ }
+}
+
+/// Instantiate the values from `var_values` into `value`. `var_values`
+/// must be values for the set of canonical variables that appear in
+/// `value`.
+pub(super) fn instantiate_value<'db, T>(
+ tcx: DbInterner<'db>,
+ var_values: &CanonicalVarValues<'db>,
+ value: T,
+) -> T
+where
+ T: TypeFoldable<DbInterner<'db>>,
+{
+ if var_values.var_values.is_empty() {
+ value
+ } else {
+ let delegate = FnMutDelegate {
+ regions: &mut |br: BoundRegion| match var_values[br.var].kind() {
+ GenericArgKind::Lifetime(l) => l,
+ r => panic!("{br:?} is a region but value is {r:?}"),
+ },
+ types: &mut |bound_ty: BoundTy| match var_values[bound_ty.var].kind() {
+ GenericArgKind::Type(ty) => ty,
+ r => panic!("{bound_ty:?} is a type but value is {r:?}"),
+ },
+ consts: &mut |bound_ct: BoundVar| match var_values[bound_ct].kind() {
+ GenericArgKind::Const(ct) => ct,
+ c => panic!("{bound_ct:?} is a const but value is {c:?}"),
+ },
+ };
+
+ tcx.replace_escaping_bound_vars_uncached(value, delegate)
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/crates/hir-ty/src/next_solver/infer/canonical/mod.rs
new file mode 100644
index 0000000..4457e13
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/canonical/mod.rs
@@ -0,0 +1,156 @@
+//! **Canonicalization** is the key to constructing a query in the
+//! middle of type inference. Ordinarily, it is not possible to store
+//! types from type inference in query keys, because they contain
+//! references to inference variables whose lifetimes are too short
+//! and so forth. Canonicalizing a value T1 using `canonicalize_query`
+//! produces two things:
+//!
+//! - a value T2 where each unbound inference variable has been
+//! replaced with a **canonical variable**;
+//! - a map M (of type `CanonicalVarValues`) from those canonical
+//! variables back to the original.
+//!
+//! We can then do queries using T2. These will give back constraints
+//! on the canonical variables which can be translated, using the map
+//! M, into constraints in our source context. This process of
+//! translating the results back is done by the
+//! `instantiate_query_result` method.
+//!
+//! For a more detailed look at what is happening here, check
+//! out the [chapter in the rustc dev guide][c].
+//!
+//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
+
+use crate::next_solver::{
+ AliasTy, Binder, Canonical, CanonicalVarValues, CanonicalVars, Const, DbInterner, GenericArg,
+ Goal, ParamEnv, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Predicate, PredicateKind,
+ Region, Ty, TyKind,
+ infer::{
+ DefineOpaqueTypes, InferCtxt, TypeTrace,
+ traits::{Obligation, PredicateObligations},
+ },
+};
+use instantiate::CanonicalExt;
+use rustc_index::IndexVec;
+use rustc_type_ir::{
+ AliasRelationDirection, AliasTyKind, CanonicalTyVarKind, CanonicalVarKind, InferTy,
+ TypeFoldable, UniverseIndex, Upcast, Variance,
+ inherent::{SliceLike, Ty as _},
+ relate::{
+ Relate, TypeRelation, VarianceDiagInfo,
+ combine::{super_combine_consts, super_combine_tys},
+ },
+};
+
+pub mod instantiate;
+
+impl<'db> InferCtxt<'db> {
+ /// Creates an instantiation S for the canonical value with fresh inference
+ /// variables and placeholders then applies it to the canonical value.
+ /// Returns both the instantiated result *and* the instantiation S.
+ ///
+ /// This can be invoked as part of constructing an
+ /// inference context at the start of a query (see
+ /// `InferCtxtBuilder::build_with_canonical`). It basically
+ /// brings the canonical value "into scope" within your new infcx.
+ ///
+ /// At the end of processing, the instantiation S (once
+ /// canonicalized) then represents the values that you computed
+ /// for each of the canonical inputs to your query.
+ pub fn instantiate_canonical<T>(
+ &self,
+ canonical: &Canonical<'db, T>,
+ ) -> (T, CanonicalVarValues<'db>)
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ // For each universe that is referred to in the incoming
+ // query, create a universe in our local inference context. In
+ // practice, as of this writing, all queries have no universes
+ // in them, so this code has no effect, but it is looking
+ // forward to the day when we *do* want to carry universes
+ // through into queries.
+ //
+ // Instantiate the root-universe content into the current universe,
+ // and create fresh universes for the higher universes.
+ let universes: IndexVec<UniverseIndex, _> = std::iter::once(self.universe())
+ .chain((1..=canonical.max_universe.as_u32()).map(|_| self.create_next_universe()))
+ .collect();
+
+ let canonical_inference_vars =
+ self.instantiate_canonical_vars(canonical.variables, |ui| universes[ui]);
+ let result = canonical.instantiate(self.interner, &canonical_inference_vars);
+ (result, canonical_inference_vars)
+ }
+
+ /// Given the "infos" about the canonical variables from some
+ /// canonical, creates fresh variables with the same
+ /// characteristics (see `instantiate_canonical_var` for
+ /// details). You can then use `instantiate` to instantiate the
+ /// canonical variable with these inference variables.
+ fn instantiate_canonical_vars(
+ &self,
+ variables: CanonicalVars<'db>,
+ universe_map: impl Fn(UniverseIndex) -> UniverseIndex,
+ ) -> CanonicalVarValues<'db> {
+ CanonicalVarValues {
+ var_values: self.interner.mk_args_from_iter(
+ variables.iter().map(|info| self.instantiate_canonical_var(info, &universe_map)),
+ ),
+ }
+ }
+
+ /// Given the "info" about a canonical variable, creates a fresh
+ /// variable for it. If this is an existentially quantified
+ /// variable, then you'll get a new inference variable; if it is a
+ /// universally quantified variable, you get a placeholder.
+ ///
+ /// FIXME(-Znext-solver): This is public because it's used by the
+ /// new trait solver which has a different canonicalization routine.
+ /// We should somehow deduplicate all of this.
+ pub fn instantiate_canonical_var(
+ &self,
+ cv_info: CanonicalVarKind<DbInterner<'db>>,
+ universe_map: impl Fn(UniverseIndex) -> UniverseIndex,
+ ) -> GenericArg<'db> {
+ match cv_info {
+ CanonicalVarKind::Ty(ty_kind) => {
+ let ty = match ty_kind {
+ CanonicalTyVarKind::General(ui) => {
+ self.next_ty_var_in_universe(universe_map(ui))
+ }
+
+ CanonicalTyVarKind::Int => self.next_int_var(),
+
+ CanonicalTyVarKind::Float => self.next_float_var(),
+ };
+ ty.into()
+ }
+
+ CanonicalVarKind::PlaceholderTy(PlaceholderTy { universe, bound }) => {
+ let universe_mapped = universe_map(universe);
+ let placeholder_mapped = PlaceholderTy { universe: universe_mapped, bound };
+ Ty::new_placeholder(self.interner, placeholder_mapped).into()
+ }
+
+ CanonicalVarKind::Region(ui) => {
+ self.next_region_var_in_universe(universe_map(ui)).into()
+ }
+
+ CanonicalVarKind::PlaceholderRegion(PlaceholderRegion { universe, bound }) => {
+ let universe_mapped = universe_map(universe);
+ let placeholder_mapped: crate::next_solver::Placeholder<
+ crate::next_solver::BoundRegion,
+ > = PlaceholderRegion { universe: universe_mapped, bound };
+ Region::new_placeholder(self.interner, placeholder_mapped).into()
+ }
+
+ CanonicalVarKind::Const(ui) => self.next_const_var_in_universe(universe_map(ui)).into(),
+ CanonicalVarKind::PlaceholderConst(PlaceholderConst { universe, bound }) => {
+ let universe_mapped = universe_map(universe);
+ let placeholder_mapped = PlaceholderConst { universe: universe_mapped, bound };
+ Const::new_placeholder(self.interner, placeholder_mapped).into()
+ }
+ }
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/context.rs b/crates/hir-ty/src/next_solver/infer/context.rs
new file mode 100644
index 0000000..93fd6ee
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/context.rs
@@ -0,0 +1,316 @@
+//! Definition of `InferCtxtLike` from the librarified type layer.
+
+use rustc_type_ir::{
+ ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy, IntTy, IntVarValue,
+ IntVid, RegionVid, TyVid, TypeFoldable, TypingMode, UniverseIndex,
+ inherent::{Const as _, IntoKind, Span as _, Ty as _},
+ relate::combine::PredicateEmittingRelation,
+};
+
+use crate::next_solver::{
+ Binder, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, OpaqueTypeKey, ParamEnv,
+ Region, SolverDefId, Span, Ty, TyKind,
+ infer::opaque_types::{OpaqueHiddenType, table::OpaqueTypeStorageEntries},
+};
+
+use super::{BoundRegionConversionTime, InferCtxt, relate::RelateResult, traits::ObligationCause};
+
+impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> {
+ type Interner = DbInterner<'db>;
+
+ fn cx(&self) -> DbInterner<'db> {
+ self.interner
+ }
+
+ fn next_trait_solver(&self) -> bool {
+ true
+ }
+
+ fn typing_mode(&self) -> TypingMode<DbInterner<'db>> {
+ self.typing_mode()
+ }
+
+ fn universe(&self) -> UniverseIndex {
+ self.universe()
+ }
+
+ fn create_next_universe(&self) -> UniverseIndex {
+ self.create_next_universe()
+ }
+
+ fn universe_of_ty(&self, vid: TyVid) -> Option<UniverseIndex> {
+ self.probe_ty_var(vid).err()
+ }
+
+ fn universe_of_lt(&self, lt: RegionVid) -> Option<UniverseIndex> {
+ self.inner.borrow_mut().unwrap_region_constraints().probe_value(lt).err()
+ }
+
+ fn universe_of_ct(&self, ct: ConstVid) -> Option<UniverseIndex> {
+ self.probe_const_var(ct).err()
+ }
+
+ fn root_ty_var(&self, var: TyVid) -> TyVid {
+ self.root_var(var)
+ }
+
+ fn root_const_var(&self, var: ConstVid) -> ConstVid {
+ self.root_const_var(var)
+ }
+
+ fn opportunistic_resolve_ty_var(&self, vid: TyVid) -> Ty<'db> {
+ match self.probe_ty_var(vid) {
+ Ok(ty) => ty,
+ Err(_) => Ty::new_var(self.interner, self.root_var(vid)),
+ }
+ }
+
+ fn opportunistic_resolve_int_var(&self, vid: IntVid) -> Ty<'db> {
+ self.opportunistic_resolve_int_var(vid)
+ }
+
+ fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'db> {
+ self.opportunistic_resolve_float_var(vid)
+ }
+
+ fn opportunistic_resolve_ct_var(&self, vid: ConstVid) -> Const<'db> {
+ match self.probe_const_var(vid) {
+ Ok(ct) => ct,
+ Err(_) => Const::new_var(self.interner, self.root_const_var(vid)),
+ }
+ }
+
+ fn opportunistic_resolve_lt_var(&self, vid: RegionVid) -> Region<'db> {
+ self.inner
+ .borrow_mut()
+ .unwrap_region_constraints()
+ .opportunistic_resolve_var(self.interner, vid)
+ }
+
+ fn is_changed_arg(&self, arg: <Self::Interner as rustc_type_ir::Interner>::GenericArg) -> bool {
+ match arg.kind() {
+ GenericArgKind::Lifetime(_) => {
+ // Lifetimes should not change affect trait selection.
+ false
+ }
+ GenericArgKind::Type(ty) => {
+ if let TyKind::Infer(infer_ty) = ty.kind() {
+ match infer_ty {
+ InferTy::TyVar(vid) => {
+ !self.probe_ty_var(vid).is_err_and(|_| self.root_var(vid) == vid)
+ }
+ InferTy::IntVar(vid) => {
+ let mut inner = self.inner.borrow_mut();
+ !matches!(
+ inner.int_unification_table().probe_value(vid),
+ IntVarValue::Unknown
+ if inner.int_unification_table().find(vid) == vid
+ )
+ }
+ InferTy::FloatVar(vid) => {
+ let mut inner = self.inner.borrow_mut();
+ !matches!(
+ inner.float_unification_table().probe_value(vid),
+ FloatVarValue::Unknown
+ if inner.float_unification_table().find(vid) == vid
+ )
+ }
+ InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => {
+ true
+ }
+ }
+ } else {
+ true
+ }
+ }
+ GenericArgKind::Const(ct) => {
+ if let ConstKind::Infer(infer_ct) = ct.kind() {
+ match infer_ct {
+ InferConst::Var(vid) => !self
+ .probe_const_var(vid)
+ .is_err_and(|_| self.root_const_var(vid) == vid),
+ InferConst::Fresh(_) => true,
+ }
+ } else {
+ true
+ }
+ }
+ }
+ }
+
+ fn next_ty_infer(&self) -> Ty<'db> {
+ self.next_ty_var()
+ }
+
+ fn next_region_infer(&self) -> <Self::Interner as rustc_type_ir::Interner>::Region {
+ self.next_region_var()
+ }
+
+ fn next_const_infer(&self) -> Const<'db> {
+ self.next_const_var()
+ }
+
+ fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> {
+ self.fresh_args_for_item(def_id)
+ }
+
+ fn instantiate_binder_with_infer<T: TypeFoldable<DbInterner<'db>> + Clone>(
+ &self,
+ value: Binder<'db, T>,
+ ) -> T {
+ self.instantiate_binder_with_fresh_vars(BoundRegionConversionTime::HigherRankedType, value)
+ }
+
+ fn enter_forall<T: TypeFoldable<DbInterner<'db>> + Clone, U>(
+ &self,
+ value: Binder<'db, T>,
+ f: impl FnOnce(T) -> U,
+ ) -> U {
+ self.enter_forall(value, f)
+ }
+
+ fn equate_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) {
+ self.inner.borrow_mut().type_variables().equate(a, b);
+ }
+
+ fn equate_int_vids_raw(&self, a: rustc_type_ir::IntVid, b: rustc_type_ir::IntVid) {
+ self.inner.borrow_mut().int_unification_table().union(a, b);
+ }
+
+ fn equate_float_vids_raw(&self, a: rustc_type_ir::FloatVid, b: rustc_type_ir::FloatVid) {
+ self.inner.borrow_mut().float_unification_table().union(a, b);
+ }
+
+ fn equate_const_vids_raw(&self, a: rustc_type_ir::ConstVid, b: rustc_type_ir::ConstVid) {
+ self.inner.borrow_mut().const_unification_table().union(a, b);
+ }
+
+ fn instantiate_ty_var_raw<R: PredicateEmittingRelation<Self>>(
+ &self,
+ relation: &mut R,
+ target_is_expected: bool,
+ target_vid: rustc_type_ir::TyVid,
+ instantiation_variance: rustc_type_ir::Variance,
+ source_ty: Ty<'db>,
+ ) -> RelateResult<'db, ()> {
+ self.instantiate_ty_var(
+ relation,
+ target_is_expected,
+ target_vid,
+ instantiation_variance,
+ source_ty,
+ )
+ }
+
+ fn instantiate_int_var_raw(
+ &self,
+ vid: rustc_type_ir::IntVid,
+ value: rustc_type_ir::IntVarValue,
+ ) {
+ self.inner.borrow_mut().int_unification_table().union_value(vid, value);
+ }
+
+ fn instantiate_float_var_raw(
+ &self,
+ vid: rustc_type_ir::FloatVid,
+ value: rustc_type_ir::FloatVarValue,
+ ) {
+ self.inner.borrow_mut().float_unification_table().union_value(vid, value);
+ }
+
+ fn instantiate_const_var_raw<R: PredicateEmittingRelation<Self>>(
+ &self,
+ relation: &mut R,
+ target_is_expected: bool,
+ target_vid: rustc_type_ir::ConstVid,
+ source_ct: Const<'db>,
+ ) -> RelateResult<'db, ()> {
+ self.instantiate_const_var(relation, target_is_expected, target_vid, source_ct)
+ }
+
+ fn set_tainted_by_errors(&self, e: ErrorGuaranteed) {
+ self.set_tainted_by_errors(e)
+ }
+
+ fn shallow_resolve(&self, ty: Ty<'db>) -> Ty<'db> {
+ self.shallow_resolve(ty)
+ }
+ fn shallow_resolve_const(&self, ct: Const<'db>) -> Const<'db> {
+ self.shallow_resolve_const(ct)
+ }
+
+ fn resolve_vars_if_possible<T>(&self, value: T) -> T
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ self.resolve_vars_if_possible(value)
+ }
+
+ fn probe<T>(&self, probe: impl FnOnce() -> T) -> T {
+ self.probe(|_| probe())
+ }
+
+ fn sub_regions(&self, sub: Region<'db>, sup: Region<'db>, span: Span) {
+ self.inner.borrow_mut().unwrap_region_constraints().make_subregion(sub, sup);
+ }
+
+ fn equate_regions(&self, a: Region<'db>, b: Region<'db>, span: Span) {
+ self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(a, b);
+ }
+
+ fn register_ty_outlives(&self, ty: Ty<'db>, r: Region<'db>, span: Span) {
+ //self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(Span::dummy()));
+ }
+
+ type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries;
+
+ fn opaque_types_storage_num_entries(&self) -> OpaqueTypeStorageEntries {
+ self.inner.borrow_mut().opaque_types().num_entries()
+ }
+ fn clone_opaque_types_lookup_table(&self) -> Vec<(OpaqueTypeKey<'db>, Ty<'db>)> {
+ self.inner.borrow_mut().opaque_types().iter_lookup_table().map(|(k, h)| (k, h.ty)).collect()
+ }
+ fn clone_duplicate_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, Ty<'db>)> {
+ self.inner
+ .borrow_mut()
+ .opaque_types()
+ .iter_duplicate_entries()
+ .map(|(k, h)| (k, h.ty))
+ .collect()
+ }
+ fn clone_opaque_types_added_since(
+ &self,
+ prev_entries: OpaqueTypeStorageEntries,
+ ) -> Vec<(OpaqueTypeKey<'db>, Ty<'db>)> {
+ self.inner
+ .borrow_mut()
+ .opaque_types()
+ .opaque_types_added_since(prev_entries)
+ .map(|(k, h)| (k, h.ty))
+ .collect()
+ }
+
+ fn register_hidden_type_in_storage(
+ &self,
+ opaque_type_key: OpaqueTypeKey<'db>,
+ hidden_ty: Ty<'db>,
+ _span: Span,
+ ) -> Option<Ty<'db>> {
+ self.register_hidden_type_in_storage(opaque_type_key, OpaqueHiddenType { ty: hidden_ty })
+ }
+ fn add_duplicate_opaque_type(
+ &self,
+ opaque_type_key: OpaqueTypeKey<'db>,
+ hidden_ty: Ty<'db>,
+ _span: Span,
+ ) {
+ self.inner
+ .borrow_mut()
+ .opaque_types()
+ .add_duplicate(opaque_type_key, OpaqueHiddenType { ty: hidden_ty })
+ }
+
+ fn reset_opaque_types(&self) {
+ let _ = self.take_opaque_types();
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/mod.rs b/crates/hir-ty/src/next_solver/infer/mod.rs
new file mode 100644
index 0000000..19ae76f
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/mod.rs
@@ -0,0 +1,1066 @@
+//! Infer context the next-trait-solver.
+
+use std::cell::{Cell, RefCell};
+use std::fmt;
+use std::sync::Arc;
+
+pub use BoundRegionConversionTime::*;
+pub use at::DefineOpaqueTypes;
+use ena::undo_log::UndoLogs;
+use ena::unify as ut;
+use intern::Symbol;
+use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage};
+use region_constraints::{
+ GenericKind, RegionConstraintCollector, RegionConstraintStorage, UndoLog, VarInfos, VerifyBound,
+};
+pub use relate::StructurallyRelateAliases;
+pub use relate::combine::PredicateEmittingRelation;
+use rustc_hash::{FxHashMap, FxHashSet};
+use rustc_pattern_analysis::Captures;
+use rustc_type_ir::error::{ExpectedFound, TypeError};
+use rustc_type_ir::inherent::{
+ Const as _, GenericArg as _, GenericArgs as _, IntoKind, ParamEnv as _, SliceLike, Term as _,
+ Ty as _,
+};
+use rustc_type_ir::{
+ BoundVar, ClosureKind, ConstVid, FloatTy, FloatVarValue, FloatVid, GenericArgKind, InferConst,
+ InferTy, IntTy, IntVarValue, IntVid, OutlivesPredicate, RegionVid, TyVid, UniverseIndex,
+};
+use rustc_type_ir::{TermKind, TypeVisitableExt};
+use rustc_type_ir::{TypeFoldable, TypeFolder, TypeSuperFoldable};
+use snapshot::undo_log::InferCtxtUndoLogs;
+use tracing::{debug, instrument};
+use traits::{ObligationCause, PredicateObligations};
+use type_variable::TypeVariableOrigin;
+use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey};
+
+use crate::next_solver::fold::BoundVarReplacerDelegate;
+use crate::next_solver::infer::opaque_types::table::OpaqueTypeStorageEntries;
+use crate::next_solver::{BoundRegion, BoundTy, BoundVarKind};
+
+use super::generics::{GenericParamDef, GenericParamDefKind};
+use super::{
+ AliasTerm, Binder, BoundRegionKind, CanonicalQueryInput, CanonicalVarValues, Const, ConstKind,
+ DbInterner, ErrorGuaranteed, FxIndexMap, GenericArg, GenericArgs, OpaqueTypeKey, ParamEnv,
+ PlaceholderRegion, PolyCoercePredicate, PolyExistentialProjection, PolyExistentialTraitRef,
+ PolyFnSig, PolyRegionOutlivesPredicate, PolySubtypePredicate, Predicate, Region, SolverDefId,
+ SubtypePredicate, Term, TraitPredicate, TraitRef, Ty, TyKind, TypingMode,
+};
+
+pub mod at;
+pub mod canonical;
+mod context;
+mod opaque_types;
+pub mod region_constraints;
+pub mod relate;
+pub mod resolve;
+pub(crate) mod snapshot;
+pub(crate) mod traits;
+mod type_variable;
+mod unify_key;
+
+/// `InferOk<'tcx, ()>` is used a lot. It may seem like a useless wrapper
+/// around `PredicateObligations`, but it has one important property:
+/// because `InferOk` is marked with `#[must_use]`, if you have a method
+/// `InferCtxt::f` that returns `InferResult<()>` and you call it with
+/// `infcx.f()?;` you'll get a warning about the obligations being discarded
+/// without use, which is probably unintentional and has been a source of bugs
+/// in the past.
+#[must_use]
+#[derive(Debug)]
+pub struct InferOk<'db, T> {
+ pub value: T,
+ pub obligations: PredicateObligations<'db>,
+}
+pub type InferResult<'db, T> = Result<InferOk<'db, T>, TypeError<DbInterner<'db>>>;
+
+pub(crate) type FixupResult<T> = Result<T, FixupError>; // "fixup result"
+
+pub(crate) type UnificationTable<'a, 'db, T> = ut::UnificationTable<
+ ut::InPlace<T, &'a mut ut::UnificationStorage<T>, &'a mut InferCtxtUndoLogs<'db>>,
+>;
+
+/// This type contains all the things within `InferCtxt` that sit within a
+/// `RefCell` and are involved with taking/rolling back snapshots. Snapshot
+/// operations are hot enough that we want only one call to `borrow_mut` per
+/// call to `start_snapshot` and `rollback_to`.
+#[derive(Clone)]
+pub struct InferCtxtInner<'db> {
+ pub(crate) undo_log: InferCtxtUndoLogs<'db>,
+
+ /// We instantiate `UnificationTable` with `bounds<Ty>` because the types
+ /// that might instantiate a general type variable have an order,
+ /// represented by its upper and lower bounds.
+ pub(crate) type_variable_storage: type_variable::TypeVariableStorage<'db>,
+
+ /// Map from const parameter variable to the kind of const it represents.
+ pub(crate) const_unification_storage: ut::UnificationTableStorage<ConstVidKey<'db>>,
+
+ /// Map from integral variable to the kind of integer it represents.
+ pub(crate) int_unification_storage: ut::UnificationTableStorage<IntVid>,
+
+ /// Map from floating variable to the kind of float it represents.
+ pub(crate) float_unification_storage: ut::UnificationTableStorage<FloatVid>,
+
+ /// Tracks the set of region variables and the constraints between them.
+ ///
+ /// This is initially `Some(_)` but when
+ /// `resolve_regions_and_report_errors` is invoked, this gets set to `None`
+ /// -- further attempts to perform unification, etc., may fail if new
+ /// region constraints would've been added.
+ pub(crate) region_constraint_storage: Option<RegionConstraintStorage<'db>>,
+
+ /// A set of constraints that regionck must validate.
+ ///
+ /// Each constraint has the form `T:'a`, meaning "some type `T` must
+ /// outlive the lifetime 'a". These constraints derive from
+ /// instantiated type parameters. So if you had a struct defined
+ /// like the following:
+ /// ```ignore (illustrative)
+ /// struct Foo<T: 'static> { ... }
+ /// ```
+ /// In some expression `let x = Foo { ... }`, it will
+ /// instantiate the type parameter `T` with a fresh type `$0`. At
+ /// the same time, it will record a region obligation of
+ /// `$0: 'static`. This will get checked later by regionck. (We
+ /// can't generally check these things right away because we have
+ /// to wait until types are resolved.)
+ ///
+ /// These are stored in a map keyed to the id of the innermost
+ /// enclosing fn body / static initializer expression. This is
+ /// because the location where the obligation was incurred can be
+ /// relevant with respect to which sublifetime assumptions are in
+ /// place. The reason that we store under the fn-id, and not
+ /// something more fine-grained, is so that it is easier for
+ /// regionck to be sure that it has found *all* the region
+ /// obligations (otherwise, it's easy to fail to walk to a
+ /// particular node-id).
+ ///
+ /// Before running `resolve_regions_and_report_errors`, the creator
+ /// of the inference context is expected to invoke
+ /// [`InferCtxt::process_registered_region_obligations`]
+ /// for each body-id in this map, which will process the
+ /// obligations within. This is expected to be done 'late enough'
+ /// that all type inference variables have been bound and so forth.
+ pub(crate) region_obligations: Vec<RegionObligation<'db>>,
+
+ /// Caches for opaque type inference.
+ pub(crate) opaque_type_storage: OpaqueTypeStorage<'db>,
+}
+
+impl<'db> InferCtxtInner<'db> {
+ fn new() -> InferCtxtInner<'db> {
+ InferCtxtInner {
+ undo_log: InferCtxtUndoLogs::default(),
+
+ type_variable_storage: Default::default(),
+ const_unification_storage: Default::default(),
+ int_unification_storage: Default::default(),
+ float_unification_storage: Default::default(),
+ region_constraint_storage: Some(Default::default()),
+ region_obligations: vec![],
+ opaque_type_storage: Default::default(),
+ }
+ }
+
+ #[inline]
+ pub fn region_obligations(&self) -> &[RegionObligation<'db>] {
+ &self.region_obligations
+ }
+
+ #[inline]
+ fn try_type_variables_probe_ref(
+ &self,
+ vid: TyVid,
+ ) -> Option<&type_variable::TypeVariableValue<'db>> {
+ // Uses a read-only view of the unification table, this way we don't
+ // need an undo log.
+ self.type_variable_storage.eq_relations_ref().try_probe_value(vid)
+ }
+
+ #[inline]
+ fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'db> {
+ self.type_variable_storage.with_log(&mut self.undo_log)
+ }
+
+ #[inline]
+ pub(crate) fn opaque_types(&mut self) -> opaque_types::OpaqueTypeTable<'_, 'db> {
+ self.opaque_type_storage.with_log(&mut self.undo_log)
+ }
+
+ #[inline]
+ fn int_unification_table(&mut self) -> UnificationTable<'_, 'db, IntVid> {
+ self.int_unification_storage.with_log(&mut self.undo_log)
+ }
+
+ #[inline]
+ fn float_unification_table(&mut self) -> UnificationTable<'_, 'db, FloatVid> {
+ self.float_unification_storage.with_log(&mut self.undo_log)
+ }
+
+ #[inline]
+ fn const_unification_table(&mut self) -> UnificationTable<'_, 'db, ConstVidKey<'db>> {
+ self.const_unification_storage.with_log(&mut self.undo_log)
+ }
+
+ #[inline]
+ pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'db, '_> {
+ self.region_constraint_storage
+ .as_mut()
+ .expect("region constraints already solved")
+ .with_log(&mut self.undo_log)
+ }
+}
+
+pub struct InferCtxt<'db> {
+ pub interner: DbInterner<'db>,
+
+ /// The mode of this inference context, see the struct documentation
+ /// for more details.
+ typing_mode: TypingMode<'db>,
+
+ pub inner: RefCell<InferCtxtInner<'db>>,
+
+ /// When an error occurs, we want to avoid reporting "derived"
+ /// errors that are due to this original failure. We have this
+ /// flag that one can set whenever one creates a type-error that
+ /// is due to an error in a prior pass.
+ ///
+ /// Don't read this flag directly, call `is_tainted_by_errors()`
+ /// and `set_tainted_by_errors()`.
+ tainted_by_errors: Cell<Option<ErrorGuaranteed>>,
+
+ /// What is the innermost universe we have created? Starts out as
+ /// `UniverseIndex::root()` but grows from there as we enter
+ /// universal quantifiers.
+ ///
+ /// N.B., at present, we exclude the universal quantifiers on the
+ /// item we are type-checking, and just consider those names as
+ /// part of the root universe. So this would only get incremented
+ /// when we enter into a higher-ranked (`for<..>`) type or trait
+ /// bound.
+ universe: Cell<UniverseIndex>,
+}
+
+/// See the `error_reporting` module for more details.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum ValuePairs<'db> {
+ Regions(ExpectedFound<Region<'db>>),
+ Terms(ExpectedFound<Term<'db>>),
+ Aliases(ExpectedFound<AliasTerm<'db>>),
+ TraitRefs(ExpectedFound<TraitRef<'db>>),
+ PolySigs(ExpectedFound<PolyFnSig<'db>>),
+ ExistentialTraitRef(ExpectedFound<PolyExistentialTraitRef<'db>>),
+ ExistentialProjection(ExpectedFound<PolyExistentialProjection<'db>>),
+}
+
+impl<'db> ValuePairs<'db> {
+ pub fn ty(&self) -> Option<(Ty<'db>, Ty<'db>)> {
+ if let ValuePairs::Terms(ExpectedFound { expected, found }) = self
+ && let Some(expected) = expected.as_type()
+ && let Some(found) = found.as_type()
+ {
+ return Some((expected, found));
+ }
+ None
+ }
+}
+
+/// The trace designates the path through inference that we took to
+/// encounter an error or subtyping constraint.
+///
+/// See the `error_reporting` module for more details.
+#[derive(Clone, Debug)]
+pub struct TypeTrace<'db> {
+ pub cause: ObligationCause,
+ pub values: ValuePairs<'db>,
+}
+
+/// Times when we replace bound regions with existentials:
+#[derive(Clone, Copy, Debug)]
+pub enum BoundRegionConversionTime {
+ /// when a fn is called
+ FnCall,
+
+ /// when two higher-ranked types are compared
+ HigherRankedType,
+
+ /// when projecting an associated type
+ AssocTypeProjection(SolverDefId),
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct FixupError {
+ unresolved: TyOrConstInferVar,
+}
+
+impl fmt::Display for FixupError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ use TyOrConstInferVar::*;
+
+ match self.unresolved {
+ TyInt(_) => write!(
+ f,
+ "cannot determine the type of this integer; \
+ add a suffix to specify the type explicitly"
+ ),
+ TyFloat(_) => write!(
+ f,
+ "cannot determine the type of this number; \
+ add a suffix to specify the type explicitly"
+ ),
+ Ty(_) => write!(f, "unconstrained type"),
+ Const(_) => write!(f, "unconstrained const value"),
+ }
+ }
+}
+
+/// See the `region_obligations` field for more information.
+#[derive(Clone, Debug)]
+pub struct RegionObligation<'db> {
+ pub sub_region: Region<'db>,
+ pub sup_type: Ty<'db>,
+}
+
+/// Used to configure inference contexts before their creation.
+pub struct InferCtxtBuilder<'db> {
+ interner: DbInterner<'db>,
+}
+
+pub trait DbInternerInferExt<'db> {
+ fn infer_ctxt(self) -> InferCtxtBuilder<'db>;
+}
+
+impl<'db> DbInternerInferExt<'db> for DbInterner<'db> {
+ fn infer_ctxt(self) -> InferCtxtBuilder<'db> {
+ InferCtxtBuilder { interner: self }
+ }
+}
+
+impl<'db> InferCtxtBuilder<'db> {
+ /// Given a canonical value `C` as a starting point, create an
+ /// inference context that contains each of the bound values
+ /// within instantiated as a fresh variable. The `f` closure is
+ /// invoked with the new infcx, along with the instantiated value
+ /// `V` and a instantiation `S`. This instantiation `S` maps from
+ /// the bound values in `C` to their instantiated values in `V`
+ /// (in other words, `S(C) = V`).
+ pub fn build_with_canonical<T>(
+ mut self,
+ input: &CanonicalQueryInput<'db, T>,
+ ) -> (InferCtxt<'db>, T, CanonicalVarValues<'db>)
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ let infcx = self.build(input.typing_mode);
+ let (value, args) = infcx.instantiate_canonical(&input.canonical);
+ (infcx, value, args)
+ }
+
+ pub fn build(&mut self, typing_mode: TypingMode<'db>) -> InferCtxt<'db> {
+ let InferCtxtBuilder { interner } = *self;
+ InferCtxt {
+ interner,
+ typing_mode,
+ inner: RefCell::new(InferCtxtInner::new()),
+ tainted_by_errors: Cell::new(None),
+ universe: Cell::new(UniverseIndex::ROOT),
+ }
+ }
+}
+
+impl<'db> InferOk<'db, ()> {
+ pub fn into_obligations(self) -> PredicateObligations<'db> {
+ self.obligations
+ }
+}
+
+impl<'db> InferCtxt<'db> {
+ #[inline(always)]
+ pub fn typing_mode(&self) -> TypingMode<'db> {
+ self.typing_mode
+ }
+
+ #[inline(always)]
+ pub fn typing_mode_unchecked(&self) -> TypingMode<'db> {
+ self.typing_mode
+ }
+
+ pub fn unresolved_variables(&self) -> Vec<Ty<'db>> {
+ let mut inner = self.inner.borrow_mut();
+ let mut vars: Vec<Ty<'db>> = inner
+ .type_variables()
+ .unresolved_variables()
+ .into_iter()
+ .map(|t| Ty::new_var(self.interner, t))
+ .collect();
+ vars.extend(
+ (0..inner.int_unification_table().len())
+ .map(IntVid::from_usize)
+ .filter(|&vid| inner.int_unification_table().probe_value(vid).is_unknown())
+ .map(|v| Ty::new_int_var(self.interner, v)),
+ );
+ vars.extend(
+ (0..inner.float_unification_table().len())
+ .map(FloatVid::from_usize)
+ .filter(|&vid| inner.float_unification_table().probe_value(vid).is_unknown())
+ .map(|v| Ty::new_float_var(self.interner, v)),
+ );
+ vars
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ pub fn sub_regions(&self, a: Region<'db>, b: Region<'db>) {
+ self.inner.borrow_mut().unwrap_region_constraints().make_subregion(a, b);
+ }
+
+ /// Processes a `Coerce` predicate from the fulfillment context.
+ /// This is NOT the preferred way to handle coercion, which is to
+ /// invoke `FnCtxt::coerce` or a similar method (see `coercion.rs`).
+ ///
+ /// This method here is actually a fallback that winds up being
+ /// invoked when `FnCtxt::coerce` encounters unresolved type variables
+ /// and records a coercion predicate. Presently, this method is equivalent
+ /// to `subtype_predicate` -- that is, "coercing" `a` to `b` winds up
+ /// actually requiring `a <: b`. This is of course a valid coercion,
+ /// but it's not as flexible as `FnCtxt::coerce` would be.
+ ///
+ /// (We may refactor this in the future, but there are a number of
+ /// practical obstacles. Among other things, `FnCtxt::coerce` presently
+ /// records adjustments that are required on the HIR in order to perform
+ /// the coercion, and we don't currently have a way to manage that.)
+ pub fn coerce_predicate(
+ &self,
+ cause: &ObligationCause,
+ param_env: ParamEnv<'db>,
+ predicate: PolyCoercePredicate<'db>,
+ ) -> Result<InferResult<'db, ()>, (TyVid, TyVid)> {
+ let subtype_predicate = predicate.map_bound(|p| SubtypePredicate {
+ a_is_expected: false, // when coercing from `a` to `b`, `b` is expected
+ a: p.a,
+ b: p.b,
+ });
+ self.subtype_predicate(cause, param_env, subtype_predicate)
+ }
+
+ pub fn subtype_predicate(
+ &self,
+ cause: &ObligationCause,
+ param_env: ParamEnv<'db>,
+ predicate: PolySubtypePredicate<'db>,
+ ) -> Result<InferResult<'db, ()>, (TyVid, TyVid)> {
+ // Check for two unresolved inference variables, in which case we can
+ // make no progress. This is partly a micro-optimization, but it's
+ // also an opportunity to "sub-unify" the variables. This isn't
+ // *necessary* to prevent cycles, because they would eventually be sub-unified
+ // anyhow during generalization, but it helps with diagnostics (we can detect
+ // earlier that they are sub-unified).
+ //
+ // Note that we can just skip the binders here because
+ // type variables can't (at present, at
+ // least) capture any of the things bound by this binder.
+ //
+ // Note that this sub here is not just for diagnostics - it has semantic
+ // effects as well.
+ let r_a = self.shallow_resolve(predicate.skip_binder().a);
+ let r_b = self.shallow_resolve(predicate.skip_binder().b);
+ match (r_a.kind(), r_b.kind()) {
+ (TyKind::Infer(InferTy::TyVar(a_vid)), TyKind::Infer(InferTy::TyVar(b_vid))) => {
+ return Err((a_vid, b_vid));
+ }
+ _ => {}
+ }
+
+ self.enter_forall(predicate, |SubtypePredicate { a_is_expected, a, b }| {
+ if a_is_expected {
+ Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::Yes, a, b))
+ } else {
+ Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::Yes, b, a))
+ }
+ })
+ }
+
+ pub fn region_outlives_predicate(
+ &self,
+ cause: &traits::ObligationCause,
+ predicate: PolyRegionOutlivesPredicate<'db>,
+ ) {
+ self.enter_forall(predicate, |OutlivesPredicate(r_a, r_b)| {
+ self.sub_regions(r_b, r_a); // `b : a` ==> `a <= b`
+ })
+ }
+
+ /// Number of type variables created so far.
+ pub fn num_ty_vars(&self) -> usize {
+ self.inner.borrow_mut().type_variables().num_vars()
+ }
+
+ pub fn next_ty_var(&self) -> Ty<'db> {
+ self.next_ty_var_with_origin(TypeVariableOrigin { param_def_id: None })
+ }
+
+ pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'db> {
+ let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin);
+ Ty::new_var(self.interner, vid)
+ }
+
+ pub fn next_ty_var_id_in_universe(&self, universe: UniverseIndex) -> TyVid {
+ let origin = TypeVariableOrigin { param_def_id: None };
+ self.inner.borrow_mut().type_variables().new_var(universe, origin)
+ }
+
+ pub fn next_ty_var_in_universe(&self, universe: UniverseIndex) -> Ty<'db> {
+ let vid = self.next_ty_var_id_in_universe(universe);
+ Ty::new_var(self.interner, vid)
+ }
+
+ pub fn next_const_var(&self) -> Const<'db> {
+ self.next_const_var_with_origin(ConstVariableOrigin { param_def_id: None })
+ }
+
+ pub fn next_const_var_with_origin(&self, origin: ConstVariableOrigin) -> Const<'db> {
+ let vid = self
+ .inner
+ .borrow_mut()
+ .const_unification_table()
+ .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() })
+ .vid;
+ Const::new_var(self.interner, vid)
+ }
+
+ pub fn next_const_var_in_universe(&self, universe: UniverseIndex) -> Const<'db> {
+ let origin = ConstVariableOrigin { param_def_id: None };
+ let vid = self
+ .inner
+ .borrow_mut()
+ .const_unification_table()
+ .new_key(ConstVariableValue::Unknown { origin, universe })
+ .vid;
+ Const::new_var(self.interner, vid)
+ }
+
+ pub fn next_int_var(&self) -> Ty<'db> {
+ let next_int_var_id =
+ self.inner.borrow_mut().int_unification_table().new_key(IntVarValue::Unknown);
+ Ty::new_int_var(self.interner, next_int_var_id)
+ }
+
+ pub fn next_float_var(&self) -> Ty<'db> {
+ let next_float_var_id =
+ self.inner.borrow_mut().float_unification_table().new_key(FloatVarValue::Unknown);
+ Ty::new_float_var(self.interner, next_float_var_id)
+ }
+
+ /// Creates a fresh region variable with the next available index.
+ /// The variable will be created in the maximum universe created
+ /// thus far, allowing it to name any region created thus far.
+ pub fn next_region_var(&self) -> Region<'db> {
+ self.next_region_var_in_universe(self.universe())
+ }
+
+ /// Creates a fresh region variable with the next available index
+ /// in the given universe; typically, you can use
+ /// `next_region_var` and just use the maximal universe.
+ pub fn next_region_var_in_universe(&self, universe: UniverseIndex) -> Region<'db> {
+ let region_var =
+ self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe);
+ Region::new_var(self.interner, region_var)
+ }
+
+ pub fn next_term_var_of_kind(&self, term: Term<'db>) -> Term<'db> {
+ match term.kind() {
+ TermKind::Ty(_) => self.next_ty_var().into(),
+ TermKind::Const(_) => self.next_const_var().into(),
+ }
+ }
+
+ /// Return the universe that the region `r` was created in. For
+ /// most regions (e.g., `'static`, named regions from the user,
+ /// etc) this is the root universe U0. For inference variables or
+ /// placeholders, however, it will return the universe which they
+ /// are associated.
+ pub fn universe_of_region(&self, r: Region<'db>) -> UniverseIndex {
+ self.inner.borrow_mut().unwrap_region_constraints().universe(r)
+ }
+
+ /// Number of region variables created so far.
+ pub fn num_region_vars(&self) -> usize {
+ self.inner.borrow_mut().unwrap_region_constraints().num_region_vars()
+ }
+
+ /// Just a convenient wrapper of `next_region_var` for using during NLL.
+ #[instrument(skip(self), level = "debug")]
+ pub fn next_nll_region_var(&self) -> Region<'db> {
+ self.next_region_var()
+ }
+
+ /// Just a convenient wrapper of `next_region_var` for using during NLL.
+ #[instrument(skip(self), level = "debug")]
+ pub fn next_nll_region_var_in_universe(&self, universe: UniverseIndex) -> Region<'db> {
+ self.next_region_var_in_universe(universe)
+ }
+
+ fn var_for_def(&self, kind: GenericParamDefKind, name: &Symbol) -> GenericArg<'db> {
+ match kind {
+ GenericParamDefKind::Lifetime => {
+ // Create a region inference variable for the given
+ // region parameter definition.
+ self.next_region_var().into()
+ }
+ GenericParamDefKind::Type => {
+ // Create a type inference variable for the given
+ // type parameter definition. The generic parameters are
+ // for actual parameters that may be referred to by
+ // the default of this type parameter, if it exists.
+ // e.g., `struct Foo<A, B, C = (A, B)>(...);` when
+ // used in a path such as `Foo::<T, U>::new()` will
+ // use an inference variable for `C` with `[T, U]`
+ // as the generic parameters for the default, `(T, U)`.
+ let ty_var_id = self
+ .inner
+ .borrow_mut()
+ .type_variables()
+ .new_var(self.universe(), TypeVariableOrigin { param_def_id: None });
+
+ Ty::new_var(self.interner, ty_var_id).into()
+ }
+ GenericParamDefKind::Const => {
+ let origin = ConstVariableOrigin { param_def_id: None };
+ let const_var_id = self
+ .inner
+ .borrow_mut()
+ .const_unification_table()
+ .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() })
+ .vid;
+ Const::new_var(self.interner, const_var_id).into()
+ }
+ }
+ }
+
+ /// Given a set of generics defined on a type or impl, returns the generic parameters mapping
+ /// each type/region parameter to a fresh inference variable.
+ pub fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> {
+ GenericArgs::for_item(self.interner, def_id, |name, index, kind, _| {
+ self.var_for_def(kind, name)
+ })
+ }
+
+ /// Returns `true` if errors have been reported since this infcx was
+ /// created. This is sometimes used as a heuristic to skip
+ /// reporting errors that often occur as a result of earlier
+ /// errors, but where it's hard to be 100% sure (e.g., unresolved
+ /// inference variables, regionck errors).
+ #[must_use = "this method does not have any side effects"]
+ pub fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> {
+ self.tainted_by_errors.get()
+ }
+
+ /// Set the "tainted by errors" flag to true. We call this when we
+ /// observe an error from a prior pass.
+ pub fn set_tainted_by_errors(&self, e: ErrorGuaranteed) {
+ debug!("set_tainted_by_errors(ErrorGuaranteed)");
+ self.tainted_by_errors.set(Some(e));
+ }
+
+ #[instrument(level = "debug", skip(self), ret)]
+ pub fn take_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> {
+ self.inner.borrow_mut().opaque_type_storage.take_opaque_types().collect()
+ }
+
+ #[instrument(level = "debug", skip(self), ret)]
+ pub fn clone_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> {
+ self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect()
+ }
+
+ #[inline(always)]
+ pub fn can_define_opaque_ty(&self, id: impl Into<SolverDefId>) -> bool {
+ match self.typing_mode_unchecked() {
+ TypingMode::Analysis { defining_opaque_types_and_generators } => {
+ defining_opaque_types_and_generators.contains(&id.into())
+ }
+ TypingMode::Coherence | TypingMode::PostAnalysis => false,
+ TypingMode::Borrowck { defining_opaque_types } => unimplemented!(),
+ TypingMode::PostBorrowckAnalysis { defined_opaque_types } => unimplemented!(),
+ }
+ }
+
+ /// If `TyVar(vid)` resolves to a type, return that type. Else, return the
+ /// universe index of `TyVar(vid)`.
+ pub fn probe_ty_var(&self, vid: TyVid) -> Result<Ty<'db>, UniverseIndex> {
+ use self::type_variable::TypeVariableValue;
+
+ match self.inner.borrow_mut().type_variables().probe(vid) {
+ TypeVariableValue::Known { value } => Ok(value),
+ TypeVariableValue::Unknown { universe } => Err(universe),
+ }
+ }
+
+ pub fn shallow_resolve(&self, ty: Ty<'db>) -> Ty<'db> {
+ if let TyKind::Infer(v) = ty.kind() {
+ match v {
+ InferTy::TyVar(v) => {
+ // Not entirely obvious: if `typ` is a type variable,
+ // it can be resolved to an int/float variable, which
+ // can then be recursively resolved, hence the
+ // recursion. Note though that we prevent type
+ // variables from unifying to other type variables
+ // directly (though they may be embedded
+ // structurally), and we prevent cycles in any case,
+ // so this recursion should always be of very limited
+ // depth.
+ //
+ // Note: if these two lines are combined into one we get
+ // dynamic borrow errors on `self.inner`.
+ let known = self.inner.borrow_mut().type_variables().probe(v).known();
+ known.map_or(ty, |t| self.shallow_resolve(t))
+ }
+
+ InferTy::IntVar(v) => {
+ match self.inner.borrow_mut().int_unification_table().probe_value(v) {
+ IntVarValue::IntType(ty) => Ty::new_int(self.interner, ty),
+ IntVarValue::UintType(ty) => Ty::new_uint(self.interner, ty),
+ IntVarValue::Unknown => ty,
+ }
+ }
+
+ InferTy::FloatVar(v) => {
+ match self.inner.borrow_mut().float_unification_table().probe_value(v) {
+ FloatVarValue::Known(ty) => Ty::new_float(self.interner, ty),
+ FloatVarValue::Unknown => ty,
+ }
+ }
+
+ InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => ty,
+ }
+ } else {
+ ty
+ }
+ }
+
+ pub fn shallow_resolve_const(&self, ct: Const<'db>) -> Const<'db> {
+ match ct.kind() {
+ ConstKind::Infer(infer_ct) => match infer_ct {
+ InferConst::Var(vid) => self
+ .inner
+ .borrow_mut()
+ .const_unification_table()
+ .probe_value(vid)
+ .known()
+ .unwrap_or(ct),
+ InferConst::Fresh(_) => ct,
+ },
+ ConstKind::Param(_)
+ | ConstKind::Bound(_, _)
+ | ConstKind::Placeholder(_)
+ | ConstKind::Unevaluated(_)
+ | ConstKind::Value(_)
+ | ConstKind::Error(_)
+ | ConstKind::Expr(_) => ct,
+ }
+ }
+
+ pub fn root_var(&self, var: TyVid) -> TyVid {
+ self.inner.borrow_mut().type_variables().root_var(var)
+ }
+
+ pub fn root_const_var(&self, var: ConstVid) -> ConstVid {
+ self.inner.borrow_mut().const_unification_table().find(var).vid
+ }
+
+ /// Resolves an int var to a rigid int type, if it was constrained to one,
+ /// or else the root int var in the unification table.
+ pub fn opportunistic_resolve_int_var(&self, vid: IntVid) -> Ty<'db> {
+ let mut inner = self.inner.borrow_mut();
+ let value = inner.int_unification_table().probe_value(vid);
+ match value {
+ IntVarValue::IntType(ty) => Ty::new_int(self.interner, ty),
+ IntVarValue::UintType(ty) => Ty::new_uint(self.interner, ty),
+ IntVarValue::Unknown => {
+ Ty::new_int_var(self.interner, inner.int_unification_table().find(vid))
+ }
+ }
+ }
+
+ /// Resolves a float var to a rigid int type, if it was constrained to one,
+ /// or else the root float var in the unification table.
+ pub fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'db> {
+ let mut inner = self.inner.borrow_mut();
+ let value = inner.float_unification_table().probe_value(vid);
+ match value {
+ FloatVarValue::Known(ty) => Ty::new_float(self.interner, ty),
+ FloatVarValue::Unknown => {
+ Ty::new_float_var(self.interner, inner.float_unification_table().find(vid))
+ }
+ }
+ }
+
+ /// Where possible, replaces type/const variables in
+ /// `value` with their final value. Note that region variables
+ /// are unaffected. If a type/const variable has not been unified, it
+ /// is left as is. This is an idempotent operation that does
+ /// not affect inference state in any way and so you can do it
+ /// at will.
+ pub fn resolve_vars_if_possible<T>(&self, value: T) -> T
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ if let Err(guar) = value.error_reported() {
+ self.set_tainted_by_errors(guar);
+ }
+ if !value.has_non_region_infer() {
+ return value;
+ }
+ let mut r = resolve::OpportunisticVarResolver::new(self);
+ value.fold_with(&mut r)
+ }
+
+ pub fn probe_const_var(&self, vid: ConstVid) -> Result<Const<'db>, UniverseIndex> {
+ match self.inner.borrow_mut().const_unification_table().probe_value(vid) {
+ ConstVariableValue::Known { value } => Ok(value),
+ ConstVariableValue::Unknown { origin: _, universe } => Err(universe),
+ }
+ }
+
+ // Instantiates the bound variables in a given binder with fresh inference
+ // variables in the current universe.
+ //
+ // Use this method if you'd like to find some generic parameters of the binder's
+ // variables (e.g. during a method call). If there isn't a [`BoundRegionConversionTime`]
+ // that corresponds to your use case, consider whether or not you should
+ // use [`InferCtxt::enter_forall`] instead.
+ pub fn instantiate_binder_with_fresh_vars<T>(
+ &self,
+ lbrct: BoundRegionConversionTime,
+ value: Binder<'db, T>,
+ ) -> T
+ where
+ T: TypeFoldable<DbInterner<'db>> + Clone,
+ {
+ if let Some(inner) = value.clone().no_bound_vars() {
+ return inner;
+ }
+
+ let bound_vars = value.clone().bound_vars();
+ let mut args = Vec::with_capacity(bound_vars.len());
+
+ for bound_var_kind in bound_vars {
+ let arg: GenericArg<'db> = match bound_var_kind {
+ BoundVarKind::Ty(_) => self.next_ty_var().into(),
+ BoundVarKind::Region(br) => self.next_region_var().into(),
+ BoundVarKind::Const => self.next_const_var().into(),
+ };
+ args.push(arg);
+ }
+
+ struct ToFreshVars<'db> {
+ args: Vec<GenericArg<'db>>,
+ }
+
+ impl<'db> BoundVarReplacerDelegate<'db> for ToFreshVars<'db> {
+ fn replace_region(&mut self, br: BoundRegion) -> Region<'db> {
+ self.args[br.var.index()].expect_region()
+ }
+ fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> {
+ self.args[bt.var.index()].expect_ty()
+ }
+ fn replace_const(&mut self, bv: BoundVar) -> Const<'db> {
+ self.args[bv.index()].expect_const()
+ }
+ }
+ let delegate = ToFreshVars { args };
+ self.interner.replace_bound_vars_uncached(value, delegate)
+ }
+
+ /// Obtains the latest type of the given closure; this may be a
+ /// closure in the current function, in which case its
+ /// `ClosureKind` may not yet be known.
+ pub fn closure_kind(&self, closure_ty: Ty<'db>) -> Option<ClosureKind> {
+ let unresolved_kind_ty = match closure_ty.kind() {
+ TyKind::Closure(_, args) => args.as_closure().kind_ty(),
+ TyKind::CoroutineClosure(_, args) => args.as_coroutine_closure().kind_ty(),
+ _ => panic!("unexpected type {closure_ty:?}"),
+ };
+ let closure_kind_ty = self.shallow_resolve(unresolved_kind_ty);
+ closure_kind_ty.to_opt_closure_kind()
+ }
+
+ pub fn universe(&self) -> UniverseIndex {
+ self.universe.get()
+ }
+
+ /// Creates and return a fresh universe that extends all previous
+ /// universes. Updates `self.universe` to that new universe.
+ pub fn create_next_universe(&self) -> UniverseIndex {
+ let u = self.universe.get().next_universe();
+ debug!("create_next_universe {u:?}");
+ self.universe.set(u);
+ u
+ }
+
+ /// The returned function is used in a fast path. If it returns `true` the variable is
+ /// unchanged, `false` indicates that the status is unknown.
+ #[inline]
+ pub fn is_ty_infer_var_definitely_unchanged<'a>(
+ &'a self,
+ ) -> (impl Fn(TyOrConstInferVar) -> bool + Captures<'db> + 'a) {
+ // This hoists the borrow/release out of the loop body.
+ let inner = self.inner.try_borrow();
+
+ move |infer_var: TyOrConstInferVar| match (infer_var, &inner) {
+ (TyOrConstInferVar::Ty(ty_var), Ok(inner)) => {
+ use self::type_variable::TypeVariableValue;
+
+ matches!(
+ inner.try_type_variables_probe_ref(ty_var),
+ Some(TypeVariableValue::Unknown { .. })
+ )
+ }
+ _ => false,
+ }
+ }
+
+ /// `ty_or_const_infer_var_changed` is equivalent to one of these two:
+ /// * `shallow_resolve(ty) != ty` (where `ty.kind = Infer(_)`)
+ /// * `shallow_resolve(ct) != ct` (where `ct.kind = ConstKind::Infer(_)`)
+ ///
+ /// However, `ty_or_const_infer_var_changed` is more efficient. It's always
+ /// inlined, despite being large, because it has only two call sites that
+ /// are extremely hot (both in `traits::fulfill`'s checking of `stalled_on`
+ /// inference variables), and it handles both `Ty` and `Const` without
+ /// having to resort to storing full `GenericArg`s in `stalled_on`.
+ #[inline(always)]
+ pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar) -> bool {
+ match infer_var {
+ TyOrConstInferVar::Ty(v) => {
+ use self::type_variable::TypeVariableValue;
+
+ // If `inlined_probe` returns a `Known` value, it never equals
+ // `Infer(TyVar(v))`.
+ match self.inner.borrow_mut().type_variables().inlined_probe(v) {
+ TypeVariableValue::Unknown { .. } => false,
+ TypeVariableValue::Known { .. } => true,
+ }
+ }
+
+ TyOrConstInferVar::TyInt(v) => {
+ // If `inlined_probe_value` returns a value it's always a
+ // `Int(_)` or `UInt(_)`, which never matches a
+ // `Infer(_)`.
+ self.inner.borrow_mut().int_unification_table().inlined_probe_value(v).is_known()
+ }
+
+ TyOrConstInferVar::TyFloat(v) => {
+ // If `probe_value` returns a value it's always a
+ // `Float(_)`, which never matches a `Infer(_)`.
+ //
+ // Not `inlined_probe_value(v)` because this call site is colder.
+ self.inner.borrow_mut().float_unification_table().probe_value(v).is_known()
+ }
+
+ TyOrConstInferVar::Const(v) => {
+ // If `probe_value` returns a `Known` value, it never equals
+ // `ConstKind::Infer(InferConst::Var(v))`.
+ //
+ // Not `inlined_probe_value(v)` because this call site is colder.
+ match self.inner.borrow_mut().const_unification_table().probe_value(v) {
+ ConstVariableValue::Unknown { .. } => false,
+ ConstVariableValue::Known { .. } => true,
+ }
+ }
+ }
+ }
+}
+
+/// Helper for [InferCtxt::ty_or_const_infer_var_changed] (see comment on that), currently
+/// used only for `traits::fulfill`'s list of `stalled_on` inference variables.
+#[derive(Copy, Clone, Debug)]
+pub enum TyOrConstInferVar {
+ /// Equivalent to `Infer(TyVar(_))`.
+ Ty(TyVid),
+ /// Equivalent to `Infer(IntVar(_))`.
+ TyInt(IntVid),
+ /// Equivalent to `Infer(FloatVar(_))`.
+ TyFloat(FloatVid),
+
+ /// Equivalent to `ConstKind::Infer(InferConst::Var(_))`.
+ Const(ConstVid),
+}
+
+impl TyOrConstInferVar {
+ /// Tries to extract an inference variable from a type or a constant, returns `None`
+ /// for types other than `Infer(_)` (or `InferTy::Fresh*`) and
+ /// for constants other than `ConstKind::Infer(_)` (or `InferConst::Fresh`).
+ pub fn maybe_from_generic_arg<'db>(arg: GenericArg<'db>) -> Option<Self> {
+ match arg.kind() {
+ GenericArgKind::Type(ty) => Self::maybe_from_ty(ty),
+ GenericArgKind::Const(ct) => Self::maybe_from_const(ct),
+ GenericArgKind::Lifetime(_) => None,
+ }
+ }
+
+ /// Tries to extract an inference variable from a type, returns `None`
+ /// for types other than `Infer(_)` (or `InferTy::Fresh*`).
+ fn maybe_from_ty<'db>(ty: Ty<'db>) -> Option<Self> {
+ match ty.kind() {
+ TyKind::Infer(InferTy::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)),
+ TyKind::Infer(InferTy::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)),
+ TyKind::Infer(InferTy::FloatVar(v)) => Some(TyOrConstInferVar::TyFloat(v)),
+ _ => None,
+ }
+ }
+
+ /// Tries to extract an inference variable from a constant, returns `None`
+ /// for constants other than `ConstKind::Infer(_)` (or `InferConst::Fresh`).
+ fn maybe_from_const<'db>(ct: Const<'db>) -> Option<Self> {
+ match ct.kind() {
+ ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)),
+ _ => None,
+ }
+ }
+}
+
+impl<'db> TypeTrace<'db> {
+ pub fn types(cause: &ObligationCause, a: Ty<'db>, b: Ty<'db>) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
+ }
+ }
+
+ pub fn trait_refs(
+ cause: &ObligationCause,
+ a: TraitRef<'db>,
+ b: TraitRef<'db>,
+ ) -> TypeTrace<'db> {
+ TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
+ }
+
+ pub fn consts(cause: &ObligationCause, a: Const<'db>, b: Const<'db>) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
+ }
+ }
+}
+
+/// Requires that `region` must be equal to one of the regions in `choice_regions`.
+/// We often denote this using the syntax:
+///
+/// ```text
+/// R0 member of [O1..On]
+/// ```
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct MemberConstraint<'db> {
+ /// The `DefId` and args of the opaque type causing this constraint.
+ /// Used for error reporting.
+ pub key: OpaqueTypeKey<'db>,
+
+ /// The hidden type in which `member_region` appears: used for error reporting.
+ pub hidden_ty: Ty<'db>,
+
+ /// The region `R0`.
+ pub member_region: Region<'db>,
+
+ /// The options `O1..On`.
+ pub choice_regions: Arc<Vec<Region<'db>>>,
+}
diff --git a/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs b/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs
new file mode 100644
index 0000000..0f68ec8
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs
@@ -0,0 +1,56 @@
+//! Things related to the infer context of the next-trait-solver.
+
+use std::sync::Arc;
+
+use tracing::{debug, instrument};
+
+use crate::next_solver::{
+ Clause, ClauseKind, FxIndexMap, GenericArgs, OpaqueTypeKey, ProjectionPredicate, SolverDefId,
+ TypingMode, util::BottomUpFolder,
+};
+
+pub(crate) mod table;
+
+pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable};
+
+use crate::next_solver::{
+ AliasTy, Binder, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Const, DbInterner, Goal,
+ ParamEnv, Predicate, PredicateKind, Region, Ty, TyKind,
+ fold::FnMutDelegate,
+ infer::{
+ DefineOpaqueTypes, InferCtxt, TypeTrace,
+ traits::{Obligation, PredicateObligations},
+ },
+};
+use rustc_type_ir::{
+ AliasRelationDirection, AliasTyKind, BoundConstness, BoundVar, Flags, GenericArgKind, InferTy,
+ Interner, RegionKind, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable,
+ TypeVisitableExt, TypeVisitor, Upcast, Variance,
+ error::{ExpectedFound, TypeError},
+ inherent::{DefId, GenericArgs as _, IntoKind, SliceLike},
+ relate::{
+ Relate, TypeRelation, VarianceDiagInfo,
+ combine::{super_combine_consts, super_combine_tys},
+ },
+};
+
+use super::{InferOk, traits::ObligationCause};
+
+#[derive(Copy, Clone, Debug)]
+pub struct OpaqueHiddenType<'db> {
+ pub ty: Ty<'db>,
+}
+
+impl<'db> InferCtxt<'db> {
+ /// Insert a hidden type into the opaque type storage, making sure
+ /// it hasn't previously been defined. This does not emit any
+ /// constraints and it's the responsibility of the caller to make
+ /// sure that the item bounds of the opaque are checked.
+ pub fn register_hidden_type_in_storage(
+ &self,
+ opaque_type_key: OpaqueTypeKey<'db>,
+ hidden_ty: OpaqueHiddenType<'db>,
+ ) -> Option<Ty<'db>> {
+ self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty)
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs b/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs
new file mode 100644
index 0000000..8ab409d
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs
@@ -0,0 +1,166 @@
+//! Things related to storage opaques in the infer context of the next-trait-solver.
+
+use std::ops::Deref;
+
+use ena::undo_log::UndoLogs;
+use tracing::instrument;
+
+use super::OpaqueHiddenType;
+use crate::next_solver::{
+ FxIndexMap, OpaqueTypeKey, Ty,
+ infer::snapshot::undo_log::{InferCtxtUndoLogs, UndoLog},
+};
+
+#[derive(Default, Debug, Clone)]
+pub(crate) struct OpaqueTypeStorage<'db> {
+ opaque_types: FxIndexMap<OpaqueTypeKey<'db>, OpaqueHiddenType<'db>>,
+ duplicate_entries: Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)>,
+}
+
+/// The number of entries in the opaque type storage at a given point.
+///
+/// Used to check that we haven't added any new opaque types after checking
+/// the opaque types currently in the storage.
+#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
+pub struct OpaqueTypeStorageEntries {
+ opaque_types: usize,
+ duplicate_entries: usize,
+}
+
+impl rustc_type_ir::inherent::OpaqueTypeStorageEntries for OpaqueTypeStorageEntries {
+ fn needs_reevaluation(self, canonicalized: usize) -> bool {
+ self.opaque_types != canonicalized
+ }
+}
+
+impl<'db> OpaqueTypeStorage<'db> {
+ #[instrument(level = "debug")]
+ pub(crate) fn remove(&mut self, key: OpaqueTypeKey<'db>, prev: Option<OpaqueHiddenType<'db>>) {
+ if let Some(prev) = prev {
+ *self.opaque_types.get_mut(&key).unwrap() = prev;
+ } else {
+ // FIXME(#120456) - is `swap_remove` correct?
+ match self.opaque_types.swap_remove(&key) {
+ None => {
+ panic!("reverted opaque type inference that was never registered: {key:?}")
+ }
+ Some(_) => {}
+ }
+ }
+ }
+
+ pub(crate) fn pop_duplicate_entry(&mut self) {
+ let entry = self.duplicate_entries.pop();
+ assert!(entry.is_some());
+ }
+
+ pub fn is_empty(&self) -> bool {
+ let OpaqueTypeStorage { opaque_types, duplicate_entries } = self;
+ opaque_types.is_empty() && duplicate_entries.is_empty()
+ }
+
+ pub(crate) fn take_opaque_types(
+ &mut self,
+ ) -> impl Iterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> {
+ let OpaqueTypeStorage { opaque_types, duplicate_entries } = self;
+ std::mem::take(opaque_types).into_iter().chain(std::mem::take(duplicate_entries))
+ }
+
+ pub fn num_entries(&self) -> OpaqueTypeStorageEntries {
+ OpaqueTypeStorageEntries {
+ opaque_types: self.opaque_types.len(),
+ duplicate_entries: self.duplicate_entries.len(),
+ }
+ }
+
+ pub fn opaque_types_added_since(
+ &self,
+ prev_entries: OpaqueTypeStorageEntries,
+ ) -> impl Iterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> {
+ self.opaque_types
+ .iter()
+ .skip(prev_entries.opaque_types)
+ .map(|(k, v)| (*k, *v))
+ .chain(self.duplicate_entries.iter().skip(prev_entries.duplicate_entries).copied())
+ }
+
+ /// Only returns the opaque types from the lookup table. These are used
+ /// when normalizing opaque types and have a unique key.
+ ///
+ /// Outside of canonicalization one should generally use `iter_opaque_types`
+ /// to also consider duplicate entries.
+ pub fn iter_lookup_table(
+ &self,
+ ) -> impl Iterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> {
+ self.opaque_types.iter().map(|(k, v)| (*k, *v))
+ }
+
+ /// Only returns the opaque types which are stored in `duplicate_entries`.
+ ///
+ /// These have to considered when checking all opaque type uses but are e.g.
+ /// irrelevant for canonical inputs as nested queries never meaningfully
+ /// accesses them.
+ pub fn iter_duplicate_entries(
+ &self,
+ ) -> impl Iterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> {
+ self.duplicate_entries.iter().copied()
+ }
+
+ pub fn iter_opaque_types(
+ &self,
+ ) -> impl Iterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> {
+ let OpaqueTypeStorage { opaque_types, duplicate_entries } = self;
+ opaque_types.iter().map(|(k, v)| (*k, *v)).chain(duplicate_entries.iter().copied())
+ }
+
+ #[inline]
+ pub(crate) fn with_log<'a>(
+ &'a mut self,
+ undo_log: &'a mut InferCtxtUndoLogs<'db>,
+ ) -> OpaqueTypeTable<'a, 'db> {
+ OpaqueTypeTable { storage: self, undo_log }
+ }
+}
+
+impl<'db> Drop for OpaqueTypeStorage<'db> {
+ fn drop(&mut self) {
+ if !self.opaque_types.is_empty() {
+ panic!("{:?}", self.opaque_types)
+ }
+ }
+}
+
+pub(crate) struct OpaqueTypeTable<'a, 'db> {
+ storage: &'a mut OpaqueTypeStorage<'db>,
+
+ undo_log: &'a mut InferCtxtUndoLogs<'db>,
+}
+impl<'db> Deref for OpaqueTypeTable<'_, 'db> {
+ type Target = OpaqueTypeStorage<'db>;
+ fn deref(&self) -> &Self::Target {
+ self.storage
+ }
+}
+
+impl<'a, 'db> OpaqueTypeTable<'a, 'db> {
+ #[instrument(skip(self), level = "debug")]
+ pub fn register(
+ &mut self,
+ key: OpaqueTypeKey<'db>,
+ hidden_type: OpaqueHiddenType<'db>,
+ ) -> Option<Ty<'db>> {
+ if let Some(entry) = self.storage.opaque_types.get_mut(&key) {
+ let prev = std::mem::replace(entry, hidden_type);
+ self.undo_log.push(UndoLog::OpaqueTypes(key, Some(prev)));
+ return Some(prev.ty);
+ }
+ self.storage.opaque_types.insert(key, hidden_type);
+ self.undo_log.push(UndoLog::OpaqueTypes(key, None));
+ None
+ }
+
+ pub fn add_duplicate(&mut self, key: OpaqueTypeKey<'db>, hidden_type: OpaqueHiddenType<'db>) {
+ self.storage.duplicate_entries.push((key, hidden_type));
+ self.undo_log.push(UndoLog::DuplicateOpaqueType);
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs
new file mode 100644
index 0000000..5054969
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs
@@ -0,0 +1,640 @@
+//! See `README.md`.
+
+use std::ops::Range;
+use std::sync::Arc;
+use std::{cmp, fmt, mem};
+
+use ena::undo_log::{Rollback, UndoLogs};
+use ena::unify as ut;
+use rustc_hash::FxHashMap;
+use rustc_index::IndexVec;
+use rustc_type_ir::inherent::IntoKind;
+use rustc_type_ir::{RegionKind, RegionVid, UniverseIndex};
+use tracing::{debug, instrument};
+
+use self::CombineMapType::*;
+use self::UndoLog::*;
+use super::MemberConstraint;
+use super::unify_key::RegionVidKey;
+use crate::next_solver::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot};
+use crate::next_solver::infer::unify_key::RegionVariableValue;
+use crate::next_solver::{
+ AliasTy, Binder, DbInterner, OpaqueTypeKey, ParamTy, PlaceholderTy, Region, Ty,
+};
+
+#[derive(Clone, Default)]
+pub struct RegionConstraintStorage<'db> {
+ /// For each `RegionVid`, the corresponding `RegionVariableOrigin`.
+ pub(super) var_infos: IndexVec<RegionVid, RegionVariableInfo>,
+
+ pub(super) data: RegionConstraintData<'db>,
+
+ /// For a given pair of regions (R1, R2), maps to a region R3 that
+ /// is designated as their LUB (edges R1 <= R3 and R2 <= R3
+ /// exist). This prevents us from making many such regions.
+ lubs: CombineMap<'db>,
+
+ /// For a given pair of regions (R1, R2), maps to a region R3 that
+ /// is designated as their GLB (edges R3 <= R1 and R3 <= R2
+ /// exist). This prevents us from making many such regions.
+ glbs: CombineMap<'db>,
+
+ /// When we add a R1 == R2 constraint, we currently add (a) edges
+ /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this
+ /// table. You can then call `opportunistic_resolve_var` early
+ /// which will map R1 and R2 to some common region (i.e., either
+ /// R1 or R2). This is important when fulfillment, dropck and other such
+ /// code is iterating to a fixed point, because otherwise we sometimes
+ /// would wind up with a fresh stream of region variables that have been
+ /// equated but appear distinct.
+ pub(super) unification_table: ut::UnificationTableStorage<RegionVidKey<'db>>,
+
+ /// a flag set to true when we perform any unifications; this is used
+ /// to micro-optimize `take_and_reset_data`
+ any_unifications: bool,
+}
+
+pub struct RegionConstraintCollector<'db, 'a> {
+ storage: &'a mut RegionConstraintStorage<'db>,
+ undo_log: &'a mut InferCtxtUndoLogs<'db>,
+}
+
+pub type VarInfos = IndexVec<RegionVid, RegionVariableInfo>;
+
+/// The full set of region constraints gathered up by the collector.
+/// Describes constraints between the region variables and other
+/// regions, as well as other conditions that must be verified, or
+/// assumptions that can be made.
+#[derive(Debug, Default, Clone)]
+pub struct RegionConstraintData<'db> {
+ /// Constraints of the form `A <= B`, where either `A` or `B` can
+ /// be a region variable (or neither, as it happens).
+ pub constraints: Vec<Constraint<'db>>,
+
+ /// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that
+ /// `R0` must be equal to one of the regions `R1..Rn`. These occur
+ /// with `impl Trait` quite frequently.
+ pub member_constraints: Vec<MemberConstraint<'db>>,
+
+ /// A "verify" is something that we need to verify after inference
+ /// is done, but which does not directly affect inference in any
+ /// way.
+ ///
+ /// An example is a `A <= B` where neither `A` nor `B` are
+ /// inference variables.
+ pub verifys: Vec<Verify<'db>>,
+}
+
+/// Represents a constraint that influences the inference process.
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub enum Constraint<'db> {
+ /// A region variable is a subregion of another.
+ VarSubVar(RegionVid, RegionVid),
+
+ /// A concrete region is a subregion of region variable.
+ RegSubVar(Region<'db>, RegionVid),
+
+ /// A region variable is a subregion of a concrete region. This does not
+ /// directly affect inference, but instead is checked after
+ /// inference is complete.
+ VarSubReg(RegionVid, Region<'db>),
+
+ /// A constraint where neither side is a variable. This does not
+ /// directly affect inference, but instead is checked after
+ /// inference is complete.
+ RegSubReg(Region<'db>, Region<'db>),
+}
+
+impl<'db> Constraint<'db> {
+ pub fn involves_placeholders(&self) -> bool {
+ match self {
+ Constraint::VarSubVar(_, _) => false,
+ Constraint::VarSubReg(_, r) | Constraint::RegSubVar(r, _) => r.is_placeholder(),
+ Constraint::RegSubReg(r, s) => r.is_placeholder() || s.is_placeholder(),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Verify<'db> {
+ pub kind: GenericKind<'db>,
+ pub region: Region<'db>,
+ pub bound: VerifyBound<'db>,
+}
+
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub enum GenericKind<'db> {
+ Param(ParamTy),
+ Placeholder(PlaceholderTy),
+ Alias(AliasTy<'db>),
+}
+
+/// Describes the things that some `GenericKind` value `G` is known to
+/// outlive. Each variant of `VerifyBound` can be thought of as a
+/// function:
+/// ```ignore (pseudo-rust)
+/// fn(min: Region) -> bool { .. }
+/// ```
+/// where `true` means that the region `min` meets that `G: min`.
+/// (False means nothing.)
+///
+/// So, for example, if we have the type `T` and we have in scope that
+/// `T: 'a` and `T: 'b`, then the verify bound might be:
+/// ```ignore (pseudo-rust)
+/// fn(min: Region) -> bool {
+/// ('a: min) || ('b: min)
+/// }
+/// ```
+/// This is described with an `AnyRegion('a, 'b)` node.
+#[derive(Debug, Clone)]
+pub enum VerifyBound<'db> {
+ /// See [`VerifyIfEq`] docs
+ IfEq(Binder<'db, VerifyIfEq<'db>>),
+
+ /// Given a region `R`, expands to the function:
+ ///
+ /// ```ignore (pseudo-rust)
+ /// fn(min) -> bool {
+ /// R: min
+ /// }
+ /// ```
+ ///
+ /// This is used when we can establish that `G: R` -- therefore,
+ /// if `R: min`, then by transitivity `G: min`.
+ OutlivedBy(Region<'db>),
+
+ /// Given a region `R`, true if it is `'empty`.
+ IsEmpty,
+
+ /// Given a set of bounds `B`, expands to the function:
+ ///
+ /// ```ignore (pseudo-rust)
+ /// fn(min) -> bool {
+ /// exists (b in B) { b(min) }
+ /// }
+ /// ```
+ ///
+ /// In other words, if we meet some bound in `B`, that suffices.
+ /// This is used when all the bounds in `B` are known to apply to `G`.
+ AnyBound(Vec<VerifyBound<'db>>),
+
+ /// Given a set of bounds `B`, expands to the function:
+ ///
+ /// ```ignore (pseudo-rust)
+ /// fn(min) -> bool {
+ /// forall (b in B) { b(min) }
+ /// }
+ /// ```
+ ///
+ /// In other words, if we meet *all* bounds in `B`, that suffices.
+ /// This is used when *some* bound in `B` is known to suffice, but
+ /// we don't know which.
+ AllBounds(Vec<VerifyBound<'db>>),
+}
+
+/// This is a "conditional bound" that checks the result of inference
+/// and supplies a bound if it ended up being relevant. It's used in situations
+/// like this:
+///
+/// ```rust,ignore (pseudo-Rust)
+/// fn foo<'a, 'b, T: SomeTrait<'a>>
+/// where
+/// <T as SomeTrait<'a>>::Item: 'b
+/// ```
+///
+/// If we have an obligation like `<T as SomeTrait<'?x>>::Item: 'c`, then
+/// we don't know yet whether it suffices to show that `'b: 'c`. If `'?x` winds
+/// up being equal to `'a`, then the where-clauses on function applies, and
+/// in that case we can show `'b: 'c`. But if `'?x` winds up being something
+/// else, the bound isn't relevant.
+///
+/// In the [`VerifyBound`], this struct is enclosed in `Binder` to account
+/// for cases like
+///
+/// ```rust,ignore (pseudo-Rust)
+/// where for<'a> <T as SomeTrait<'a>::Item: 'a
+/// ```
+///
+/// The idea is that we have to find some instantiation of `'a` that can
+/// make `<T as SomeTrait<'a>>::Item` equal to the final value of `G`,
+/// the generic we are checking.
+///
+/// ```ignore (pseudo-rust)
+/// fn(min) -> bool {
+/// exists<'a> {
+/// if G == K {
+/// B(min)
+/// } else {
+/// false
+/// }
+/// }
+/// }
+/// ```
+#[derive(Debug, Clone)]
+pub struct VerifyIfEq<'db> {
+ /// Type which must match the generic `G`
+ pub ty: Ty<'db>,
+
+ /// Bound that applies if `ty` is equal.
+ pub bound: Region<'db>,
+}
+
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub(crate) struct TwoRegions<'db> {
+ a: Region<'db>,
+ b: Region<'db>,
+}
+
+#[derive(Clone, PartialEq)]
+pub(crate) enum UndoLog<'db> {
+ /// We added `RegionVid`.
+ AddVar(RegionVid),
+
+ /// We added the given `constraint`.
+ AddConstraint(usize),
+
+ /// We added the given `verify`.
+ AddVerify(usize),
+
+ /// We added a GLB/LUB "combination variable".
+ AddCombination(CombineMapType, TwoRegions<'db>),
+}
+
+#[derive(Clone, PartialEq)]
+pub(crate) enum CombineMapType {
+ Lub,
+ Glb,
+}
+
+type CombineMap<'db> = FxHashMap<TwoRegions<'db>, RegionVid>;
+
+#[derive(Debug, Clone)]
+pub struct RegionVariableInfo {
+ // FIXME: This is only necessary for `fn take_and_reset_data` and
+ // `lexical_region_resolve`. We should rework `lexical_region_resolve`
+ // in the near/medium future anyways and could move the unverse info
+ // for `fn take_and_reset_data` into a separate table which is
+ // only populated when needed.
+ //
+ // For both of these cases it is fine that this can diverge from the
+ // actual universe of the variable, which is directly stored in the
+ // unification table for unknown region variables. At some point we could
+ // stop emitting bidirectional outlives constraints if equate succeeds.
+ // This would be currently unsound as it would cause us to drop the universe
+ // changes in `lexical_region_resolve`.
+ pub universe: UniverseIndex,
+}
+
+pub(crate) struct RegionSnapshot {
+ any_unifications: bool,
+}
+
+impl<'db> RegionConstraintStorage<'db> {
+ #[inline]
+ pub(crate) fn with_log<'a>(
+ &'a mut self,
+ undo_log: &'a mut InferCtxtUndoLogs<'db>,
+ ) -> RegionConstraintCollector<'db, 'a> {
+ RegionConstraintCollector { storage: self, undo_log }
+ }
+}
+
+impl<'db> RegionConstraintCollector<'db, '_> {
+ pub fn num_region_vars(&self) -> usize {
+ self.storage.var_infos.len()
+ }
+
+ pub fn region_constraint_data(&self) -> &RegionConstraintData<'db> {
+ &self.storage.data
+ }
+
+ /// Takes (and clears) the current set of constraints. Note that
+ /// the set of variables remains intact, but all relationships
+ /// between them are reset. This is used during NLL checking to
+ /// grab the set of constraints that arose from a particular
+ /// operation.
+ ///
+ /// We don't want to leak relationships between variables between
+ /// points because just because (say) `r1 == r2` was true at some
+ /// point P in the graph doesn't imply that it will be true at
+ /// some other point Q, in NLL.
+ ///
+ /// Not legal during a snapshot.
+ pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'db> {
+ assert!(!UndoLogs::<UndoLog<'db>>::in_snapshot(&self.undo_log));
+
+ // If you add a new field to `RegionConstraintCollector`, you
+ // should think carefully about whether it needs to be cleared
+ // or updated in some way.
+ let RegionConstraintStorage {
+ var_infos: _,
+ data,
+ lubs,
+ glbs,
+ unification_table: _,
+ any_unifications,
+ } = self.storage;
+
+ // Clear the tables of (lubs, glbs), so that we will create
+ // fresh regions if we do a LUB operation. As it happens,
+ // LUB/GLB are not performed by the MIR type-checker, which is
+ // the one that uses this method, but it's good to be correct.
+ lubs.clear();
+ glbs.clear();
+
+ let data = mem::take(data);
+
+ // Clear all unifications and recreate the variables a "now
+ // un-unified" state. Note that when we unify `a` and `b`, we
+ // also insert `a <= b` and a `b <= a` edges, so the
+ // `RegionConstraintData` contains the relationship here.
+ if *any_unifications {
+ *any_unifications = false;
+ // Manually inlined `self.unification_table_mut()` as `self` is used in the closure.
+ ut::UnificationTable::with_log(&mut self.storage.unification_table, &mut self.undo_log)
+ .reset_unifications(|key| RegionVariableValue::Unknown {
+ universe: self.storage.var_infos[key.vid].universe,
+ });
+ }
+
+ data
+ }
+
+ pub fn data(&self) -> &RegionConstraintData<'db> {
+ &self.storage.data
+ }
+
+ pub(super) fn start_snapshot(&self) -> RegionSnapshot {
+ debug!("RegionConstraintCollector: start_snapshot");
+ RegionSnapshot { any_unifications: self.storage.any_unifications }
+ }
+
+ pub(super) fn rollback_to(&mut self, snapshot: RegionSnapshot) {
+ debug!("RegionConstraintCollector: rollback_to({:?})", snapshot);
+ self.storage.any_unifications = snapshot.any_unifications;
+ }
+
+ pub(super) fn new_region_var(&mut self, universe: UniverseIndex) -> RegionVid {
+ let vid = self.storage.var_infos.push(RegionVariableInfo { universe });
+
+ let u_vid = self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe });
+ assert_eq!(vid, u_vid.vid);
+ self.undo_log.push(AddVar(vid));
+ debug!("created new region variable {:?} in {:?}", vid, universe);
+ vid
+ }
+
+ fn add_constraint(&mut self, constraint: Constraint<'db>) {
+ // cannot add constraints once regions are resolved
+ debug!("RegionConstraintCollector: add_constraint({:?})", constraint);
+
+ let index = self.storage.data.constraints.len();
+ self.storage.data.constraints.push(constraint);
+ self.undo_log.push(AddConstraint(index));
+ }
+
+ pub(super) fn make_eqregion(&mut self, a: Region<'db>, b: Region<'db>) {
+ if a != b {
+ // Eventually, it would be nice to add direct support for
+ // equating regions.
+ self.make_subregion(a, b);
+ self.make_subregion(b, a);
+
+ match (a.kind(), b.kind()) {
+ (RegionKind::ReVar(a), RegionKind::ReVar(b)) => {
+ debug!("make_eqregion: unifying {:?} with {:?}", a, b);
+ if self.unification_table_mut().unify_var_var(a, b).is_ok() {
+ self.storage.any_unifications = true;
+ }
+ }
+ (RegionKind::ReVar(vid), _) => {
+ debug!("make_eqregion: unifying {:?} with {:?}", vid, b);
+ if self
+ .unification_table_mut()
+ .unify_var_value(vid, RegionVariableValue::Known { value: b })
+ .is_ok()
+ {
+ self.storage.any_unifications = true;
+ };
+ }
+ (_, RegionKind::ReVar(vid)) => {
+ debug!("make_eqregion: unifying {:?} with {:?}", a, vid);
+ if self
+ .unification_table_mut()
+ .unify_var_value(vid, RegionVariableValue::Known { value: a })
+ .is_ok()
+ {
+ self.storage.any_unifications = true;
+ };
+ }
+ (_, _) => {}
+ }
+ }
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ pub(super) fn make_subregion(&mut self, sub: Region<'db>, sup: Region<'db>) {
+ // cannot add constraints once regions are resolved
+
+ match (sub.kind(), sup.kind()) {
+ (RegionKind::ReBound(..), _) | (_, RegionKind::ReBound(..)) => {
+ panic!("cannot relate bound region: {sub:?} <= {sup:?}");
+ }
+ (_, RegionKind::ReStatic) => {
+ // all regions are subregions of static, so we can ignore this
+ }
+ (RegionKind::ReVar(sub_id), RegionKind::ReVar(sup_id)) => {
+ self.add_constraint(Constraint::VarSubVar(sub_id, sup_id));
+ }
+ (_, RegionKind::ReVar(sup_id)) => {
+ self.add_constraint(Constraint::RegSubVar(sub, sup_id));
+ }
+ (RegionKind::ReVar(sub_id), _) => {
+ self.add_constraint(Constraint::VarSubReg(sub_id, sup));
+ }
+ _ => {
+ self.add_constraint(Constraint::RegSubReg(sub, sup));
+ }
+ }
+ }
+
+ /// Resolves a region var to its value in the unification table, if it exists.
+ /// Otherwise, it is resolved to the root `ReVar` in the table.
+ pub fn opportunistic_resolve_var(
+ &mut self,
+ cx: DbInterner<'db>,
+ vid: RegionVid,
+ ) -> Region<'db> {
+ let mut ut = self.unification_table_mut();
+ let root_vid = ut.find(vid).vid;
+ match ut.probe_value(root_vid) {
+ RegionVariableValue::Known { value } => value,
+ RegionVariableValue::Unknown { .. } => Region::new_var(cx, root_vid),
+ }
+ }
+
+ pub fn probe_value(&mut self, vid: RegionVid) -> Result<Region<'db>, UniverseIndex> {
+ match self.unification_table_mut().probe_value(vid) {
+ RegionVariableValue::Known { value } => Ok(value),
+ RegionVariableValue::Unknown { universe } => Err(universe),
+ }
+ }
+
+ fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'db> {
+ match t {
+ Glb => &mut self.storage.glbs,
+ Lub => &mut self.storage.lubs,
+ }
+ }
+
+ fn combine_vars(
+ &mut self,
+ cx: DbInterner<'db>,
+ t: CombineMapType,
+ a: Region<'db>,
+ b: Region<'db>,
+ ) -> Region<'db> {
+ let vars = TwoRegions { a, b };
+ if let Some(c) = self.combine_map(t.clone()).get(&vars) {
+ return Region::new_var(cx, *c);
+ }
+ let a_universe = self.universe(a);
+ let b_universe = self.universe(b);
+ let c_universe = cmp::max(a_universe, b_universe);
+ let c = self.new_region_var(c_universe);
+ self.combine_map(t.clone()).insert(vars.clone(), c);
+ self.undo_log.push(AddCombination(t.clone(), vars));
+ let new_r = Region::new_var(cx, c);
+ for old_r in [a, b] {
+ match t {
+ Glb => self.make_subregion(new_r, old_r),
+ Lub => self.make_subregion(old_r, new_r),
+ }
+ }
+ debug!("combine_vars() c={:?}", c);
+ new_r
+ }
+
+ pub fn universe(&mut self, region: Region<'db>) -> UniverseIndex {
+ match region.kind() {
+ RegionKind::ReStatic
+ | RegionKind::ReErased
+ | RegionKind::ReLateParam(..)
+ | RegionKind::ReEarlyParam(..)
+ | RegionKind::ReError(_) => UniverseIndex::ROOT,
+ RegionKind::RePlaceholder(placeholder) => placeholder.universe,
+ RegionKind::ReVar(vid) => match self.probe_value(vid) {
+ Ok(value) => self.universe(value),
+ Err(universe) => universe,
+ },
+ RegionKind::ReBound(..) => panic!("universe(): encountered bound region {region:?}"),
+ }
+ }
+
+ #[inline]
+ fn unification_table_mut(&mut self) -> super::UnificationTable<'_, 'db, RegionVidKey<'db>> {
+ ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log)
+ }
+}
+
+impl fmt::Debug for RegionSnapshot {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "RegionSnapshot")
+ }
+}
+
+impl<'db> fmt::Debug for GenericKind<'db> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ GenericKind::Param(ref p) => write!(f, "{p:?}"),
+ GenericKind::Placeholder(ref p) => write!(f, "{p:?}"),
+ GenericKind::Alias(ref p) => write!(f, "{p:?}"),
+ }
+ }
+}
+
+impl<'db> fmt::Display for GenericKind<'db> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ GenericKind::Param(ref p) => write!(f, "{p:?}"),
+ GenericKind::Placeholder(ref p) => write!(f, "{p:?}"),
+ GenericKind::Alias(ref p) => write!(f, "{p}"),
+ }
+ }
+}
+
+impl<'db> GenericKind<'db> {
+ pub fn to_ty(&self, interner: DbInterner<'db>) -> Ty<'db> {
+ match *self {
+ GenericKind::Param(ref p) => (*p).to_ty(interner),
+ GenericKind::Placeholder(ref p) => Ty::new_placeholder(interner, *p),
+ GenericKind::Alias(ref p) => (*p).to_ty(interner),
+ }
+ }
+}
+
+impl<'db> VerifyBound<'db> {
+ pub fn must_hold(&self) -> bool {
+ match self {
+ VerifyBound::IfEq(..) => false,
+ VerifyBound::OutlivedBy(re) => re.is_static(),
+ VerifyBound::IsEmpty => false,
+ VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()),
+ VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()),
+ }
+ }
+
+ pub fn cannot_hold(&self) -> bool {
+ match self {
+ VerifyBound::IfEq(..) => false,
+ VerifyBound::IsEmpty => false,
+ VerifyBound::OutlivedBy(_) => false,
+ VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()),
+ VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()),
+ }
+ }
+
+ pub fn or(self, vb: VerifyBound<'db>) -> VerifyBound<'db> {
+ if self.must_hold() || vb.cannot_hold() {
+ self
+ } else if self.cannot_hold() || vb.must_hold() {
+ vb
+ } else {
+ VerifyBound::AnyBound(vec![self, vb])
+ }
+ }
+}
+
+impl<'db> RegionConstraintData<'db> {
+ /// Returns `true` if this region constraint data contains no constraints, and `false`
+ /// otherwise.
+ pub fn is_empty(&self) -> bool {
+ let RegionConstraintData { constraints, member_constraints, verifys } = self;
+ constraints.is_empty() && member_constraints.is_empty() && verifys.is_empty()
+ }
+}
+
+impl<'db> Rollback<UndoLog<'db>> for RegionConstraintStorage<'db> {
+ fn reverse(&mut self, undo: UndoLog<'db>) {
+ match undo {
+ AddVar(vid) => {
+ self.var_infos.pop().unwrap();
+ assert_eq!(self.var_infos.len(), vid.index());
+ }
+ AddConstraint(index) => {
+ self.data.constraints.pop().unwrap();
+ assert_eq!(self.data.constraints.len(), index);
+ }
+ AddVerify(index) => {
+ self.data.verifys.pop();
+ assert_eq!(self.data.verifys.len(), index);
+ }
+ AddCombination(Glb, ref regions) => {
+ self.glbs.remove(regions);
+ }
+ AddCombination(Lub, ref regions) => {
+ self.lubs.remove(regions);
+ }
+ }
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/crates/hir-ty/src/next_solver/infer/relate/generalize.rs
new file mode 100644
index 0000000..de336c6
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/relate/generalize.rs
@@ -0,0 +1,720 @@
+//! Type generation code.
+
+use std::mem;
+
+use rustc_hash::FxHashMap;
+use rustc_type_ir::error::TypeError;
+use rustc_type_ir::inherent::{Const as _, IntoKind, Ty as _};
+use rustc_type_ir::relate::VarianceDiagInfo;
+use rustc_type_ir::{
+ AliasRelationDirection, AliasTyKind, ConstVid, InferConst, InferCtxtLike, InferTy, RegionKind,
+ TermKind, TyVid, UniverseIndex, Variance,
+};
+use rustc_type_ir::{Interner, TypeVisitable, TypeVisitableExt};
+use tracing::{debug, instrument, warn};
+
+use super::{
+ PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation,
+};
+use crate::next_solver::infer::type_variable::TypeVariableValue;
+use crate::next_solver::infer::unify_key::ConstVariableValue;
+use crate::next_solver::infer::{InferCtxt, relate};
+use crate::next_solver::util::MaxUniverse;
+use crate::next_solver::{
+ AliasTy, Binder, ClauseKind, Const, ConstKind, DbInterner, GenericArgs, PredicateKind,
+ ProjectionPredicate, Region, SolverDefId, Term, TermVid, Ty, TyKind, TypingMode,
+ UnevaluatedConst,
+};
+
+impl<'db> InferCtxt<'db> {
+ /// The idea is that we should ensure that the type variable `target_vid`
+ /// is equal to, a subtype of, or a supertype of `source_ty`.
+ ///
+ /// For this, we will instantiate `target_vid` with a *generalized* version
+ /// of `source_ty`. Generalization introduces other inference variables wherever
+ /// subtyping could occur. This also does the occurs checks, detecting whether
+ /// instantiating `target_vid` would result in a cyclic type. We eagerly error
+ /// in this case.
+ ///
+ /// This is *not* expected to be used anywhere except for an implementation of
+ /// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all
+ /// other usecases (i.e. setting the value of a type var).
+ #[instrument(level = "debug", skip(self, relation))]
+ pub fn instantiate_ty_var<R: PredicateEmittingRelation<InferCtxt<'db>>>(
+ &self,
+ relation: &mut R,
+ target_is_expected: bool,
+ target_vid: TyVid,
+ instantiation_variance: Variance,
+ source_ty: Ty<'db>,
+ ) -> RelateResult<'db, ()> {
+ debug_assert!(self.inner.borrow_mut().type_variables().probe(target_vid).is_unknown());
+
+ // Generalize `source_ty` depending on the current variance. As an example, assume
+ // `?target <: &'x ?1`, where `'x` is some free region and `?1` is an inference
+ // variable.
+ //
+ // Then the `generalized_ty` would be `&'?2 ?3`, where `'?2` and `?3` are fresh
+ // region/type inference variables.
+ //
+ // We then relate `generalized_ty <: source_ty`, adding constraints like `'x: '?2` and
+ // `?1 <: ?3`.
+ let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } = self
+ .generalize(
+ relation.structurally_relate_aliases(),
+ target_vid,
+ instantiation_variance,
+ source_ty,
+ )?;
+
+ // Constrain `b_vid` to the generalized type `generalized_ty`.
+ if let TyKind::Infer(InferTy::TyVar(generalized_vid)) = generalized_ty.kind() {
+ self.inner.borrow_mut().type_variables().equate(target_vid, generalized_vid);
+ } else {
+ self.inner.borrow_mut().type_variables().instantiate(target_vid, generalized_ty);
+ }
+
+ // See the comment on `Generalization::has_unconstrained_ty_var`.
+ if has_unconstrained_ty_var {
+ relation.register_predicates([ClauseKind::WellFormed(generalized_ty.into())]);
+ }
+
+ // Finally, relate `generalized_ty` to `source_ty`, as described in previous comment.
+ //
+ // FIXME(#16847): This code is non-ideal because all these subtype
+ // relations wind up attributed to the same spans. We need
+ // to associate causes/spans with each of the relations in
+ // the stack to get this right.
+ if generalized_ty.is_ty_var() {
+ // This happens for cases like `<?0 as Trait>::Assoc == ?0`.
+ // We can't instantiate `?0` here as that would result in a
+ // cyclic type. We instead delay the unification in case
+ // the alias can be normalized to something which does not
+ // mention `?0`.
+ let (lhs, rhs, direction) = match instantiation_variance {
+ Variance::Invariant => {
+ (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Equate)
+ }
+ Variance::Covariant => {
+ (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Subtype)
+ }
+ Variance::Contravariant => {
+ (source_ty.into(), generalized_ty.into(), AliasRelationDirection::Subtype)
+ }
+ Variance::Bivariant => unreachable!("bivariant generalization"),
+ };
+
+ relation.register_predicates([PredicateKind::AliasRelate(lhs, rhs, direction)]);
+ } else {
+ // NOTE: The `instantiation_variance` is not the same variance as
+ // used by the relation. When instantiating `b`, `target_is_expected`
+ // is flipped and the `instantiation_variance` is also flipped. To
+ // constrain the `generalized_ty` while using the original relation,
+ // we therefore only have to flip the arguments.
+ //
+ // ```ignore (not code)
+ // ?a rel B
+ // instantiate_ty_var(?a, B) # expected and variance not flipped
+ // B' rel B
+ // ```
+ // or
+ // ```ignore (not code)
+ // A rel ?b
+ // instantiate_ty_var(?b, A) # expected and variance flipped
+ // A rel A'
+ // ```
+ if target_is_expected {
+ relation.relate(generalized_ty, source_ty)?;
+ } else {
+ debug!("flip relation");
+ relation.relate(source_ty, generalized_ty)?;
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Instantiates the const variable `target_vid` with the given constant.
+ ///
+ /// This also tests if the given const `ct` contains an inference variable which was previously
+ /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct`
+ /// would result in an infinite type as we continuously replace an inference variable
+ /// in `ct` with `ct` itself.
+ ///
+ /// This is especially important as unevaluated consts use their parents generics.
+ /// They therefore often contain unused args, making these errors far more likely.
+ ///
+ /// A good example of this is the following:
+ ///
+ /// ```compile_fail,E0308
+ /// #![feature(generic_const_exprs)]
+ ///
+ /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] {
+ /// todo!()
+ /// }
+ ///
+ /// fn main() {
+ /// let mut arr = Default::default();
+ /// arr = bind(arr);
+ /// }
+ /// ```
+ ///
+ /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics
+ /// of `fn bind` (meaning that its args contain `N`).
+ ///
+ /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`.
+ /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`.
+ ///
+ /// As `3 + 4` contains `N` in its args, this must not succeed.
+ ///
+ /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant.
+ #[instrument(level = "debug", skip(self, relation))]
+ pub(crate) fn instantiate_const_var<R: PredicateEmittingRelation<InferCtxt<'db>>>(
+ &self,
+ relation: &mut R,
+ target_is_expected: bool,
+ target_vid: ConstVid,
+ source_ct: Const<'db>,
+ ) -> RelateResult<'db, ()> {
+ // FIXME(generic_const_exprs): Occurs check failures for unevaluated
+ // constants and generic expressions are not yet handled correctly.
+ let Generalization { value_may_be_infer: generalized_ct, has_unconstrained_ty_var } = self
+ .generalize(
+ relation.structurally_relate_aliases(),
+ target_vid,
+ Variance::Invariant,
+ source_ct,
+ )?;
+
+ debug_assert!(!generalized_ct.is_ct_infer());
+ if has_unconstrained_ty_var {
+ panic!("unconstrained ty var when generalizing `{source_ct:?}`");
+ }
+
+ self.inner
+ .borrow_mut()
+ .const_unification_table()
+ .union_value(target_vid, ConstVariableValue::Known { value: generalized_ct });
+
+ // Make sure that the order is correct when relating the
+ // generalized const and the source.
+ if target_is_expected {
+ relation.relate_with_variance(
+ Variance::Invariant,
+ VarianceDiagInfo::default(),
+ generalized_ct,
+ source_ct,
+ )?;
+ } else {
+ relation.relate_with_variance(
+ Variance::Invariant,
+ VarianceDiagInfo::default(),
+ source_ct,
+ generalized_ct,
+ )?;
+ }
+
+ Ok(())
+ }
+
+ /// Attempts to generalize `source_term` for the type variable `target_vid`.
+ /// This checks for cycles -- that is, whether `source_term` references `target_vid`.
+ fn generalize<T: Into<Term<'db>> + Relate<DbInterner<'db>>>(
+ &self,
+ structurally_relate_aliases: StructurallyRelateAliases,
+ target_vid: impl Into<TermVid>,
+ ambient_variance: Variance,
+ source_term: T,
+ ) -> RelateResult<'db, Generalization<T>> {
+ assert!(!source_term.clone().has_escaping_bound_vars());
+ let (for_universe, root_vid) = match target_vid.into() {
+ TermVid::Ty(ty_vid) => {
+ (self.probe_ty_var(ty_vid).unwrap_err(), TermVid::Ty(self.root_var(ty_vid)))
+ }
+ TermVid::Const(ct_vid) => (
+ self.probe_const_var(ct_vid).unwrap_err(),
+ TermVid::Const(self.inner.borrow_mut().const_unification_table().find(ct_vid).vid),
+ ),
+ };
+
+ let mut generalizer = Generalizer {
+ infcx: self,
+ structurally_relate_aliases,
+ root_vid,
+ for_universe,
+ root_term: source_term.into(),
+ ambient_variance,
+ in_alias: false,
+ cache: Default::default(),
+ has_unconstrained_ty_var: false,
+ };
+
+ let value_may_be_infer = generalizer.relate(source_term, source_term)?;
+ let has_unconstrained_ty_var = generalizer.has_unconstrained_ty_var;
+ Ok(Generalization { value_may_be_infer, has_unconstrained_ty_var })
+ }
+}
+
+/// The "generalizer" is used when handling inference variables.
+///
+/// The basic strategy for handling a constraint like `?A <: B` is to
+/// apply a "generalization strategy" to the term `B` -- this replaces
+/// all the lifetimes in the term `B` with fresh inference variables.
+/// (You can read more about the strategy in this [blog post].)
+///
+/// As an example, if we had `?A <: &'x u32`, we would generalize `&'x
+/// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the
+/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which
+/// establishes `'0: 'x` as a constraint.
+///
+/// [blog post]: https://is.gd/0hKvIr
+struct Generalizer<'me, 'db> {
+ infcx: &'me InferCtxt<'db>,
+
+ /// Whether aliases should be related structurally. If not, we have to
+ /// be careful when generalizing aliases.
+ structurally_relate_aliases: StructurallyRelateAliases,
+
+ /// The vid of the type variable that is in the process of being
+ /// instantiated. If we find this within the value we are folding,
+ /// that means we would have created a cyclic value.
+ root_vid: TermVid,
+
+ /// The universe of the type variable that is in the process of being
+ /// instantiated. If we find anything that this universe cannot name,
+ /// we reject the relation.
+ for_universe: UniverseIndex,
+
+ /// The root term (const or type) we're generalizing. Used for cycle errors.
+ root_term: Term<'db>,
+
+ /// After we generalize this type, we are going to relate it to
+ /// some other type. What will be the variance at this point?
+ ambient_variance: Variance,
+
+ /// This is set once we're generalizing the arguments of an alias.
+ ///
+ /// This is necessary to correctly handle
+ /// `<T as Bar<<?0 as Foo>::Assoc>::Assoc == ?0`. This equality can
+ /// hold by either normalizing the outer or the inner associated type.
+ in_alias: bool,
+
+ cache: FxHashMap<(Ty<'db>, Variance, bool), Ty<'db>>,
+
+ /// See the field `has_unconstrained_ty_var` in `Generalization`.
+ has_unconstrained_ty_var: bool,
+}
+
+impl<'db> Generalizer<'_, 'db> {
+ /// Create an error that corresponds to the term kind in `root_term`
+ fn cyclic_term_error(&self) -> TypeError<DbInterner<'db>> {
+ match self.root_term.kind() {
+ TermKind::Ty(ty) => TypeError::CyclicTy(ty),
+ TermKind::Const(ct) => TypeError::CyclicConst(ct),
+ }
+ }
+
+ /// Create a new type variable in the universe of the target when
+ /// generalizing an alias. This has to set `has_unconstrained_ty_var`
+ /// if we're currently in a bivariant context.
+ fn next_ty_var_for_alias(&mut self) -> Ty<'db> {
+ self.has_unconstrained_ty_var |= self.ambient_variance == Variance::Bivariant;
+ self.infcx.next_ty_var_in_universe(self.for_universe)
+ }
+
+ /// An occurs check failure inside of an alias does not mean
+ /// that the types definitely don't unify. We may be able
+ /// to normalize the alias after all.
+ ///
+ /// We handle this by lazily equating the alias and generalizing
+ /// it to an inference variable. In the new solver, we always
+ /// generalize to an infer var unless the alias contains escaping
+ /// bound variables.
+ ///
+ /// Correctly handling aliases with escaping bound variables is
+ /// difficult and currently incomplete in two opposite ways:
+ /// - if we get an occurs check failure in the alias, replace it with a new infer var.
+ /// This causes us to later emit an alias-relate goal and is incomplete in case the
+ /// alias normalizes to type containing one of the bound variables.
+ /// - if the alias contains an inference variable not nameable by `for_universe`, we
+ /// continue generalizing the alias. This ends up pulling down the universe of the
+ /// inference variable and is incomplete in case the alias would normalize to a type
+ /// which does not mention that inference variable.
+ fn generalize_alias_ty(
+ &mut self,
+ alias: AliasTy<'db>,
+ ) -> Result<Ty<'db>, TypeError<DbInterner<'db>>> {
+ // We do not eagerly replace aliases with inference variables if they have
+ // escaping bound vars, see the method comment for details. However, when we
+ // are inside of an alias with escaping bound vars replacing nested aliases
+ // with inference variables can cause incorrect ambiguity.
+ //
+ // cc trait-system-refactor-initiative#110
+ if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() && !self.in_alias {
+ return Ok(self.next_ty_var_for_alias());
+ }
+
+ let is_nested_alias = mem::replace(&mut self.in_alias, true);
+ let result = match self.relate(alias, alias) {
+ Ok(alias) => Ok(alias.to_ty(self.cx())),
+ Err(e) => {
+ if is_nested_alias {
+ return Err(e);
+ } else {
+ let mut visitor = MaxUniverse::new();
+ alias.visit_with(&mut visitor);
+ let infer_replacement_is_complete =
+ self.for_universe.can_name(visitor.max_universe())
+ && !alias.has_escaping_bound_vars();
+ if !infer_replacement_is_complete {
+ warn!("may incompletely handle alias type: {alias:?}");
+ }
+
+ debug!("generalization failure in alias");
+ Ok(self.next_ty_var_for_alias())
+ }
+ }
+ };
+ self.in_alias = is_nested_alias;
+ result
+ }
+}
+
+impl<'db> TypeRelation<DbInterner<'db>> for Generalizer<'_, 'db> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.infcx.interner
+ }
+
+ fn relate_item_args(
+ &mut self,
+ item_def_id: SolverDefId,
+ a_arg: GenericArgs<'db>,
+ b_arg: GenericArgs<'db>,
+ ) -> RelateResult<'db, GenericArgs<'db>> {
+ if self.ambient_variance == Variance::Invariant {
+ // Avoid fetching the variance if we are in an invariant
+ // context; no need, and it can induce dependency cycles
+ // (e.g., #41849).
+ relate::relate_args_invariantly(self, a_arg, b_arg)
+ } else {
+ let tcx = self.cx();
+ let opt_variances = tcx.variances_of(item_def_id);
+ relate::relate_args_with_variances(
+ self,
+ item_def_id,
+ opt_variances,
+ a_arg,
+ b_arg,
+ false,
+ )
+ }
+ }
+
+ #[instrument(level = "debug", skip(self, variance, b), ret)]
+ fn relate_with_variance<T: Relate<DbInterner<'db>>>(
+ &mut self,
+ variance: Variance,
+ _info: VarianceDiagInfo<DbInterner<'db>>,
+ a: T,
+ b: T,
+ ) -> RelateResult<'db, T> {
+ let old_ambient_variance = self.ambient_variance;
+ self.ambient_variance = self.ambient_variance.xform(variance);
+ debug!(?self.ambient_variance, "new ambient variance");
+ // Recursive calls to `relate` can overflow the stack. For example a deeper version of
+ // `ui/associated-consts/issue-93775.rs`.
+ let r = self.relate(a, b);
+ self.ambient_variance = old_ambient_variance;
+ r
+ }
+
+ #[instrument(level = "debug", skip(self, t2), ret)]
+ fn tys(&mut self, t: Ty<'db>, t2: Ty<'db>) -> RelateResult<'db, Ty<'db>> {
+ assert_eq!(t, t2); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
+
+ if let Some(result) = self.cache.get(&(t, self.ambient_variance, self.in_alias)) {
+ return Ok(*result);
+ }
+
+ // Check to see whether the type we are generalizing references
+ // any other type variable related to `vid` via
+ // subtyping. This is basically our "occurs check", preventing
+ // us from creating infinitely sized types.
+ let g = match t.kind() {
+ TyKind::Infer(
+ InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_),
+ ) => {
+ panic!("unexpected infer type: {t:?}")
+ }
+
+ TyKind::Infer(InferTy::TyVar(vid)) => {
+ let mut inner = self.infcx.inner.borrow_mut();
+ let vid = inner.type_variables().root_var(vid);
+ if TermVid::Ty(vid) == self.root_vid {
+ // If sub-roots are equal, then `root_vid` and
+ // `vid` are related via subtyping.
+ Err(self.cyclic_term_error())
+ } else {
+ let probe = inner.type_variables().probe(vid);
+ match probe {
+ TypeVariableValue::Known { value: u } => {
+ drop(inner);
+ self.relate(u, u)
+ }
+ TypeVariableValue::Unknown { universe } => {
+ match self.ambient_variance {
+ // Invariant: no need to make a fresh type variable
+ // if we can name the universe.
+ Variance::Invariant => {
+ if self.for_universe.can_name(universe) {
+ return Ok(t);
+ }
+ }
+
+ // Bivariant: make a fresh var, but remember that
+ // it is unconstrained. See the comment in
+ // `Generalization`.
+ Variance::Bivariant => self.has_unconstrained_ty_var = true,
+
+ // Co/contravariant: this will be
+ // sufficiently constrained later on.
+ Variance::Covariant | Variance::Contravariant => (),
+ }
+
+ let origin = inner.type_variables().var_origin(vid);
+ let new_var_id =
+ inner.type_variables().new_var(self.for_universe, origin);
+ // If we're in the new solver and create a new inference
+ // variable inside of an alias we eagerly constrain that
+ // inference variable to prevent unexpected ambiguity errors.
+ //
+ // This is incomplete as it pulls down the universe of the
+ // original inference variable, even though the alias could
+ // normalize to a type which does not refer to that type at
+ // all. I don't expect this to cause unexpected errors in
+ // practice.
+ //
+ // We only need to do so for type and const variables, as
+ // region variables do not impact normalization, and will get
+ // correctly constrained by `AliasRelate` later on.
+ //
+ // cc trait-system-refactor-initiative#108
+ if self.infcx.next_trait_solver()
+ && !matches!(
+ self.infcx.typing_mode_unchecked(),
+ TypingMode::Coherence
+ )
+ && self.in_alias
+ {
+ inner.type_variables().equate(vid, new_var_id);
+ }
+
+ debug!("replacing original vid={:?} with new={:?}", vid, new_var_id);
+ Ok(Ty::new_var(self.infcx.interner, new_var_id))
+ }
+ }
+ }
+ }
+
+ TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => {
+ // No matter what mode we are in,
+ // integer/floating-point types must be equal to be
+ // relatable.
+ Ok(t)
+ }
+
+ TyKind::Placeholder(placeholder) => {
+ if self.for_universe.can_name(placeholder.universe) {
+ Ok(t)
+ } else {
+ debug!(
+ "root universe {:?} cannot name placeholder in universe {:?}",
+ self.for_universe, placeholder.universe
+ );
+ Err(TypeError::Mismatch)
+ }
+ }
+
+ TyKind::Alias(_, data) => match self.structurally_relate_aliases {
+ StructurallyRelateAliases::No => self.generalize_alias_ty(data),
+ StructurallyRelateAliases::Yes => relate::structurally_relate_tys(self, t, t),
+ },
+
+ _ => relate::structurally_relate_tys(self, t, t),
+ }?;
+
+ self.cache.insert((t, self.ambient_variance, self.in_alias), g);
+ Ok(g)
+ }
+
+ #[instrument(level = "debug", skip(self, r2), ret)]
+ fn regions(&mut self, r: Region<'db>, r2: Region<'db>) -> RelateResult<'db, Region<'db>> {
+ assert_eq!(r, r2); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
+
+ match r.kind() {
+ // Never make variables for regions bound within the type itself,
+ // nor for erased regions.
+ RegionKind::ReBound(..) | RegionKind::ReErased => {
+ return Ok(r);
+ }
+
+ // It doesn't really matter for correctness if we generalize ReError,
+ // since we're already on a doomed compilation path.
+ RegionKind::ReError(_) => {
+ return Ok(r);
+ }
+
+ RegionKind::RePlaceholder(..)
+ | RegionKind::ReVar(..)
+ | RegionKind::ReStatic
+ | RegionKind::ReEarlyParam(..)
+ | RegionKind::ReLateParam(..) => {
+ // see common code below
+ }
+ }
+
+ // If we are in an invariant context, we can re-use the region
+ // as is, unless it happens to be in some universe that we
+ // can't name.
+ if let Variance::Invariant = self.ambient_variance {
+ let r_universe = self.infcx.universe_of_region(r);
+ if self.for_universe.can_name(r_universe) {
+ return Ok(r);
+ }
+ }
+
+ Ok(self.infcx.next_region_var_in_universe(self.for_universe))
+ }
+
+ #[instrument(level = "debug", skip(self, c2), ret)]
+ fn consts(&mut self, c: Const<'db>, c2: Const<'db>) -> RelateResult<'db, Const<'db>> {
+ assert_eq!(c, c2); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
+
+ match c.kind() {
+ ConstKind::Infer(InferConst::Var(vid)) => {
+ // If root const vids are equal, then `root_vid` and
+ // `vid` are related and we'd be inferring an infinitely
+ // deep const.
+ if TermVid::Const(
+ self.infcx.inner.borrow_mut().const_unification_table().find(vid).vid,
+ ) == self.root_vid
+ {
+ return Err(self.cyclic_term_error());
+ }
+
+ let mut inner = self.infcx.inner.borrow_mut();
+ let variable_table = &mut inner.const_unification_table();
+ match variable_table.probe_value(vid) {
+ ConstVariableValue::Known { value: u } => {
+ drop(inner);
+ self.relate(u, u)
+ }
+ ConstVariableValue::Unknown { origin, universe } => {
+ if self.for_universe.can_name(universe) {
+ Ok(c)
+ } else {
+ let new_var_id = variable_table
+ .new_key(ConstVariableValue::Unknown {
+ origin,
+ universe: self.for_universe,
+ })
+ .vid;
+
+ // See the comment for type inference variables
+ // for more details.
+ if self.infcx.next_trait_solver()
+ && !matches!(
+ self.infcx.typing_mode_unchecked(),
+ TypingMode::Coherence
+ )
+ && self.in_alias
+ {
+ variable_table.union(vid, new_var_id);
+ }
+ Ok(Const::new_var(self.infcx.interner, new_var_id))
+ }
+ }
+ }
+ }
+ // FIXME: Unevaluated constants are also not rigid, so the current
+ // approach of always relating them structurally is incomplete.
+ //
+ // FIXME: remove this branch once `structurally_relate_consts` is fully
+ // structural.
+ ConstKind::Unevaluated(UnevaluatedConst { def, args }) => {
+ let args = self.relate_with_variance(
+ Variance::Invariant,
+ VarianceDiagInfo::default(),
+ args,
+ args,
+ )?;
+ Ok(Const::new_unevaluated(self.infcx.interner, UnevaluatedConst { def, args }))
+ }
+ ConstKind::Placeholder(placeholder) => {
+ if self.for_universe.can_name(placeholder.universe) {
+ Ok(c)
+ } else {
+ debug!(
+ "root universe {:?} cannot name placeholder in universe {:?}",
+ self.for_universe, placeholder.universe
+ );
+ Err(TypeError::Mismatch)
+ }
+ }
+ _ => relate::structurally_relate_consts(self, c, c),
+ }
+ }
+
+ #[instrument(level = "debug", skip(self), ret)]
+ fn binders<T>(
+ &mut self,
+ a: Binder<'db, T>,
+ _: Binder<'db, T>,
+ ) -> RelateResult<'db, Binder<'db, T>>
+ where
+ T: Relate<DbInterner<'db>>,
+ {
+ let result = self.relate(a.skip_binder(), a.skip_binder())?;
+ Ok(a.rebind(result))
+ }
+}
+
+/// Result from a generalization operation. This includes
+/// not only the generalized type, but also a bool flag
+/// indicating whether further WF checks are needed.
+#[derive(Debug)]
+struct Generalization<T> {
+ /// When generalizing `<?0 as Trait>::Assoc` or
+ /// `<T as Bar<<?0 as Foo>::Assoc>>::Assoc`
+ /// for `?0` generalization returns an inference
+ /// variable.
+ ///
+ /// This has to be handled wotj care as it can
+ /// otherwise very easily result in infinite
+ /// recursion.
+ pub value_may_be_infer: T,
+
+ /// In general, we do not check whether all types which occur during
+ /// type checking are well-formed. We only check wf of user-provided types
+ /// and when actually using a type, e.g. for method calls.
+ ///
+ /// This means that when subtyping, we may end up with unconstrained
+ /// inference variables if a generalized type has bivariant parameters.
+ /// A parameter may only be bivariant if it is constrained by a projection
+ /// bound in a where-clause. As an example, imagine a type:
+ ///
+ /// struct Foo<A, B> where A: Iterator<Item = B> {
+ /// data: A
+ /// }
+ ///
+ /// here, `A` will be covariant, but `B` is unconstrained.
+ ///
+ /// However, whatever it is, for `Foo` to be WF, it must be equal to `A::Item`.
+ /// If we have an input `Foo<?A, ?B>`, then after generalization we will wind
+ /// up with a type like `Foo<?C, ?D>`. When we enforce `Foo<?A, ?B> <: Foo<?C, ?D>`,
+ /// we will wind up with the requirement that `?A <: ?C`, but no particular
+ /// relationship between `?B` and `?D` (after all, these types may be completely
+ /// different). If we do nothing else, this may mean that `?D` goes unconstrained
+ /// (as in #41677). To avoid this we emit a `WellFormed` obligation in these cases.
+ pub has_unconstrained_ty_var: bool,
+}
diff --git a/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs b/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs
new file mode 100644
index 0000000..bb80c51
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs
@@ -0,0 +1,89 @@
+//! Helper routines for higher-ranked things. See the `doc` module at
+//! the end of the file for details.
+
+use rustc_type_ir::TypeFoldable;
+use rustc_type_ir::{BoundVar, UniverseIndex};
+use tracing::{debug, instrument};
+
+use super::RelateResult;
+use crate::next_solver::fold::FnMutDelegate;
+use crate::next_solver::infer::InferCtxt;
+use crate::next_solver::infer::snapshot::CombinedSnapshot;
+use crate::next_solver::{
+ Binder, BoundRegion, BoundTy, Const, DbInterner, PlaceholderConst, PlaceholderRegion,
+ PlaceholderTy, Region, Ty,
+};
+
+impl<'db> InferCtxt<'db> {
+ /// Replaces all bound variables (lifetimes, types, and constants) bound by
+ /// `binder` with placeholder variables in a new universe. This means that the
+ /// new placeholders can only be named by inference variables created after
+ /// this method has been called.
+ ///
+ /// This is the first step of checking subtyping when higher-ranked things are involved.
+ /// For more details visit the relevant sections of the [rustc dev guide].
+ ///
+ /// `fn enter_forall` should be preferred over this method.
+ ///
+ /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
+ #[instrument(level = "debug", skip(self), ret)]
+ pub fn enter_forall_and_leak_universe<T>(&self, binder: Binder<'db, T>) -> T
+ where
+ T: TypeFoldable<DbInterner<'db>> + Clone,
+ {
+ if let Some(inner) = binder.clone().no_bound_vars() {
+ return inner;
+ }
+
+ let next_universe = self.create_next_universe();
+
+ let delegate = FnMutDelegate {
+ regions: &mut |br: BoundRegion| {
+ Region::new_placeholder(
+ self.interner,
+ PlaceholderRegion { universe: next_universe, bound: br },
+ )
+ },
+ types: &mut |bound_ty: BoundTy| {
+ Ty::new_placeholder(
+ self.interner,
+ PlaceholderTy { universe: next_universe, bound: bound_ty },
+ )
+ },
+ consts: &mut |bound_var: BoundVar| {
+ Const::new_placeholder(
+ self.interner,
+ PlaceholderConst { universe: next_universe, bound: bound_var },
+ )
+ },
+ };
+
+ debug!(?next_universe);
+ self.interner.replace_bound_vars_uncached(binder, delegate)
+ }
+
+ /// Replaces all bound variables (lifetimes, types, and constants) bound by
+ /// `binder` with placeholder variables in a new universe and then calls the
+ /// closure `f` with the instantiated value. The new placeholders can only be
+ /// named by inference variables created inside of the closure `f` or afterwards.
+ ///
+ /// This is the first step of checking subtyping when higher-ranked things are involved.
+ /// For more details visit the relevant sections of the [rustc dev guide].
+ ///
+ /// This method should be preferred over `fn enter_forall_and_leak_universe`.
+ ///
+ /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
+ #[instrument(level = "debug", skip(self, f))]
+ pub fn enter_forall<T, U>(&self, forall: Binder<'db, T>, f: impl FnOnce(T) -> U) -> U
+ where
+ T: TypeFoldable<DbInterner<'db>> + Clone,
+ {
+ // FIXME: currently we do nothing to prevent placeholders with the new universe being
+ // used after exiting `f`. For example region subtyping can result in outlives constraints
+ // that name placeholders created in this function. Nested goals from type relations can
+ // also contain placeholders created by this function.
+ let value = self.enter_forall_and_leak_universe(forall);
+ debug!(?value);
+ f(value)
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/relate/mod.rs b/crates/hir-ty/src/next_solver/infer/relate/mod.rs
new file mode 100644
index 0000000..836ae39
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/relate/mod.rs
@@ -0,0 +1,13 @@
+//! This module contains the definitions of most `TypeRelation`s in the type system
+//! (except for some relations used for diagnostics and heuristics in the compiler).
+//! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc).
+
+pub use rustc_type_ir::relate::combine::PredicateEmittingRelation;
+pub use rustc_type_ir::relate::*;
+
+use crate::next_solver::DbInterner;
+
+mod generalize;
+mod higher_ranked;
+
+pub type RelateResult<'db, T> = rustc_type_ir::relate::RelateResult<DbInterner<'db>, T>;
diff --git a/crates/hir-ty/src/next_solver/infer/resolve.rs b/crates/hir-ty/src/next_solver/infer/resolve.rs
new file mode 100644
index 0000000..84338ad
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/resolve.rs
@@ -0,0 +1,62 @@
+//! Things for resolving vars in the infer context of the next-trait-solver.
+
+use rustc_type_ir::{
+ ConstKind, FallibleTypeFolder, InferConst, InferTy, RegionKind, TyKind, TypeFoldable,
+ TypeFolder, TypeSuperFoldable, TypeVisitableExt, data_structures::DelayedMap,
+ inherent::IntoKind,
+};
+
+use crate::next_solver::{Const, DbInterner, Region, Ty};
+
+use super::{FixupError, FixupResult, InferCtxt};
+
+///////////////////////////////////////////////////////////////////////////
+// OPPORTUNISTIC VAR RESOLVER
+
+/// The opportunistic resolver can be used at any time. It simply replaces
+/// type/const variables that have been unified with the things they have
+/// been unified with (similar to `shallow_resolve`, but deep). This is
+/// useful for printing messages etc but also required at various
+/// points for correctness.
+pub struct OpportunisticVarResolver<'a, 'db> {
+ infcx: &'a InferCtxt<'db>,
+ /// We're able to use a cache here as the folder does
+ /// not have any mutable state.
+ cache: DelayedMap<Ty<'db>, Ty<'db>>,
+}
+
+impl<'a, 'db> OpportunisticVarResolver<'a, 'db> {
+ #[inline]
+ pub fn new(infcx: &'a InferCtxt<'db>) -> Self {
+ OpportunisticVarResolver { infcx, cache: Default::default() }
+ }
+}
+
+impl<'a, 'db> TypeFolder<DbInterner<'db>> for OpportunisticVarResolver<'a, 'db> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.infcx.interner
+ }
+
+ #[inline]
+ fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
+ if !t.has_non_region_infer() {
+ t // micro-optimize -- if there is nothing in this type that this fold affects...
+ } else if let Some(ty) = self.cache.get(&t) {
+ *ty
+ } else {
+ let shallow = self.infcx.shallow_resolve(t);
+ let res = shallow.super_fold_with(self);
+ assert!(self.cache.insert(t, res));
+ res
+ }
+ }
+
+ fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
+ if !ct.has_non_region_infer() {
+ ct // micro-optimize -- if there is nothing in this const that this fold affects...
+ } else {
+ let ct = self.infcx.shallow_resolve_const(ct);
+ ct.super_fold_with(self)
+ }
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs
new file mode 100644
index 0000000..eb42620
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs
@@ -0,0 +1,111 @@
+//! Snapshotting in the infer ctxt of the next-trait-solver.
+
+use ena::undo_log::UndoLogs;
+use rustc_type_ir::UniverseIndex;
+use tracing::{debug, instrument};
+
+use super::InferCtxt;
+use super::region_constraints::RegionSnapshot;
+
+pub(crate) mod undo_log;
+
+use undo_log::{Snapshot, UndoLog};
+
+#[must_use = "once you start a snapshot, you should always consume it"]
+pub struct CombinedSnapshot {
+ pub(super) undo_snapshot: Snapshot,
+ region_constraints_snapshot: RegionSnapshot,
+ universe: UniverseIndex,
+}
+
+struct VariableLengths {
+ region_constraints_len: usize,
+ type_var_len: usize,
+ int_var_len: usize,
+ float_var_len: usize,
+ const_var_len: usize,
+}
+
+impl<'db> InferCtxt<'db> {
+ fn variable_lengths(&self) -> VariableLengths {
+ let mut inner = self.inner.borrow_mut();
+ VariableLengths {
+ region_constraints_len: inner.unwrap_region_constraints().num_region_vars(),
+ type_var_len: inner.type_variables().num_vars(),
+ int_var_len: inner.int_unification_table().len(),
+ float_var_len: inner.float_unification_table().len(),
+ const_var_len: inner.const_unification_table().len(),
+ }
+ }
+
+ pub fn in_snapshot(&self) -> bool {
+ UndoLogs::<UndoLog<'db>>::in_snapshot(&self.inner.borrow_mut().undo_log)
+ }
+
+ pub fn num_open_snapshots(&self) -> usize {
+ UndoLogs::<UndoLog<'db>>::num_open_snapshots(&self.inner.borrow_mut().undo_log)
+ }
+
+ fn start_snapshot(&self) -> CombinedSnapshot {
+ debug!("start_snapshot()");
+
+ let mut inner = self.inner.borrow_mut();
+
+ CombinedSnapshot {
+ undo_snapshot: inner.undo_log.start_snapshot(),
+ region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(),
+ universe: self.universe(),
+ }
+ }
+
+ #[instrument(skip(self, snapshot), level = "debug")]
+ fn rollback_to(&self, snapshot: CombinedSnapshot) {
+ let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot;
+
+ self.universe.set(universe);
+
+ let mut inner = self.inner.borrow_mut();
+ inner.rollback_to(undo_snapshot);
+ inner.unwrap_region_constraints().rollback_to(region_constraints_snapshot);
+ }
+
+ #[instrument(skip(self, snapshot), level = "debug")]
+ fn commit_from(&self, snapshot: CombinedSnapshot) {
+ let CombinedSnapshot { undo_snapshot, region_constraints_snapshot: _, universe: _ } =
+ snapshot;
+
+ self.inner.borrow_mut().commit(undo_snapshot);
+ }
+
+ /// Execute `f` and commit the bindings if closure `f` returns `Ok(_)`.
+ #[instrument(skip(self, f), level = "debug")]
+ pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
+ where
+ F: FnOnce(&CombinedSnapshot) -> Result<T, E>,
+ {
+ let snapshot = self.start_snapshot();
+ let r = f(&snapshot);
+ debug!("commit_if_ok() -- r.is_ok() = {}", r.is_ok());
+ match r {
+ Ok(_) => {
+ self.commit_from(snapshot);
+ }
+ Err(_) => {
+ self.rollback_to(snapshot);
+ }
+ }
+ r
+ }
+
+ /// Execute `f` then unroll any bindings it creates.
+ #[instrument(skip(self, f), level = "debug")]
+ pub fn probe<R, F>(&self, f: F) -> R
+ where
+ F: FnOnce(&CombinedSnapshot) -> R,
+ {
+ let snapshot = self.start_snapshot();
+ let r = f(&snapshot);
+ self.rollback_to(snapshot);
+ r
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs b/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs
new file mode 100644
index 0000000..0fa6421
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs
@@ -0,0 +1,198 @@
+//! Snapshotting in the infer ctxt of the next-trait-solver.
+
+use std::marker::PhantomData;
+
+use ena::snapshot_vec as sv;
+use ena::undo_log::{Rollback, UndoLogs};
+use ena::unify as ut;
+use rustc_type_ir::FloatVid;
+use rustc_type_ir::IntVid;
+use tracing::debug;
+
+use crate::next_solver::OpaqueTypeKey;
+use crate::next_solver::infer::opaque_types::OpaqueHiddenType;
+use crate::next_solver::infer::unify_key::ConstVidKey;
+use crate::next_solver::infer::unify_key::RegionVidKey;
+use crate::next_solver::infer::{InferCtxtInner, region_constraints, type_variable};
+use crate::traits;
+
+pub struct Snapshot {
+ pub(crate) undo_len: usize,
+}
+
+/// Records the "undo" data for a single operation that affects some form of inference variable.
+#[derive(Clone)]
+pub(crate) enum UndoLog<'db> {
+ DuplicateOpaqueType,
+ OpaqueTypes(OpaqueTypeKey<'db>, Option<OpaqueHiddenType<'db>>),
+ TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'db>>>),
+ ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'db>>>),
+ IntUnificationTable(sv::UndoLog<ut::Delegate<IntVid>>),
+ FloatUnificationTable(sv::UndoLog<ut::Delegate<FloatVid>>),
+ RegionConstraintCollector(region_constraints::UndoLog<'db>),
+ RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'db>>>),
+ PushRegionObligation,
+}
+
+macro_rules! impl_from {
+ ($($ctor:ident ($ty:ty),)*) => {
+ $(
+ impl<'db> From<$ty> for UndoLog<'db> {
+ fn from(x: $ty) -> Self {
+ UndoLog::$ctor(x.into())
+ }
+ }
+ )*
+ }
+}
+
+// Upcast from a single kind of "undoable action" to the general enum
+impl_from! {
+ RegionConstraintCollector(region_constraints::UndoLog<'db>),
+
+ TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'db>>>),
+ IntUnificationTable(sv::UndoLog<ut::Delegate<IntVid>>),
+ FloatUnificationTable(sv::UndoLog<ut::Delegate<FloatVid>>),
+
+ ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'db>>>),
+
+ RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'db>>>),
+}
+
+/// The Rollback trait defines how to rollback a particular action.
+impl<'db> Rollback<UndoLog<'db>> for InferCtxtInner<'db> {
+ fn reverse(&mut self, undo: UndoLog<'db>) {
+ match undo {
+ UndoLog::DuplicateOpaqueType => self.opaque_type_storage.pop_duplicate_entry(),
+ UndoLog::OpaqueTypes(key, idx) => self.opaque_type_storage.remove(key, idx),
+ UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo),
+ UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo),
+ UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo),
+ UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo),
+ UndoLog::RegionConstraintCollector(undo) => {
+ self.region_constraint_storage.as_mut().unwrap().reverse(undo)
+ }
+ UndoLog::RegionUnificationTable(undo) => {
+ self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo)
+ }
+ UndoLog::PushRegionObligation => {
+ self.region_obligations.pop();
+ }
+ }
+ }
+}
+
+/// The combined undo log for all the various unification tables. For each change to the storage
+/// for any kind of inference variable, we record an UndoLog entry in the vector here.
+#[derive(Clone, Default)]
+pub(crate) struct InferCtxtUndoLogs<'db> {
+ logs: Vec<UndoLog<'db>>,
+ num_open_snapshots: usize,
+}
+
+/// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any
+/// action that is convertible into an UndoLog (per the From impls above).
+impl<'db, T> UndoLogs<T> for InferCtxtUndoLogs<'db>
+where
+ UndoLog<'db>: From<T>,
+{
+ #[inline]
+ fn num_open_snapshots(&self) -> usize {
+ self.num_open_snapshots
+ }
+
+ #[inline]
+ fn push(&mut self, undo: T) {
+ if self.in_snapshot() {
+ self.logs.push(undo.into())
+ }
+ }
+
+ fn clear(&mut self) {
+ self.logs.clear();
+ self.num_open_snapshots = 0;
+ }
+
+ fn extend<J>(&mut self, undos: J)
+ where
+ Self: Sized,
+ J: IntoIterator<Item = T>,
+ {
+ if self.in_snapshot() {
+ self.logs.extend(undos.into_iter().map(UndoLog::from))
+ }
+ }
+}
+
+impl<'db> InferCtxtInner<'db> {
+ pub fn rollback_to(&mut self, snapshot: Snapshot) {
+ debug!("rollback_to({})", snapshot.undo_len);
+ self.undo_log.assert_open_snapshot(&snapshot);
+
+ while self.undo_log.logs.len() > snapshot.undo_len {
+ let undo = self.undo_log.logs.pop().unwrap();
+ self.reverse(undo);
+ }
+
+ self.type_variable_storage.finalize_rollback();
+
+ if self.undo_log.num_open_snapshots == 1 {
+ // After the root snapshot the undo log should be empty.
+ assert!(snapshot.undo_len == 0);
+ assert!(self.undo_log.logs.is_empty());
+ }
+
+ self.undo_log.num_open_snapshots -= 1;
+ }
+
+ pub fn commit(&mut self, snapshot: Snapshot) {
+ debug!("commit({})", snapshot.undo_len);
+
+ if self.undo_log.num_open_snapshots == 1 {
+ // The root snapshot. It's safe to clear the undo log because
+ // there's no snapshot further out that we might need to roll back
+ // to.
+ assert!(snapshot.undo_len == 0);
+ self.undo_log.logs.clear();
+ }
+
+ self.undo_log.num_open_snapshots -= 1;
+ }
+}
+
+impl<'db> InferCtxtUndoLogs<'db> {
+ pub(crate) fn start_snapshot(&mut self) -> Snapshot {
+ self.num_open_snapshots += 1;
+ Snapshot { undo_len: self.logs.len() }
+ }
+
+ pub(crate) fn region_constraints_in_snapshot(
+ &self,
+ s: &Snapshot,
+ ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'db>> + Clone {
+ self.logs[s.undo_len..].iter().filter_map(|log| match log {
+ UndoLog::RegionConstraintCollector(log) => Some(log),
+ _ => None,
+ })
+ }
+
+ fn assert_open_snapshot(&self, snapshot: &Snapshot) {
+ // Failures here may indicate a failure to follow a stack discipline.
+ assert!(self.logs.len() >= snapshot.undo_len);
+ assert!(self.num_open_snapshots > 0);
+ }
+}
+
+impl<'db> std::ops::Index<usize> for InferCtxtUndoLogs<'db> {
+ type Output = UndoLog<'db>;
+
+ fn index(&self, key: usize) -> &Self::Output {
+ &self.logs[key]
+ }
+}
+
+impl<'db> std::ops::IndexMut<usize> for InferCtxtUndoLogs<'db> {
+ fn index_mut(&mut self, key: usize) -> &mut Self::Output {
+ &mut self.logs[key]
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/traits.rs b/crates/hir-ty/src/next_solver/infer/traits.rs
new file mode 100644
index 0000000..f1df806
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/traits.rs
@@ -0,0 +1,175 @@
+//! Trait Resolution. See the [rustc-dev-guide] for more information on how this works.
+//!
+//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
+
+use std::{
+ cmp,
+ hash::{Hash, Hasher},
+};
+
+use rustc_type_ir::{
+ PredicatePolarity, Upcast,
+ solve::{Certainty, NoSolution},
+};
+
+use crate::next_solver::{
+ Binder, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, SolverDefId, TraitPredicate,
+ Ty,
+};
+
+use super::InferCtxt;
+
+/// The reason why we incurred this obligation; used for error reporting.
+///
+/// Non-misc `ObligationCauseCode`s are stored on the heap. This gives the
+/// best trade-off between keeping the type small (which makes copies cheaper)
+/// while not doing too many heap allocations.
+///
+/// We do not want to intern this as there are a lot of obligation causes which
+/// only live for a short period of time.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct ObligationCause {
+ /// The ID of the fn body that triggered this obligation. This is
+ /// used for region obligations to determine the precise
+ /// environment in which the region obligation should be evaluated
+ /// (in particular, closures can add new assumptions). See the
+ /// field `region_obligations` of the `FulfillmentContext` for more
+ /// information.
+ pub body_id: Option<SolverDefId>,
+}
+
+impl ObligationCause {
+ #[inline]
+ pub fn new(body_id: SolverDefId) -> ObligationCause {
+ ObligationCause { body_id: Some(body_id) }
+ }
+
+ #[inline(always)]
+ pub fn dummy() -> ObligationCause {
+ ObligationCause { body_id: None }
+ }
+}
+
+/// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for
+/// which the "impl_source" must be found. The process of finding an "impl_source" is
+/// called "resolving" the `Obligation`. This process consists of
+/// either identifying an `impl` (e.g., `impl Eq for i32`) that
+/// satisfies the obligation, or else finding a bound that is in
+/// scope. The eventual result is usually a `Selection` (defined below).
+#[derive(Clone, Debug)]
+pub struct Obligation<'db, T> {
+ /// The reason we have to prove this thing.
+ pub cause: ObligationCause,
+
+ /// The environment in which we should prove this thing.
+ pub param_env: ParamEnv<'db>,
+
+ /// The thing we are trying to prove.
+ pub predicate: T,
+
+ /// If we started proving this as a result of trying to prove
+ /// something else, track the total depth to ensure termination.
+ /// If this goes over a certain threshold, we abort compilation --
+ /// in such cases, we can not say whether or not the predicate
+ /// holds for certain. Stupid halting problem; such a drag.
+ pub recursion_depth: usize,
+}
+
+impl<'db, T: Copy> Obligation<'db, T> {
+ pub fn as_goal(&self) -> Goal<'db, T> {
+ Goal { param_env: self.param_env, predicate: self.predicate }
+ }
+}
+
+impl<'db, T: PartialEq> PartialEq<Obligation<'db, T>> for Obligation<'db, T> {
+ #[inline]
+ fn eq(&self, other: &Obligation<'db, T>) -> bool {
+ // Ignore `cause` and `recursion_depth`. This is a small performance
+ // win for a few crates, and a huge performance win for the crate in
+ // https://github.com/rust-lang/rustc-perf/pull/1680, which greatly
+ // stresses the trait system.
+ self.param_env == other.param_env && self.predicate == other.predicate
+ }
+}
+
+impl<'db, T: Eq> Eq for Obligation<'db, T> {}
+
+impl<'db, T: Hash> Hash for Obligation<'db, T> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ // See the comment on `Obligation::eq`.
+ self.param_env.hash(state);
+ self.predicate.hash(state);
+ }
+}
+
+impl<'db, P> From<Obligation<'db, P>> for Goal<'db, P> {
+ fn from(value: Obligation<'db, P>) -> Self {
+ Goal { param_env: value.param_env, predicate: value.predicate }
+ }
+}
+
+pub type PredicateObligation<'db> = Obligation<'db, Predicate<'db>>;
+pub type TraitObligation<'db> = Obligation<'db, TraitPredicate<'db>>;
+
+pub type PredicateObligations<'db> = Vec<PredicateObligation<'db>>;
+
+impl<'db> PredicateObligation<'db> {
+ /// Flips the polarity of the inner predicate.
+ ///
+ /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`.
+ pub fn flip_polarity(&self, tcx: DbInterner<'db>) -> Option<PredicateObligation<'db>> {
+ Some(PredicateObligation {
+ cause: self.cause.clone(),
+ param_env: self.param_env,
+ predicate: self.predicate.flip_polarity()?,
+ recursion_depth: self.recursion_depth,
+ })
+ }
+}
+
+impl<'db, O> Obligation<'db, O> {
+ pub fn new(
+ tcx: DbInterner<'db>,
+ cause: ObligationCause,
+ param_env: ParamEnv<'db>,
+ predicate: impl Upcast<DbInterner<'db>, O>,
+ ) -> Obligation<'db, O> {
+ Self::with_depth(tcx, cause, 0, param_env, predicate)
+ }
+
+ /// We often create nested obligations without setting the correct depth.
+ ///
+ /// To deal with this evaluate and fulfill explicitly update the depth
+ /// of nested obligations using this function.
+ pub fn set_depth_from_parent(&mut self, parent_depth: usize) {
+ self.recursion_depth = cmp::max(parent_depth + 1, self.recursion_depth);
+ }
+
+ pub fn with_depth(
+ tcx: DbInterner<'db>,
+ cause: ObligationCause,
+ recursion_depth: usize,
+ param_env: ParamEnv<'db>,
+ predicate: impl Upcast<DbInterner<'db>, O>,
+ ) -> Obligation<'db, O> {
+ let predicate = predicate.upcast(tcx);
+ Obligation { cause, param_env, recursion_depth, predicate }
+ }
+
+ pub fn misc(
+ tcx: DbInterner<'db>,
+ body_id: SolverDefId,
+ param_env: ParamEnv<'db>,
+ trait_ref: impl Upcast<DbInterner<'db>, O>,
+ ) -> Obligation<'db, O> {
+ Obligation::new(tcx, ObligationCause::new(body_id), param_env, trait_ref)
+ }
+
+ pub fn with<P>(
+ &self,
+ tcx: DbInterner<'db>,
+ value: impl Upcast<DbInterner<'db>, P>,
+ ) -> Obligation<'db, P> {
+ Obligation::with_depth(tcx, self.cause.clone(), self.recursion_depth, self.param_env, value)
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/type_variable.rs b/crates/hir-ty/src/next_solver/infer/type_variable.rs
new file mode 100644
index 0000000..5217308
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/type_variable.rs
@@ -0,0 +1,268 @@
+//! Storage for type variables for the infer context the next-trait-solver.
+
+use std::cmp;
+use std::marker::PhantomData;
+use std::ops::Range;
+
+use ena::snapshot_vec as sv;
+use ena::undo_log::Rollback;
+use ena::unify as ut;
+use rustc_index::IndexVec;
+use rustc_type_ir::TyVid;
+use rustc_type_ir::UniverseIndex;
+use rustc_type_ir::inherent::Ty as _;
+use tracing::debug;
+
+use crate::next_solver::SolverDefId;
+use crate::next_solver::Ty;
+use crate::next_solver::infer::InferCtxtUndoLogs;
+
+impl<'db> Rollback<sv::UndoLog<ut::Delegate<TyVidEqKey<'db>>>> for TypeVariableStorage<'db> {
+ fn reverse(&mut self, undo: sv::UndoLog<ut::Delegate<TyVidEqKey<'db>>>) {
+ self.eq_relations.reverse(undo)
+ }
+}
+
+#[derive(Clone, Default)]
+pub(crate) struct TypeVariableStorage<'db> {
+ /// The origins of each type variable.
+ values: IndexVec<TyVid, TypeVariableData>,
+ /// Two variables are unified in `eq_relations` when we have a
+ /// constraint `?X == ?Y`. This table also stores, for each key,
+ /// the known value.
+ eq_relations: ut::UnificationTableStorage<TyVidEqKey<'db>>,
+}
+
+pub(crate) struct TypeVariableTable<'a, 'db> {
+ storage: &'a mut TypeVariableStorage<'db>,
+
+ undo_log: &'a mut InferCtxtUndoLogs<'db>,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct TypeVariableOrigin {
+ /// `DefId` of the type parameter this was instantiated for, if any.
+ ///
+ /// This should only be used for diagnostics.
+ pub param_def_id: Option<SolverDefId>,
+}
+
+#[derive(Clone)]
+pub(crate) struct TypeVariableData {
+ origin: TypeVariableOrigin,
+}
+
+#[derive(Clone, Debug)]
+pub(crate) enum TypeVariableValue<'db> {
+ Known { value: Ty<'db> },
+ Unknown { universe: UniverseIndex },
+}
+
+impl<'db> TypeVariableValue<'db> {
+ /// If this value is known, returns the type it is known to be.
+ /// Otherwise, `None`.
+ pub(crate) fn known(&self) -> Option<Ty<'db>> {
+ match self {
+ TypeVariableValue::Unknown { .. } => None,
+ TypeVariableValue::Known { value } => Some(*value),
+ }
+ }
+
+ pub(crate) fn is_unknown(&self) -> bool {
+ match *self {
+ TypeVariableValue::Unknown { .. } => true,
+ TypeVariableValue::Known { .. } => false,
+ }
+ }
+}
+
+impl<'db> TypeVariableStorage<'db> {
+ #[inline]
+ pub(crate) fn with_log<'a>(
+ &'a mut self,
+ undo_log: &'a mut InferCtxtUndoLogs<'db>,
+ ) -> TypeVariableTable<'a, 'db> {
+ TypeVariableTable { storage: self, undo_log }
+ }
+
+ #[inline]
+ pub(crate) fn eq_relations_ref(&self) -> &ut::UnificationTableStorage<TyVidEqKey<'db>> {
+ &self.eq_relations
+ }
+
+ pub(super) fn finalize_rollback(&mut self) {
+ debug_assert!(self.values.len() >= self.eq_relations.len());
+ self.values.truncate(self.eq_relations.len());
+ }
+}
+
+impl<'db> TypeVariableTable<'_, 'db> {
+ /// Returns the origin that was given when `vid` was created.
+ ///
+ /// Note that this function does not return care whether
+ /// `vid` has been unified with something else or not.
+ pub(crate) fn var_origin(&self, vid: TyVid) -> TypeVariableOrigin {
+ self.storage.values[vid].origin
+ }
+
+ /// Records that `a == b`, depending on `dir`.
+ ///
+ /// Precondition: neither `a` nor `b` are known.
+ pub(crate) fn equate(&mut self, a: TyVid, b: TyVid) {
+ debug_assert!(self.probe(a).is_unknown());
+ debug_assert!(self.probe(b).is_unknown());
+ self.eq_relations().union(a, b);
+ }
+
+ /// Instantiates `vid` with the type `ty`.
+ ///
+ /// Precondition: `vid` must not have been previously instantiated.
+ pub(crate) fn instantiate(&mut self, vid: TyVid, ty: Ty<'db>) {
+ let vid = self.root_var(vid);
+ debug_assert!(!ty.is_ty_var(), "instantiating ty var with var: {vid:?} {ty:?}");
+ debug_assert!(self.probe(vid).is_unknown());
+ debug_assert!(
+ self.eq_relations().probe_value(vid).is_unknown(),
+ "instantiating type variable `{vid:?}` twice: new-value = {ty:?}, old-value={:?}",
+ self.eq_relations().probe_value(vid)
+ );
+ self.eq_relations().union_value(vid, TypeVariableValue::Known { value: ty });
+ }
+
+ /// Creates a new type variable.
+ ///
+ /// - `diverging`: indicates if this is a "diverging" type
+ /// variable, e.g., one created as the type of a `return`
+ /// expression. The code in this module doesn't care if a
+ /// variable is diverging, but the main Rust type-checker will
+ /// sometimes "unify" such variables with the `!` or `()` types.
+ /// - `origin`: indicates *why* the type variable was created.
+ /// The code in this module doesn't care, but it can be useful
+ /// for improving error messages.
+ pub(crate) fn new_var(&mut self, universe: UniverseIndex, origin: TypeVariableOrigin) -> TyVid {
+ let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe });
+ let index = self.storage.values.push(TypeVariableData { origin });
+ debug_assert_eq!(eq_key.vid, index);
+
+ debug!("new_var(index={:?}, universe={:?}, origin={:?})", eq_key.vid, universe, origin);
+
+ index
+ }
+
+ /// Returns the number of type variables created thus far.
+ pub(crate) fn num_vars(&self) -> usize {
+ self.storage.values.len()
+ }
+
+ /// Returns the "root" variable of `vid` in the `eq_relations`
+ /// equivalence table. All type variables that have been equated
+ /// will yield the same root variable (per the union-find
+ /// algorithm), so `root_var(a) == root_var(b)` implies that `a ==
+ /// b` (transitively).
+ pub(crate) fn root_var(&mut self, vid: TyVid) -> TyVid {
+ self.eq_relations().find(vid).vid
+ }
+
+ /// Retrieves the type to which `vid` has been instantiated, if
+ /// any.
+ pub(crate) fn probe(&mut self, vid: TyVid) -> TypeVariableValue<'db> {
+ self.inlined_probe(vid)
+ }
+
+ /// An always-inlined variant of `probe`, for very hot call sites.
+ #[inline(always)]
+ pub(crate) fn inlined_probe(&mut self, vid: TyVid) -> TypeVariableValue<'db> {
+ self.eq_relations().inlined_probe_value(vid)
+ }
+
+ #[inline]
+ fn eq_relations(&mut self) -> super::UnificationTable<'_, 'db, TyVidEqKey<'db>> {
+ self.storage.eq_relations.with_log(self.undo_log)
+ }
+
+ /// Returns indices of all variables that are not yet
+ /// instantiated.
+ pub(crate) fn unresolved_variables(&mut self) -> Vec<TyVid> {
+ (0..self.num_vars())
+ .filter_map(|i| {
+ let vid = TyVid::from_usize(i);
+ match self.probe(vid) {
+ TypeVariableValue::Unknown { .. } => Some(vid),
+ TypeVariableValue::Known { .. } => None,
+ }
+ })
+ .collect()
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/// These structs (a newtyped TyVid) are used as the unification key
+/// for the `eq_relations`; they carry a `TypeVariableValue` along
+/// with them.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub(crate) struct TyVidEqKey<'db> {
+ vid: TyVid,
+
+ // in the table, we map each ty-vid to one of these:
+ phantom: PhantomData<TypeVariableValue<'db>>,
+}
+
+impl<'db> From<TyVid> for TyVidEqKey<'db> {
+ #[inline] // make this function eligible for inlining - it is quite hot.
+ fn from(vid: TyVid) -> Self {
+ TyVidEqKey { vid, phantom: PhantomData }
+ }
+}
+
+impl<'db> ut::UnifyKey for TyVidEqKey<'db> {
+ type Value = TypeVariableValue<'db>;
+ #[inline(always)]
+ fn index(&self) -> u32 {
+ self.vid.as_u32()
+ }
+ #[inline]
+ fn from_index(i: u32) -> Self {
+ TyVidEqKey::from(TyVid::from_u32(i))
+ }
+ fn tag() -> &'static str {
+ "TyVidEqKey"
+ }
+}
+
+impl<'db> ut::UnifyValue for TypeVariableValue<'db> {
+ type Error = ut::NoError;
+
+ fn unify_values(value1: &Self, value2: &Self) -> Result<Self, ut::NoError> {
+ match (value1, value2) {
+ // We never equate two type variables, both of which
+ // have known types. Instead, we recursively equate
+ // those types.
+ (&TypeVariableValue::Known { .. }, &TypeVariableValue::Known { .. }) => {
+ panic!("equating two type variables, both of which have known types")
+ }
+
+ // If one side is known, prefer that one.
+ (&TypeVariableValue::Known { .. }, &TypeVariableValue::Unknown { .. }) => {
+ Ok(value1.clone())
+ }
+ (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { .. }) => {
+ Ok(value2.clone())
+ }
+
+ // If both sides are *unknown*, it hardly matters, does it?
+ (
+ &TypeVariableValue::Unknown { universe: universe1 },
+ &TypeVariableValue::Unknown { universe: universe2 },
+ ) => {
+ // If we unify two unbound variables, ?T and ?U, then whatever
+ // value they wind up taking (which must be the same value) must
+ // be nameable by both universes. Therefore, the resulting
+ // universe is the minimum of the two universes, because that is
+ // the one which contains the fewest names in scope.
+ let universe = cmp::min(universe1, universe2);
+ Ok(TypeVariableValue::Unknown { universe })
+ }
+ }
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/unify_key.rs b/crates/hir-ty/src/next_solver/infer/unify_key.rs
new file mode 100644
index 0000000..b9afb45
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/unify_key.rs
@@ -0,0 +1,176 @@
+//! Unification keyes for the infer context the next-trait-solver.
+
+use std::cmp;
+use std::marker::PhantomData;
+
+use ena::unify::{NoError, UnifyKey, UnifyValue};
+use rustc_type_ir::{ConstVid, RegionKind, RegionVid, UniverseIndex, inherent::IntoKind};
+
+use crate::next_solver::{Const, Region, SolverDefId, Ty};
+
+#[derive(Clone, Debug)]
+pub enum RegionVariableValue<'db> {
+ Known { value: Region<'db> },
+ Unknown { universe: UniverseIndex },
+}
+
+#[derive(PartialEq, Copy, Clone, Debug)]
+pub struct RegionVidKey<'db> {
+ pub vid: RegionVid,
+ pub phantom: PhantomData<RegionVariableValue<'db>>,
+}
+
+impl<'db> From<RegionVid> for RegionVidKey<'db> {
+ fn from(vid: RegionVid) -> Self {
+ RegionVidKey { vid, phantom: PhantomData }
+ }
+}
+
+impl<'db> UnifyKey for RegionVidKey<'db> {
+ type Value = RegionVariableValue<'db>;
+ #[inline]
+ fn index(&self) -> u32 {
+ self.vid.as_u32()
+ }
+ #[inline]
+ fn from_index(i: u32) -> Self {
+ RegionVidKey::from(RegionVid::from_u32(i))
+ }
+ fn tag() -> &'static str {
+ "RegionVidKey"
+ }
+}
+
+pub struct RegionUnificationError;
+impl<'db> UnifyValue for RegionVariableValue<'db> {
+ type Error = RegionUnificationError;
+
+ fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
+ match (value1, value2) {
+ (RegionVariableValue::Known { .. }, RegionVariableValue::Known { .. }) => {
+ Err(RegionUnificationError)
+ }
+
+ (RegionVariableValue::Known { value }, RegionVariableValue::Unknown { universe })
+ | (RegionVariableValue::Unknown { universe }, RegionVariableValue::Known { value }) => {
+ let universe_of_value = match (*value).kind() {
+ RegionKind::ReStatic
+ | RegionKind::ReErased
+ | RegionKind::ReLateParam(..)
+ | RegionKind::ReEarlyParam(..)
+ | RegionKind::ReError(_) => UniverseIndex::ROOT,
+ RegionKind::RePlaceholder(placeholder) => placeholder.universe,
+ RegionKind::ReVar(..) | RegionKind::ReBound(..) => {
+ panic!("not a universal region")
+ }
+ };
+
+ if universe.can_name(universe_of_value) {
+ Ok(RegionVariableValue::Known { value: *value })
+ } else {
+ Err(RegionUnificationError)
+ }
+ }
+
+ (
+ RegionVariableValue::Unknown { universe: a },
+ RegionVariableValue::Unknown { universe: b },
+ ) => {
+ // If we unify two unconstrained regions then whatever
+ // value they wind up taking (which must be the same value) must
+ // be nameable by both universes. Therefore, the resulting
+ // universe is the minimum of the two universes, because that is
+ // the one which contains the fewest names in scope.
+ Ok(RegionVariableValue::Unknown { universe: (*a).min(*b) })
+ }
+ }
+ }
+}
+
+// Generic consts.
+
+#[derive(Copy, Clone, Debug)]
+pub struct ConstVariableOrigin {
+ /// `DefId` of the const parameter this was instantiated for, if any.
+ ///
+ /// This should only be used for diagnostics.
+ pub param_def_id: Option<SolverDefId>,
+}
+
+#[derive(Clone, Debug)]
+pub enum ConstVariableValue<'db> {
+ Known { value: Const<'db> },
+ Unknown { origin: ConstVariableOrigin, universe: UniverseIndex },
+}
+
+impl<'db> ConstVariableValue<'db> {
+ /// If this value is known, returns the const it is known to be.
+ /// Otherwise, `None`.
+ pub fn known(&self) -> Option<Const<'db>> {
+ match self {
+ ConstVariableValue::Unknown { .. } => None,
+ ConstVariableValue::Known { value } => Some(*value),
+ }
+ }
+}
+
+#[derive(PartialEq, Copy, Clone, Debug)]
+pub struct ConstVidKey<'db> {
+ pub vid: ConstVid,
+ pub phantom: PhantomData<Const<'db>>,
+}
+
+impl<'db> From<ConstVid> for ConstVidKey<'db> {
+ fn from(vid: ConstVid) -> Self {
+ ConstVidKey { vid, phantom: PhantomData }
+ }
+}
+
+impl<'db> UnifyKey for ConstVidKey<'db> {
+ type Value = ConstVariableValue<'db>;
+ #[inline]
+ fn index(&self) -> u32 {
+ self.vid.as_u32()
+ }
+ #[inline]
+ fn from_index(i: u32) -> Self {
+ ConstVidKey::from(ConstVid::from_u32(i))
+ }
+ fn tag() -> &'static str {
+ "ConstVidKey"
+ }
+}
+
+impl<'db> UnifyValue for ConstVariableValue<'db> {
+ type Error = NoError;
+
+ fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
+ match (value1, value2) {
+ (ConstVariableValue::Known { .. }, ConstVariableValue::Known { .. }) => {
+ panic!("equating two const variables, both of which have known values")
+ }
+
+ // If one side is known, prefer that one.
+ (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => {
+ Ok(value1.clone())
+ }
+ (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => {
+ Ok(value2.clone())
+ }
+
+ // If both sides are *unknown*, it hardly matters, does it?
+ (
+ ConstVariableValue::Unknown { origin, universe: universe1 },
+ ConstVariableValue::Unknown { origin: _, universe: universe2 },
+ ) => {
+ // If we unify two unbound variables, ?T and ?U, then whatever
+ // value they wind up taking (which must be the same value) must
+ // be nameable by both universes. Therefore, the resulting
+ // universe is the minimum of the two universes, because that is
+ // the one which contains the fewest names in scope.
+ let universe = cmp::min(*universe1, *universe2);
+ Ok(ConstVariableValue::Unknown { origin: *origin, universe })
+ }
+ }
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs
new file mode 100644
index 0000000..9c1bd52
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -0,0 +1,2173 @@
+//! Things related to the Interner in the next-trait-solver.
+#![allow(unused)]
+
+use base_db::Crate;
+use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variances};
+use hir_def::lang_item::LangItem;
+use hir_def::signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags};
+use hir_def::{AdtId, BlockId, TypeAliasId, VariantId};
+use hir_def::{AttrDefId, Lookup};
+use hir_def::{CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId};
+use intern::sym::non_exhaustive;
+use intern::{Interned, impl_internable, sym};
+use la_arena::Idx;
+use rustc_abi::{Align, ReprFlags, ReprOptions};
+use rustc_hash::FxHashSet;
+use rustc_index::bit_set::DenseBitSet;
+use rustc_type_ir::elaborate::elaborate;
+use rustc_type_ir::error::TypeError;
+use rustc_type_ir::inherent::{
+ AdtDef as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike as _, Span as _,
+};
+use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::solve::SizedTraitKind;
+use rustc_type_ir::{
+ AliasTerm, AliasTermKind, AliasTy, EarlyBinder, FlagComputation, Flags, ImplPolarity, InferTy,
+ ProjectionPredicate, TraitPredicate, TraitRef, Upcast,
+};
+use salsa::plumbing::AsId;
+use smallvec::{SmallVec, smallvec};
+use std::fmt;
+use std::ops::ControlFlow;
+use syntax::ast::SelfParamKind;
+use triomphe::Arc;
+
+use rustc_ast_ir::visit::VisitorResult;
+use rustc_index::IndexVec;
+use rustc_type_ir::TypeVisitableExt;
+use rustc_type_ir::{
+ BoundVar, CollectAndApply, DebruijnIndex, GenericArgKind, RegionKind, TermKind, UniverseIndex,
+ Variance, WithCachedTypeInfo, elaborate,
+ inherent::{self, Const as _, Region as _, Ty as _},
+ ir_print, relate,
+};
+
+use crate::lower_nextsolver::{self, TyLoweringContext};
+use crate::method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint};
+use crate::next_solver::util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls};
+use crate::next_solver::{
+ CanonicalVarKind, FxIndexMap, InternedWrapperNoDebug, RegionAssumptions, SolverDefIds,
+};
+use crate::{ConstScalar, FnAbi, Interner, db::HirDatabase};
+
+use super::generics::generics;
+use super::util::sizedness_constraint_for_ty;
+use super::{
+ Binder, BoundExistentialPredicate, BoundExistentialPredicates, BoundTy, BoundTyKind, Clause,
+ Clauses, Const, ConstKind, ErrorGuaranteed, ExprConst, ExternalConstraints,
+ ExternalConstraintsData, GenericArg, GenericArgs, InternedClausesWrapper, ParamConst, ParamEnv,
+ ParamTy, PlaceholderConst, PlaceholderTy, PredefinedOpaques, PredefinedOpaquesData, Predicate,
+ PredicateKind, Term, Ty, TyKind, Tys, ValueConst,
+ abi::Safety,
+ fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate},
+ generics::Generics,
+ mapping::ChalkToNextSolver,
+ region::{
+ BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, PlaceholderRegion, Region,
+ },
+};
+use super::{ClauseKind, SolverDefId, Valtree};
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! _interned_vec_nolifetime_salsa {
+ ($name:ident, $ty:ty) => {
+ interned_vec_nolifetime_salsa!($name, $ty, nofold);
+
+ impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for $name {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ use rustc_type_ir::inherent::SliceLike as _;
+ let inner: smallvec::SmallVec<[_; 2]> =
+ self.iter().map(|v| v.try_fold_with(folder)).collect::<Result<_, _>>()?;
+ Ok($name::new_(folder.cx().db(), inner))
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Self {
+ use rustc_type_ir::inherent::SliceLike as _;
+ let inner: smallvec::SmallVec<[_; 2]> =
+ self.iter().map(|v| v.fold_with(folder)).collect();
+ $name::new_(folder.cx().db(), inner)
+ }
+ }
+
+ impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for $name {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ use rustc_ast_ir::visit::VisitorResult;
+ use rustc_type_ir::inherent::SliceLike as _;
+ rustc_ast_ir::walk_visitable_list!(visitor, self.as_slice().iter());
+ V::Result::output()
+ }
+ }
+ };
+ ($name:ident, $ty:ty, nofold) => {
+ #[salsa::interned(no_lifetime, constructor = new_, debug)]
+ pub struct $name {
+ #[returns(ref)]
+ inner_: smallvec::SmallVec<[$ty; 2]>,
+ }
+
+ impl $name {
+ pub fn new_from_iter<'db>(
+ interner: DbInterner<'db>,
+ data: impl IntoIterator<Item = $ty>,
+ ) -> Self {
+ $name::new_(interner.db(), data.into_iter().collect::<smallvec::SmallVec<[_; 2]>>())
+ }
+
+ pub fn inner(&self) -> &smallvec::SmallVec<[$ty; 2]> {
+ // SAFETY: ¯\_(ツ)_/¯
+ salsa::with_attached_database(|db| {
+ let inner = self.inner_(db);
+ unsafe { std::mem::transmute(inner) }
+ })
+ .unwrap()
+ }
+ }
+
+ impl rustc_type_ir::inherent::SliceLike for $name {
+ type Item = $ty;
+
+ type IntoIter = <smallvec::SmallVec<[$ty; 2]> as IntoIterator>::IntoIter;
+
+ fn iter(self) -> Self::IntoIter {
+ self.inner().clone().into_iter()
+ }
+
+ fn as_slice(&self) -> &[Self::Item] {
+ self.inner().as_slice()
+ }
+ }
+
+ impl IntoIterator for $name {
+ type Item = $ty;
+ type IntoIter = <Self as rustc_type_ir::inherent::SliceLike>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ rustc_type_ir::inherent::SliceLike::iter(self)
+ }
+ }
+
+ impl Default for $name {
+ fn default() -> Self {
+ $name::new_from_iter(DbInterner::conjure(), [])
+ }
+ }
+ };
+}
+
+pub use crate::_interned_vec_nolifetime_salsa as interned_vec_nolifetime_salsa;
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! _interned_vec_db {
+ ($name:ident, $ty:ident) => {
+ interned_vec_db!($name, $ty, nofold);
+
+ impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for $name<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ use rustc_type_ir::inherent::SliceLike as _;
+ let inner: smallvec::SmallVec<[_; 2]> =
+ self.iter().map(|v| v.try_fold_with(folder)).collect::<Result<_, _>>()?;
+ Ok($name::new_(folder.cx().db(), inner))
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Self {
+ use rustc_type_ir::inherent::SliceLike as _;
+ let inner: smallvec::SmallVec<[_; 2]> =
+ self.iter().map(|v| v.fold_with(folder)).collect();
+ $name::new_(folder.cx().db(), inner)
+ }
+ }
+
+ impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for $name<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ use rustc_ast_ir::visit::VisitorResult;
+ use rustc_type_ir::inherent::SliceLike as _;
+ rustc_ast_ir::walk_visitable_list!(visitor, self.as_slice().iter());
+ V::Result::output()
+ }
+ }
+ };
+ ($name:ident, $ty:ident, nofold) => {
+ #[salsa::interned(constructor = new_)]
+ pub struct $name<'db> {
+ #[returns(ref)]
+ inner_: smallvec::SmallVec<[$ty<'db>; 2]>,
+ }
+
+ impl<'db> std::fmt::Debug for $name<'db> {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.as_slice().fmt(fmt)
+ }
+ }
+
+ impl<'db> $name<'db> {
+ pub fn new_from_iter(
+ interner: DbInterner<'db>,
+ data: impl IntoIterator<Item = $ty<'db>>,
+ ) -> Self {
+ $name::new_(interner.db(), data.into_iter().collect::<smallvec::SmallVec<[_; 2]>>())
+ }
+
+ pub fn inner(&self) -> &smallvec::SmallVec<[$ty<'db>; 2]> {
+ // SAFETY: ¯\_(ツ)_/¯
+ salsa::with_attached_database(|db| {
+ let inner = self.inner_(db);
+ unsafe { std::mem::transmute(inner) }
+ })
+ .unwrap()
+ }
+ }
+
+ impl<'db> rustc_type_ir::inherent::SliceLike for $name<'db> {
+ type Item = $ty<'db>;
+
+ type IntoIter = <smallvec::SmallVec<[$ty<'db>; 2]> as IntoIterator>::IntoIter;
+
+ fn iter(self) -> Self::IntoIter {
+ self.inner().clone().into_iter()
+ }
+
+ fn as_slice(&self) -> &[Self::Item] {
+ self.inner().as_slice()
+ }
+ }
+
+ impl<'db> IntoIterator for $name<'db> {
+ type Item = $ty<'db>;
+ type IntoIter = <Self as rustc_type_ir::inherent::SliceLike>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ rustc_type_ir::inherent::SliceLike::iter(self)
+ }
+ }
+
+ impl<'db> Default for $name<'db> {
+ fn default() -> Self {
+ $name::new_from_iter(DbInterner::conjure(), [])
+ }
+ }
+ };
+}
+
+pub use crate::_interned_vec_db as interned_vec_db;
+
+#[derive(Debug, Copy, Clone)]
+pub struct DbInterner<'db> {
+ pub(crate) db: &'db dyn HirDatabase,
+ pub(crate) krate: Option<Crate>,
+ pub(crate) block: Option<BlockId>,
+}
+
+// FIXME: very wrong, see https://github.com/rust-lang/rust/pull/144808
+unsafe impl Send for DbInterner<'_> {}
+unsafe impl Sync for DbInterner<'_> {}
+
+impl<'db> DbInterner<'db> {
+ // FIXME(next-solver): remove this method
+ pub fn conjure() -> DbInterner<'db> {
+ salsa::with_attached_database(|db| DbInterner {
+ db: unsafe {
+ std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>(db.as_view())
+ },
+ krate: None,
+ block: None,
+ })
+ .unwrap()
+ }
+
+ pub fn new_with(
+ db: &'db dyn HirDatabase,
+ krate: Option<Crate>,
+ block: Option<BlockId>,
+ ) -> DbInterner<'db> {
+ DbInterner { db, krate, block }
+ }
+
+ pub fn db(&self) -> &'db dyn HirDatabase {
+ self.db
+ }
+}
+
+// This is intentionally left as `()`
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub struct Span(());
+
+impl<'db> inherent::Span<DbInterner<'db>> for Span {
+ fn dummy() -> Self {
+ Span(())
+ }
+}
+
+interned_vec_nolifetime_salsa!(BoundVarKinds, BoundVarKind, nofold);
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum BoundVarKind {
+ Ty(BoundTyKind),
+ Region(BoundRegionKind),
+ Const,
+}
+
+impl BoundVarKind {
+ pub fn expect_region(self) -> BoundRegionKind {
+ match self {
+ BoundVarKind::Region(lt) => lt,
+ _ => panic!("expected a region, but found another kind"),
+ }
+ }
+
+ pub fn expect_ty(self) -> BoundTyKind {
+ match self {
+ BoundVarKind::Ty(ty) => ty,
+ _ => panic!("expected a type, but found another kind"),
+ }
+ }
+
+ pub fn expect_const(self) {
+ match self {
+ BoundVarKind::Const => (),
+ _ => panic!("expected a const, but found another kind"),
+ }
+ }
+}
+
+interned_vec_db!(CanonicalVars, CanonicalVarKind, nofold);
+
+pub struct DepNodeIndex;
+
+#[derive(Debug)]
+pub struct Tracked<T: fmt::Debug + Clone>(T);
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct Placeholder<T> {
+ pub universe: UniverseIndex,
+ pub bound: T,
+}
+
+impl<T: std::fmt::Debug> std::fmt::Debug for Placeholder<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
+ if self.universe == UniverseIndex::ROOT {
+ write!(f, "!{:?}", self.bound)
+ } else {
+ write!(f, "!{}_{:?}", self.universe.index(), self.bound)
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub struct AllocId;
+
+interned_vec_nolifetime_salsa!(VariancesOf, Variance, nofold);
+
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub struct VariantIdx(usize);
+
+// FIXME: could/should store actual data?
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub enum VariantDef {
+ Struct(StructId),
+ Union(UnionId),
+ Enum(EnumVariantId),
+}
+
+impl VariantDef {
+ pub fn id(&self) -> VariantId {
+ match self {
+ VariantDef::Struct(struct_id) => VariantId::StructId(*struct_id),
+ VariantDef::Union(union_id) => VariantId::UnionId(*union_id),
+ VariantDef::Enum(enum_variant_id) => VariantId::EnumVariantId(*enum_variant_id),
+ }
+ }
+
+ pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Idx<FieldData>, FieldData)> {
+ let id: VariantId = match self {
+ VariantDef::Struct(it) => (*it).into(),
+ VariantDef::Union(it) => (*it).into(),
+ VariantDef::Enum(it) => (*it).into(),
+ };
+ id.fields(db).fields().iter().map(|(id, data)| (id, data.clone())).collect()
+ }
+}
+
+/*
+/// Definition of a variant -- a struct's fields or an enum variant.
+#[derive(Debug, HashStable, TyEncodable, TyDecodable)]
+pub struct VariantDef {
+ /// `DefId` that identifies the variant itself.
+ /// If this variant belongs to a struct or union, then this is a copy of its `DefId`.
+ pub def_id: DefId,
+ /// `DefId` that identifies the variant's constructor.
+ /// If this variant is a struct variant, then this is `None`.
+ pub ctor: Option<(CtorKind, DefId)>,
+ /// Variant or struct name, maybe empty for anonymous adt (struct or union).
+ pub name: Symbol,
+ /// Discriminant of this variant.
+ pub discr: VariantDiscr,
+ /// Fields of this variant.
+ pub fields: IndexVec<FieldIdx, FieldDef>,
+ /// The error guarantees from parser, if any.
+ tainted: Option<ErrorGuaranteed>,
+ /// Flags of the variant (e.g. is field list non-exhaustive)?
+ flags: VariantFlags,
+}
+*/
+
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub struct AdtFlags {
+ is_enum: bool,
+ is_union: bool,
+ is_struct: bool,
+ is_phantom_data: bool,
+ is_fundamental: bool,
+ is_box: bool,
+ is_manually_drop: bool,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct AdtDefInner {
+ pub id: AdtId,
+ variants: Vec<(VariantIdx, VariantDef)>,
+ flags: AdtFlags,
+ repr: ReprOptions,
+}
+
+// We're gonna cheat a little bit and implement `Hash` on only the `DefId` and
+// accept there might be collisions for def ids from different crates (or across
+// different tests, oh my).
+impl std::hash::Hash for AdtDefInner {
+ #[inline]
+ fn hash<H: std::hash::Hasher>(&self, s: &mut H) {
+ self.id.hash(s)
+ }
+}
+
+#[salsa::interned(no_lifetime, constructor = new_)]
+pub struct AdtDef {
+ #[returns(ref)]
+ data_: AdtDefInner,
+}
+
+impl AdtDef {
+ pub fn new<'db>(def_id: AdtId, interner: DbInterner<'db>) -> Self {
+ let db = interner.db();
+ let (flags, variants, repr) = match def_id {
+ AdtId::StructId(struct_id) => {
+ let data = db.struct_signature(struct_id);
+
+ let flags = AdtFlags {
+ is_enum: false,
+ is_union: false,
+ is_struct: true,
+ is_phantom_data: data.flags.contains(StructFlags::IS_PHANTOM_DATA),
+ is_fundamental: data.flags.contains(StructFlags::FUNDAMENTAL),
+ is_box: data.flags.contains(StructFlags::IS_BOX),
+ is_manually_drop: data.flags.contains(StructFlags::IS_MANUALLY_DROP),
+ };
+
+ let variants = vec![(VariantIdx(0), VariantDef::Struct(struct_id))];
+
+ let mut repr = ReprOptions::default();
+ repr.align = data.repr.and_then(|r| r.align);
+ repr.pack = data.repr.and_then(|r| r.pack);
+ repr.int = data.repr.and_then(|r| r.int);
+
+ let mut repr_flags = ReprFlags::empty();
+ if flags.is_box {
+ repr_flags.insert(ReprFlags::IS_LINEAR);
+ }
+ if data.repr.is_some_and(|r| r.c()) {
+ repr_flags.insert(ReprFlags::IS_C);
+ }
+ if data.repr.is_some_and(|r| r.simd()) {
+ repr_flags.insert(ReprFlags::IS_SIMD);
+ }
+ repr.flags = repr_flags;
+
+ (flags, variants, repr)
+ }
+ AdtId::UnionId(union_id) => {
+ let data = db.union_signature(union_id);
+
+ let flags = AdtFlags {
+ is_enum: false,
+ is_union: true,
+ is_struct: false,
+ is_phantom_data: false,
+ is_fundamental: false,
+ is_box: false,
+ is_manually_drop: false,
+ };
+
+ let variants = vec![(VariantIdx(0), VariantDef::Union(union_id))];
+
+ let mut repr = ReprOptions::default();
+ repr.align = data.repr.and_then(|r| r.align);
+ repr.pack = data.repr.and_then(|r| r.pack);
+ repr.int = data.repr.and_then(|r| r.int);
+
+ let mut repr_flags = ReprFlags::empty();
+ if flags.is_box {
+ repr_flags.insert(ReprFlags::IS_LINEAR);
+ }
+ if data.repr.is_some_and(|r| r.c()) {
+ repr_flags.insert(ReprFlags::IS_C);
+ }
+ if data.repr.is_some_and(|r| r.simd()) {
+ repr_flags.insert(ReprFlags::IS_SIMD);
+ }
+ repr.flags = repr_flags;
+
+ (flags, variants, repr)
+ }
+ AdtId::EnumId(enum_id) => {
+ let flags = AdtFlags {
+ is_enum: true,
+ is_union: false,
+ is_struct: false,
+ is_phantom_data: false,
+ is_fundamental: false,
+ is_box: false,
+ is_manually_drop: false,
+ };
+
+ let variants = enum_id
+ .enum_variants(db)
+ .variants
+ .iter()
+ .enumerate()
+ .map(|(idx, v)| (VariantIdx(idx), v))
+ .map(|(idx, v)| (idx, VariantDef::Enum(v.0)))
+ .collect();
+
+ let data = db.enum_signature(enum_id);
+
+ let mut repr = ReprOptions::default();
+ repr.align = data.repr.and_then(|r| r.align);
+ repr.pack = data.repr.and_then(|r| r.pack);
+ repr.int = data.repr.and_then(|r| r.int);
+
+ let mut repr_flags = ReprFlags::empty();
+ if flags.is_box {
+ repr_flags.insert(ReprFlags::IS_LINEAR);
+ }
+ if data.repr.is_some_and(|r| r.c()) {
+ repr_flags.insert(ReprFlags::IS_C);
+ }
+ if data.repr.is_some_and(|r| r.simd()) {
+ repr_flags.insert(ReprFlags::IS_SIMD);
+ }
+ repr.flags = repr_flags;
+
+ (flags, variants, repr)
+ }
+ };
+
+ AdtDef::new_(db, AdtDefInner { id: def_id, variants, flags, repr })
+ }
+
+ pub fn inner(&self) -> &AdtDefInner {
+ salsa::with_attached_database(|db| {
+ let inner = self.data_(db);
+ // SAFETY: ¯\_(ツ)_/¯
+ unsafe { std::mem::transmute(inner) }
+ })
+ .unwrap()
+ }
+
+ pub fn is_enum(&self) -> bool {
+ self.inner().flags.is_enum
+ }
+
+ #[inline]
+ pub fn repr(self) -> ReprOptions {
+ self.inner().repr
+ }
+
+ /// Asserts this is a struct or union and returns its unique variant.
+ pub fn non_enum_variant(self) -> VariantDef {
+ assert!(self.inner().flags.is_struct || self.inner().flags.is_union);
+ self.inner().variants[0].1.clone()
+ }
+}
+
+impl<'db> inherent::AdtDef<DbInterner<'db>> for AdtDef {
+ fn def_id(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::DefId {
+ SolverDefId::AdtId(self.inner().id)
+ }
+
+ fn is_struct(self) -> bool {
+ self.inner().flags.is_struct
+ }
+
+ fn is_phantom_data(self) -> bool {
+ self.inner().flags.is_phantom_data
+ }
+
+ fn is_fundamental(self) -> bool {
+ self.inner().flags.is_fundamental
+ }
+
+ fn struct_tail_ty(
+ self,
+ interner: DbInterner<'db>,
+ ) -> Option<
+ rustc_type_ir::EarlyBinder<
+ DbInterner<'db>,
+ <DbInterner<'db> as rustc_type_ir::Interner>::Ty,
+ >,
+ > {
+ let db = interner.db();
+ let hir_def::AdtId::StructId(struct_id) = self.inner().id else {
+ return None;
+ };
+ let id: VariantId = struct_id.into();
+ let field_types = interner.db().field_types_ns(id);
+
+ field_types.iter().last().map(|f| *f.1)
+ }
+
+ fn all_field_tys(
+ self,
+ interner: DbInterner<'db>,
+ ) -> rustc_type_ir::EarlyBinder<
+ DbInterner<'db>,
+ impl IntoIterator<Item = <DbInterner<'db> as rustc_type_ir::Interner>::Ty>,
+ > {
+ let db = interner.db();
+ // FIXME: this is disabled just to match the behavior with chalk right now
+ let field_tys = |id: VariantId| {
+ let variant_data = id.fields(db);
+ let fields = if variant_data.fields().is_empty() {
+ vec![]
+ } else {
+ let field_types = db.field_types_ns(id);
+ variant_data
+ .fields()
+ .iter()
+ .map(|(idx, _)| {
+ let ty = field_types[idx];
+ ty.skip_binder()
+ })
+ .collect()
+ };
+ };
+ let field_tys = |id: VariantId| vec![];
+ let tys: Vec<_> = match self.inner().id {
+ hir_def::AdtId::StructId(id) => field_tys(id.into()),
+ hir_def::AdtId::UnionId(id) => field_tys(id.into()),
+ hir_def::AdtId::EnumId(id) => id
+ .enum_variants(db)
+ .variants
+ .iter()
+ .flat_map(|&(variant_id, _, _)| field_tys(variant_id.into()))
+ .collect(),
+ };
+
+ rustc_type_ir::EarlyBinder::bind(tys)
+ }
+
+ fn sizedness_constraint(
+ self,
+ interner: DbInterner<'db>,
+ sizedness: SizedTraitKind,
+ ) -> Option<
+ rustc_type_ir::EarlyBinder<
+ DbInterner<'db>,
+ <DbInterner<'db> as rustc_type_ir::Interner>::Ty,
+ >,
+ > {
+ if self.is_struct() {
+ let tail_ty = self.all_field_tys(interner).skip_binder().into_iter().last()?;
+
+ let constraint_ty = sizedness_constraint_for_ty(interner, sizedness, tail_ty)?;
+
+ Some(EarlyBinder::bind(constraint_ty))
+ } else {
+ None
+ }
+ }
+
+ fn destructor(
+ self,
+ interner: DbInterner<'db>,
+ ) -> Option<rustc_type_ir::solve::AdtDestructorKind> {
+ // FIXME(next-solver)
+ None
+ }
+
+ fn is_manually_drop(self) -> bool {
+ self.inner().flags.is_manually_drop
+ }
+}
+
+impl fmt::Debug for AdtDef {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ salsa::with_attached_database(|db| match self.inner().id {
+ AdtId::StructId(struct_id) => {
+ let data = db.as_view::<dyn HirDatabase>().struct_signature(struct_id);
+ f.write_str(data.name.as_str())
+ }
+ AdtId::UnionId(union_id) => {
+ let data = db.as_view::<dyn HirDatabase>().union_signature(union_id);
+ f.write_str(data.name.as_str())
+ }
+ AdtId::EnumId(enum_id) => {
+ let data = db.as_view::<dyn HirDatabase>().enum_signature(enum_id);
+ f.write_str(data.name.as_str())
+ }
+ })
+ .unwrap_or_else(|| f.write_str(&format!("AdtDef({:?})", self.inner().id)))
+ }
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct Features;
+
+impl<'db> inherent::Features<DbInterner<'db>> for Features {
+ fn generic_const_exprs(self) -> bool {
+ false
+ }
+
+ fn coroutine_clone(self) -> bool {
+ false
+ }
+
+ fn associated_const_equality(self) -> bool {
+ false
+ }
+
+ fn feature_bound_holds_in_crate(self, symbol: ()) -> bool {
+ false
+ }
+}
+
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub struct UnsizingParams(pub(crate) DenseBitSet<u32>);
+
+impl std::ops::Deref for UnsizingParams {
+ type Target = DenseBitSet<u32>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+pub type PatternKind<'db> = rustc_type_ir::PatternKind<DbInterner<'db>>;
+
+#[salsa::interned(constructor = new_, debug)]
+pub struct Pattern<'db> {
+ #[returns(ref)]
+ kind_: InternedWrapperNoDebug<PatternKind<'db>>,
+}
+
+impl<'db> std::fmt::Debug for InternedWrapperNoDebug<PatternKind<'db>> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl<'db> Pattern<'db> {
+ pub fn new(interner: DbInterner<'db>, kind: PatternKind<'db>) -> Self {
+ Pattern::new_(interner.db(), InternedWrapperNoDebug(kind))
+ }
+
+ pub fn inner(&self) -> &PatternKind<'db> {
+ salsa::with_attached_database(|db| {
+ let inner = &self.kind_(db).0;
+ // SAFETY: The caller already has access to a `Ty<'db>`, so borrowchecking will
+ // make sure that our returned value is valid for the lifetime `'db`.
+ unsafe { std::mem::transmute(inner) }
+ })
+ .unwrap()
+ }
+}
+
+impl<'db> Flags for Pattern<'db> {
+ fn flags(&self) -> rustc_type_ir::TypeFlags {
+ match self.inner() {
+ PatternKind::Range { start, end } => {
+ FlagComputation::for_const_kind(&start.kind()).flags
+ | FlagComputation::for_const_kind(&end.kind()).flags
+ }
+ PatternKind::Or(pats) => {
+ let mut flags = pats.as_slice()[0].flags();
+ for pat in pats.as_slice()[1..].iter() {
+ flags |= pat.flags();
+ }
+ flags
+ }
+ }
+ }
+
+ fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
+ match self.inner() {
+ PatternKind::Range { start, end } => {
+ start.outer_exclusive_binder().max(end.outer_exclusive_binder())
+ }
+ PatternKind::Or(pats) => {
+ let mut idx = pats.as_slice()[0].outer_exclusive_binder();
+ for pat in pats.as_slice()[1..].iter() {
+ idx = idx.max(pat.outer_exclusive_binder());
+ }
+ idx
+ }
+ }
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::IntoKind for Pattern<'db> {
+ type Kind = rustc_type_ir::PatternKind<DbInterner<'db>>;
+ fn kind(self) -> Self::Kind {
+ *self.inner()
+ }
+}
+
+impl<'db> rustc_type_ir::relate::Relate<DbInterner<'db>> for Pattern<'db> {
+ fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
+ relation: &mut R,
+ a: Self,
+ b: Self,
+ ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
+ let tcx = relation.cx();
+ match (a.kind(), b.kind()) {
+ (
+ PatternKind::Range { start: start_a, end: end_a },
+ PatternKind::Range { start: start_b, end: end_b },
+ ) => {
+ let start = relation.relate(start_a, start_b)?;
+ let end = relation.relate(end_a, end_b)?;
+ Ok(Pattern::new(tcx, PatternKind::Range { start, end }))
+ }
+ (PatternKind::Or(a), PatternKind::Or(b)) => {
+ if a.len() != b.len() {
+ return Err(TypeError::Mismatch);
+ }
+ let pats = CollectAndApply::collect_and_apply(
+ std::iter::zip(a.iter(), b.iter()).map(|(a, b)| relation.relate(a, b)),
+ |g| PatList::new_from_iter(tcx, g.iter().cloned()),
+ )?;
+ Ok(Pattern::new(tcx, PatternKind::Or(pats)))
+ }
+ (PatternKind::Range { .. } | PatternKind::Or(_), _) => Err(TypeError::Mismatch),
+ }
+ }
+}
+
+interned_vec_db!(PatList, Pattern);
+
+impl<'db> rustc_type_ir::Interner for DbInterner<'db> {
+ type DefId = SolverDefId;
+ type LocalDefId = SolverDefId;
+ type LocalDefIds = SolverDefIds;
+ type Span = Span;
+
+ type GenericArgs = GenericArgs<'db>;
+ type GenericArgsSlice = GenericArgs<'db>;
+ type GenericArg = GenericArg<'db>;
+
+ type Term = Term<'db>;
+
+ type BoundVarKinds = BoundVarKinds;
+ type BoundVarKind = BoundVarKind;
+
+ type PredefinedOpaques = PredefinedOpaques<'db>;
+
+ fn mk_predefined_opaques_in_body(
+ self,
+ data: rustc_type_ir::solve::PredefinedOpaquesData<Self>,
+ ) -> Self::PredefinedOpaques {
+ PredefinedOpaques::new(self, data)
+ }
+
+ type CanonicalVarKinds = CanonicalVars<'db>;
+
+ fn mk_canonical_var_kinds(
+ self,
+ kinds: &[rustc_type_ir::CanonicalVarKind<Self>],
+ ) -> Self::CanonicalVarKinds {
+ CanonicalVars::new_from_iter(self, kinds.iter().cloned())
+ }
+
+ type ExternalConstraints = ExternalConstraints<'db>;
+
+ fn mk_external_constraints(
+ self,
+ data: rustc_type_ir::solve::ExternalConstraintsData<Self>,
+ ) -> Self::ExternalConstraints {
+ ExternalConstraints::new(self, data)
+ }
+
+ type DepNodeIndex = DepNodeIndex;
+
+ type Tracked<T: fmt::Debug + Clone> = Tracked<T>;
+
+ type Ty = Ty<'db>;
+ type Tys = Tys<'db>;
+ type FnInputTys = Tys<'db>;
+ type ParamTy = ParamTy;
+ type BoundTy = BoundTy;
+ type PlaceholderTy = PlaceholderTy;
+ type Symbol = ();
+
+ type ErrorGuaranteed = ErrorGuaranteed;
+ type BoundExistentialPredicates = BoundExistentialPredicates<'db>;
+ type AllocId = AllocId;
+ type Pat = Pattern<'db>;
+ type PatList = PatList<'db>;
+ type Safety = Safety;
+ type Abi = FnAbi;
+
+ type Const = Const<'db>;
+ type PlaceholderConst = PlaceholderConst;
+ type ParamConst = ParamConst;
+ type BoundConst = rustc_type_ir::BoundVar;
+ type ValueConst = ValueConst<'db>;
+ type ValTree = Valtree<'db>;
+ type ExprConst = ExprConst;
+
+ type Region = Region<'db>;
+ type EarlyParamRegion = EarlyParamRegion;
+ type LateParamRegion = LateParamRegion;
+ type BoundRegion = BoundRegion;
+ type PlaceholderRegion = PlaceholderRegion;
+
+ type RegionAssumptions = RegionAssumptions<'db>;
+
+ type ParamEnv = ParamEnv<'db>;
+ type Predicate = Predicate<'db>;
+ type Clause = Clause<'db>;
+ type Clauses = Clauses<'db>;
+
+ type GenericsOf = Generics;
+
+ type VariancesOf = VariancesOf;
+
+ type AdtDef = AdtDef;
+
+ type Features = Features;
+
+ fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs {
+ GenericArgs::new_from_iter(self, args.iter().cloned())
+ }
+
+ fn mk_args_from_iter<I, T>(self, args: I) -> T::Output
+ where
+ I: Iterator<Item = T>,
+ T: rustc_type_ir::CollectAndApply<Self::GenericArg, Self::GenericArgs>,
+ {
+ CollectAndApply::collect_and_apply(args, |g| {
+ GenericArgs::new_from_iter(self, g.iter().cloned())
+ })
+ }
+
+ type UnsizingParams = UnsizingParams;
+
+ fn mk_tracked<T: fmt::Debug + Clone>(
+ self,
+ data: T,
+ dep_node: Self::DepNodeIndex,
+ ) -> Self::Tracked<T> {
+ Tracked(data)
+ }
+
+ fn get_tracked<T: fmt::Debug + Clone>(self, tracked: &Self::Tracked<T>) -> T {
+ tracked.0.clone()
+ }
+
+ fn with_cached_task<T>(self, task: impl FnOnce() -> T) -> (T, Self::DepNodeIndex) {
+ (task(), DepNodeIndex)
+ }
+
+ fn with_global_cache<R>(
+ self,
+ f: impl FnOnce(&mut rustc_type_ir::search_graph::GlobalCache<Self>) -> R,
+ ) -> R {
+ salsa::with_attached_database(|db| {
+ tls_cache::with_cache(
+ unsafe {
+ std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>(
+ db.as_view::<dyn HirDatabase>(),
+ )
+ },
+ f,
+ )
+ })
+ .unwrap()
+ }
+
+ fn canonical_param_env_cache_get_or_insert<R>(
+ self,
+ param_env: Self::ParamEnv,
+ f: impl FnOnce() -> rustc_type_ir::CanonicalParamEnvCacheEntry<Self>,
+ from_entry: impl FnOnce(&rustc_type_ir::CanonicalParamEnvCacheEntry<Self>) -> R,
+ ) -> R {
+ from_entry(&f())
+ }
+
+ fn evaluation_is_concurrent(&self) -> bool {
+ false
+ }
+
+ fn expand_abstract_consts<T: rustc_type_ir::TypeFoldable<Self>>(self, t: T) -> T {
+ t
+ }
+
+ fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf {
+ generics(self.db(), def_id)
+ }
+
+ fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf {
+ match def_id {
+ SolverDefId::FunctionId(def_id) => VariancesOf::new_from_iter(
+ self,
+ self.db()
+ .variances_of(hir_def::GenericDefId::FunctionId(def_id))
+ .as_deref()
+ .unwrap_or_default()
+ .iter()
+ .map(|v| v.to_nextsolver(self)),
+ ),
+ SolverDefId::AdtId(def_id) => VariancesOf::new_from_iter(
+ self,
+ self.db()
+ .variances_of(hir_def::GenericDefId::AdtId(def_id))
+ .as_deref()
+ .unwrap_or_default()
+ .iter()
+ .map(|v| v.to_nextsolver(self)),
+ ),
+ SolverDefId::InternedOpaqueTyId(_def_id) => {
+ // FIXME(next-solver): track variances
+ VariancesOf::new_from_iter(
+ self,
+ (0..self.generics_of(def_id).count()).map(|_| Variance::Invariant),
+ )
+ }
+ _ => VariancesOf::new_from_iter(self, []),
+ }
+ }
+
+ fn type_of(self, def_id: Self::DefId) -> rustc_type_ir::EarlyBinder<Self, Self::Ty> {
+ let def_id = match def_id {
+ SolverDefId::TypeAliasId(id) => {
+ use hir_def::Lookup;
+ match id.lookup(self.db()).container {
+ ItemContainerId::ImplId(it) => it,
+ _ => panic!("assoc ty value should be in impl"),
+ };
+ crate::TyDefId::TypeAliasId(id)
+ }
+ SolverDefId::AdtId(id) => crate::TyDefId::AdtId(id),
+ _ => panic!("Unexpected def_id `{def_id:?}` provided for `type_of`"),
+ };
+ self.db().ty_ns(def_id)
+ }
+
+ fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef {
+ let def_id = match adt_def_id {
+ SolverDefId::AdtId(adt_id) => adt_id,
+ _ => panic!("Invalid DefId passed to adt_def"),
+ };
+ AdtDef::new(def_id, self)
+ }
+
+ fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy<Self>) -> rustc_type_ir::AliasTyKind {
+ // FIXME: not currently creating any others
+ rustc_type_ir::AliasTyKind::Projection
+ }
+
+ fn alias_term_kind(
+ self,
+ alias: rustc_type_ir::AliasTerm<Self>,
+ ) -> rustc_type_ir::AliasTermKind {
+ match alias.def_id {
+ SolverDefId::InternedOpaqueTyId(_) => AliasTermKind::OpaqueTy,
+ SolverDefId::TypeAliasId(_) => AliasTermKind::ProjectionTy,
+ SolverDefId::ConstId(_) => AliasTermKind::UnevaluatedConst,
+ _ => unreachable!("Unexpected alias: {:?}", alias.def_id),
+ }
+ }
+
+ fn trait_ref_and_own_args_for_alias(
+ self,
+ def_id: Self::DefId,
+ args: Self::GenericArgs,
+ ) -> (rustc_type_ir::TraitRef<Self>, Self::GenericArgsSlice) {
+ let trait_def_id = self.parent(def_id);
+ let trait_generics = self.generics_of(trait_def_id);
+ let trait_args = GenericArgs::new_from_iter(
+ self,
+ args.as_slice()[0..trait_generics.own_params.len()].iter().cloned(),
+ );
+ let alias_args =
+ GenericArgs::new_from_iter(self, args.iter().skip(trait_generics.own_params.len()));
+ (TraitRef::new_from_args(self, trait_def_id, trait_args), alias_args)
+ }
+
+ fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool {
+ // FIXME
+ true
+ }
+
+ fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) {}
+
+ fn debug_assert_existential_args_compatible(
+ self,
+ def_id: Self::DefId,
+ args: Self::GenericArgs,
+ ) {
+ }
+
+ fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
+ where
+ I: Iterator<Item = T>,
+ T: rustc_type_ir::CollectAndApply<Self::Ty, Self::Tys>,
+ {
+ CollectAndApply::collect_and_apply(args, |g| Tys::new_from_iter(self, g.iter().cloned()))
+ }
+
+ fn parent(self, def_id: Self::DefId) -> Self::DefId {
+ use hir_def::Lookup;
+
+ let container = match def_id {
+ SolverDefId::FunctionId(it) => it.lookup(self.db()).container,
+ SolverDefId::TypeAliasId(it) => it.lookup(self.db()).container,
+ SolverDefId::ForeignId(it) => it.lookup(self.db()).container,
+ SolverDefId::ConstId(it) => it.lookup(self.db()).container,
+ SolverDefId::InternedClosureId(it) => {
+ return self
+ .db()
+ .lookup_intern_closure(it)
+ .0
+ .as_generic_def_id(self.db())
+ .unwrap()
+ .into();
+ }
+ SolverDefId::InternedCoroutineId(it) => {
+ return self
+ .db()
+ .lookup_intern_coroutine(it)
+ .0
+ .as_generic_def_id(self.db())
+ .unwrap()
+ .into();
+ }
+ SolverDefId::StaticId(_)
+ | SolverDefId::AdtId(_)
+ | SolverDefId::TraitId(_)
+ | SolverDefId::ImplId(_)
+ | SolverDefId::TraitAliasId(_)
+ | SolverDefId::Ctor(..)
+ | SolverDefId::InternedOpaqueTyId(..) => panic!(),
+ };
+
+ match container {
+ ItemContainerId::ImplId(it) => it.into(),
+ ItemContainerId::TraitId(it) => it.into(),
+ ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => panic!(),
+ }
+ }
+
+ fn recursion_limit(self) -> usize {
+ 50
+ }
+
+ fn features(self) -> Self::Features {
+ Features
+ }
+
+ fn fn_sig(
+ self,
+ def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<Self, rustc_type_ir::Binder<Self, rustc_type_ir::FnSig<Self>>>
+ {
+ let id = match def_id {
+ SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id),
+ SolverDefId::Ctor(ctor) => match ctor {
+ super::Ctor::Struct(struct_id) => CallableDefId::StructId(struct_id),
+ super::Ctor::Enum(enum_variant_id) => CallableDefId::EnumVariantId(enum_variant_id),
+ },
+ def => unreachable!("{:?}", def),
+ };
+ self.db().callable_item_signature_ns(id)
+ }
+
+ fn coroutine_movability(self, def_id: Self::DefId) -> rustc_ast_ir::Movability {
+ unimplemented!()
+ }
+
+ fn coroutine_for_closure(self, def_id: Self::DefId) -> Self::DefId {
+ unimplemented!()
+ }
+
+ fn generics_require_sized_self(self, def_id: Self::DefId) -> bool {
+ let sized_trait =
+ LangItem::Sized.resolve_trait(self.db(), self.krate.expect("Must have self.krate"));
+ let Some(sized_id) = sized_trait else {
+ return false; /* No Sized trait, can't require it! */
+ };
+ let sized_def_id = sized_id.into();
+
+ // Search for a predicate like `Self : Sized` amongst the trait bounds.
+ let predicates = self.predicates_of(def_id);
+ elaborate(self, predicates.iter_identity()).any(|pred| match pred.kind().skip_binder() {
+ ClauseKind::Trait(ref trait_pred) => {
+ trait_pred.def_id() == sized_def_id
+ && matches!(
+ trait_pred.self_ty().kind(),
+ TyKind::Param(ParamTy { index: 0, .. })
+ )
+ }
+ ClauseKind::RegionOutlives(_)
+ | ClauseKind::TypeOutlives(_)
+ | ClauseKind::Projection(_)
+ | ClauseKind::ConstArgHasType(_, _)
+ | ClauseKind::WellFormed(_)
+ | ClauseKind::ConstEvaluatable(_)
+ | ClauseKind::HostEffect(..)
+ | ClauseKind::UnstableFeature(_) => false,
+ })
+ }
+
+ #[tracing::instrument(skip(self), ret)]
+ fn item_bounds(
+ self,
+ def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
+ explicit_item_bounds(self, def_id).map_bound(|bounds| {
+ Clauses::new_from_iter(self, elaborate(self, bounds).collect::<Vec<_>>())
+ })
+ }
+
+ #[tracing::instrument(skip(self), ret)]
+ fn item_self_bounds(
+ self,
+ def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
+ explicit_item_bounds(self, def_id).map_bound(|bounds| {
+ Clauses::new_from_iter(
+ self,
+ elaborate(self, bounds).filter_only_self().collect::<Vec<_>>(),
+ )
+ })
+ }
+
+ fn item_non_self_bounds(
+ self,
+ def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
+ let all_bounds: FxHashSet<_> = self.item_bounds(def_id).skip_binder().into_iter().collect();
+ let own_bounds: FxHashSet<_> =
+ self.item_self_bounds(def_id).skip_binder().into_iter().collect();
+ if all_bounds.len() == own_bounds.len() {
+ EarlyBinder::bind(Clauses::new_from_iter(self, []))
+ } else {
+ EarlyBinder::bind(Clauses::new_from_iter(
+ self,
+ all_bounds.difference(&own_bounds).cloned(),
+ ))
+ }
+ }
+
+ #[tracing::instrument(level = "debug", skip(self), ret)]
+ fn predicates_of(
+ self,
+ def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
+ let predicates = self.db().generic_predicates_ns(def_id.try_into().unwrap());
+ let predicates: Vec<_> = predicates.iter().cloned().collect();
+ EarlyBinder::bind(predicates.into_iter())
+ }
+
+ #[tracing::instrument(level = "debug", skip(self), ret)]
+ fn own_predicates_of(
+ self,
+ def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
+ let predicates = self.db().generic_predicates_without_parent_ns(def_id.try_into().unwrap());
+ let predicates: Vec<_> = predicates.iter().cloned().collect();
+ EarlyBinder::bind(predicates.into_iter())
+ }
+
+ #[tracing::instrument(skip(self), ret)]
+ fn explicit_super_predicates_of(
+ self,
+ def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>
+ {
+ let predicates: Vec<(Clause<'db>, Span)> = self
+ .db()
+ .generic_predicates_ns(def_id.try_into().unwrap())
+ .iter()
+ .cloned()
+ .map(|p| (p, Span::dummy()))
+ .collect();
+ rustc_type_ir::EarlyBinder::bind(predicates)
+ }
+
+ #[tracing::instrument(skip(self), ret)]
+ fn explicit_implied_predicates_of(
+ self,
+ def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>
+ {
+ let predicates: Vec<(Clause<'db>, Span)> = self
+ .db()
+ .generic_predicates_ns(def_id.try_into().unwrap())
+ .iter()
+ .cloned()
+ .map(|p| (p, Span::dummy()))
+ .collect();
+ rustc_type_ir::EarlyBinder::bind(predicates)
+ }
+
+ fn impl_super_outlives(
+ self,
+ impl_def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
+ let impl_id = match impl_def_id {
+ SolverDefId::ImplId(id) => id,
+ _ => unreachable!(),
+ };
+ let trait_ref = self.db().impl_trait_ns(impl_id).expect("expected an impl of trait");
+ trait_ref.map_bound(|trait_ref| {
+ let clause: Clause<'_> = trait_ref.upcast(self);
+ Clauses::new_from_iter(
+ self,
+ rustc_type_ir::elaborate::elaborate(self, [clause]).filter(|clause| {
+ matches!(
+ clause.kind().skip_binder(),
+ ClauseKind::TypeOutlives(_) | ClauseKind::RegionOutlives(_)
+ )
+ }),
+ )
+ })
+ }
+
+ fn const_conditions(
+ self,
+ def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<
+ Self,
+ impl IntoIterator<Item = rustc_type_ir::Binder<Self, rustc_type_ir::TraitRef<Self>>>,
+ > {
+ rustc_type_ir::EarlyBinder::bind([unimplemented!()])
+ }
+
+ fn has_target_features(self, def_id: Self::DefId) -> bool {
+ false
+ }
+
+ fn require_lang_item(
+ self,
+ lang_item: rustc_type_ir::lang_items::TraitSolverLangItem,
+ ) -> Self::DefId {
+ let lang_item = match lang_item {
+ rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFn => LangItem::AsyncFn,
+ rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnKindHelper => unimplemented!(),
+ rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnKindUpvars => unimplemented!(),
+ rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnMut => LangItem::AsyncFnMut,
+ rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnOnce => LangItem::AsyncFnOnce,
+ rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnOnceOutput => {
+ LangItem::AsyncFnOnceOutput
+ }
+ rustc_type_ir::lang_items::TraitSolverLangItem::AsyncIterator => unimplemented!(),
+ rustc_type_ir::lang_items::TraitSolverLangItem::CallOnceFuture => {
+ LangItem::CallOnceFuture
+ }
+ rustc_type_ir::lang_items::TraitSolverLangItem::CallRefFuture => {
+ LangItem::CallRefFuture
+ }
+ rustc_type_ir::lang_items::TraitSolverLangItem::Clone => LangItem::Clone,
+ rustc_type_ir::lang_items::TraitSolverLangItem::Copy => LangItem::Copy,
+ rustc_type_ir::lang_items::TraitSolverLangItem::Coroutine => LangItem::Coroutine,
+ rustc_type_ir::lang_items::TraitSolverLangItem::CoroutineReturn => {
+ LangItem::CoroutineReturn
+ }
+ rustc_type_ir::lang_items::TraitSolverLangItem::CoroutineYield => {
+ LangItem::CoroutineYield
+ }
+ rustc_type_ir::lang_items::TraitSolverLangItem::Destruct => LangItem::Destruct,
+ rustc_type_ir::lang_items::TraitSolverLangItem::DiscriminantKind => {
+ LangItem::DiscriminantKind
+ }
+ rustc_type_ir::lang_items::TraitSolverLangItem::Drop => LangItem::Drop,
+ rustc_type_ir::lang_items::TraitSolverLangItem::DynMetadata => LangItem::DynMetadata,
+ rustc_type_ir::lang_items::TraitSolverLangItem::Fn => LangItem::Fn,
+ rustc_type_ir::lang_items::TraitSolverLangItem::FnMut => LangItem::FnMut,
+ rustc_type_ir::lang_items::TraitSolverLangItem::FnOnce => LangItem::FnOnce,
+ rustc_type_ir::lang_items::TraitSolverLangItem::FnPtrTrait => LangItem::FnPtrTrait,
+ rustc_type_ir::lang_items::TraitSolverLangItem::FusedIterator => unimplemented!(),
+ rustc_type_ir::lang_items::TraitSolverLangItem::Future => LangItem::Future,
+ rustc_type_ir::lang_items::TraitSolverLangItem::FutureOutput => LangItem::FutureOutput,
+ rustc_type_ir::lang_items::TraitSolverLangItem::Iterator => LangItem::Iterator,
+ rustc_type_ir::lang_items::TraitSolverLangItem::Metadata => LangItem::Metadata,
+ rustc_type_ir::lang_items::TraitSolverLangItem::Option => LangItem::Option,
+ rustc_type_ir::lang_items::TraitSolverLangItem::PointeeTrait => LangItem::PointeeTrait,
+ rustc_type_ir::lang_items::TraitSolverLangItem::Poll => LangItem::Poll,
+ rustc_type_ir::lang_items::TraitSolverLangItem::Sized => LangItem::Sized,
+ rustc_type_ir::lang_items::TraitSolverLangItem::MetaSized => LangItem::MetaSized,
+ rustc_type_ir::lang_items::TraitSolverLangItem::PointeeSized => LangItem::PointeeSized,
+ rustc_type_ir::lang_items::TraitSolverLangItem::TransmuteTrait => {
+ LangItem::TransmuteTrait
+ }
+ rustc_type_ir::lang_items::TraitSolverLangItem::Tuple => LangItem::Tuple,
+ rustc_type_ir::lang_items::TraitSolverLangItem::Unpin => LangItem::Unpin,
+ rustc_type_ir::lang_items::TraitSolverLangItem::Unsize => LangItem::Unsize,
+ rustc_type_ir::lang_items::TraitSolverLangItem::BikeshedGuaranteedNoDrop => {
+ unimplemented!()
+ }
+ };
+ let target = hir_def::lang_item::lang_item(
+ self.db(),
+ self.krate.expect("Must have self.krate"),
+ lang_item,
+ )
+ .unwrap_or_else(|| panic!("Lang item {lang_item:?} required but not found."));
+ match target {
+ hir_def::lang_item::LangItemTarget::EnumId(enum_id) => enum_id.into(),
+ hir_def::lang_item::LangItemTarget::Function(function_id) => function_id.into(),
+ hir_def::lang_item::LangItemTarget::ImplDef(impl_id) => impl_id.into(),
+ hir_def::lang_item::LangItemTarget::Static(static_id) => static_id.into(),
+ hir_def::lang_item::LangItemTarget::Struct(struct_id) => struct_id.into(),
+ hir_def::lang_item::LangItemTarget::Union(union_id) => union_id.into(),
+ hir_def::lang_item::LangItemTarget::TypeAlias(type_alias_id) => type_alias_id.into(),
+ hir_def::lang_item::LangItemTarget::Trait(trait_id) => trait_id.into(),
+ hir_def::lang_item::LangItemTarget::EnumVariant(enum_variant_id) => unimplemented!(),
+ }
+ }
+
+ #[allow(clippy::match_like_matches_macro)]
+ fn is_lang_item(
+ self,
+ def_id: Self::DefId,
+ lang_item: rustc_type_ir::lang_items::TraitSolverLangItem,
+ ) -> bool {
+ use rustc_type_ir::lang_items::TraitSolverLangItem::*;
+
+ // FIXME: derive PartialEq on TraitSolverLangItem
+ self.as_lang_item(def_id).map_or(false, |l| match (l, lang_item) {
+ (AsyncFn, AsyncFn) => true,
+ (AsyncFnKindHelper, AsyncFnKindHelper) => true,
+ (AsyncFnKindUpvars, AsyncFnKindUpvars) => true,
+ (AsyncFnMut, AsyncFnMut) => true,
+ (AsyncFnOnce, AsyncFnOnce) => true,
+ (AsyncFnOnceOutput, AsyncFnOnceOutput) => true,
+ (AsyncIterator, AsyncIterator) => true,
+ (CallOnceFuture, CallOnceFuture) => true,
+ (CallRefFuture, CallRefFuture) => true,
+ (Clone, Clone) => true,
+ (Copy, Copy) => true,
+ (Coroutine, Coroutine) => true,
+ (CoroutineReturn, CoroutineReturn) => true,
+ (CoroutineYield, CoroutineYield) => true,
+ (Destruct, Destruct) => true,
+ (DiscriminantKind, DiscriminantKind) => true,
+ (Drop, Drop) => true,
+ (DynMetadata, DynMetadata) => true,
+ (Fn, Fn) => true,
+ (FnMut, FnMut) => true,
+ (FnOnce, FnOnce) => true,
+ (FnPtrTrait, FnPtrTrait) => true,
+ (FusedIterator, FusedIterator) => true,
+ (Future, Future) => true,
+ (FutureOutput, FutureOutput) => true,
+ (Iterator, Iterator) => true,
+ (Metadata, Metadata) => true,
+ (Option, Option) => true,
+ (PointeeTrait, PointeeTrait) => true,
+ (Poll, Poll) => true,
+ (Sized, Sized) => true,
+ (TransmuteTrait, TransmuteTrait) => true,
+ (Tuple, Tuple) => true,
+ (Unpin, Unpin) => true,
+ (Unsize, Unsize) => true,
+ _ => false,
+ })
+ }
+
+ fn as_lang_item(
+ self,
+ def_id: Self::DefId,
+ ) -> Option<rustc_type_ir::lang_items::TraitSolverLangItem> {
+ use rustc_type_ir::lang_items::TraitSolverLangItem;
+
+ let def_id: AttrDefId = match def_id {
+ SolverDefId::TraitId(id) => id.into(),
+ SolverDefId::TypeAliasId(id) => id.into(),
+ _ => panic!("Unexpected SolverDefId in as_lang_item"),
+ };
+ let lang_item = self.db().lang_attr(def_id)?;
+ Some(match lang_item {
+ LangItem::Sized => TraitSolverLangItem::Sized,
+ LangItem::MetaSized => TraitSolverLangItem::MetaSized,
+ LangItem::PointeeSized => TraitSolverLangItem::PointeeSized,
+ LangItem::Unsize => TraitSolverLangItem::Unsize,
+ LangItem::StructuralPeq => return None,
+ LangItem::StructuralTeq => return None,
+ LangItem::Copy => TraitSolverLangItem::Copy,
+ LangItem::Clone => TraitSolverLangItem::Clone,
+ LangItem::Sync => return None,
+ LangItem::DiscriminantKind => TraitSolverLangItem::DiscriminantKind,
+ LangItem::Discriminant => return None,
+ LangItem::PointeeTrait => TraitSolverLangItem::PointeeTrait,
+ LangItem::Metadata => TraitSolverLangItem::Metadata,
+ LangItem::DynMetadata => TraitSolverLangItem::DynMetadata,
+ LangItem::Freeze => return None,
+ LangItem::FnPtrTrait => TraitSolverLangItem::FnPtrTrait,
+ LangItem::FnPtrAddr => return None,
+ LangItem::Drop => TraitSolverLangItem::Drop,
+ LangItem::Destruct => TraitSolverLangItem::Destruct,
+ LangItem::CoerceUnsized => return None,
+ LangItem::DispatchFromDyn => return None,
+ LangItem::TransmuteOpts => return None,
+ LangItem::TransmuteTrait => TraitSolverLangItem::TransmuteTrait,
+ LangItem::Add => return None,
+ LangItem::Sub => return None,
+ LangItem::Mul => return None,
+ LangItem::Div => return None,
+ LangItem::Rem => return None,
+ LangItem::Neg => return None,
+ LangItem::Not => return None,
+ LangItem::BitXor => return None,
+ LangItem::BitAnd => return None,
+ LangItem::BitOr => return None,
+ LangItem::Shl => return None,
+ LangItem::Shr => return None,
+ LangItem::AddAssign => return None,
+ LangItem::SubAssign => return None,
+ LangItem::MulAssign => return None,
+ LangItem::DivAssign => return None,
+ LangItem::RemAssign => return None,
+ LangItem::BitXorAssign => return None,
+ LangItem::BitAndAssign => return None,
+ LangItem::BitOrAssign => return None,
+ LangItem::ShlAssign => return None,
+ LangItem::ShrAssign => return None,
+ LangItem::Index => return None,
+ LangItem::IndexMut => return None,
+ LangItem::UnsafeCell => return None,
+ LangItem::VaList => return None,
+ LangItem::Deref => return None,
+ LangItem::DerefMut => return None,
+ LangItem::DerefTarget => return None,
+ LangItem::Receiver => return None,
+ LangItem::Fn => TraitSolverLangItem::Fn,
+ LangItem::FnMut => TraitSolverLangItem::FnMut,
+ LangItem::FnOnce => TraitSolverLangItem::FnOnce,
+ LangItem::FnOnceOutput => return None,
+ LangItem::Future => TraitSolverLangItem::Future,
+ LangItem::CoroutineState => return None,
+ LangItem::Coroutine => TraitSolverLangItem::Coroutine,
+ LangItem::CoroutineReturn => TraitSolverLangItem::CoroutineReturn,
+ LangItem::CoroutineYield => TraitSolverLangItem::CoroutineYield,
+ LangItem::Unpin => TraitSolverLangItem::Unpin,
+ LangItem::Pin => return None,
+ LangItem::PartialEq => return None,
+ LangItem::PartialOrd => return None,
+ LangItem::CVoid => return None,
+ LangItem::Panic => return None,
+ LangItem::PanicNounwind => return None,
+ LangItem::PanicFmt => return None,
+ LangItem::PanicDisplay => return None,
+ LangItem::ConstPanicFmt => return None,
+ LangItem::PanicBoundsCheck => return None,
+ LangItem::PanicMisalignedPointerDereference => return None,
+ LangItem::PanicInfo => return None,
+ LangItem::PanicLocation => return None,
+ LangItem::PanicImpl => return None,
+ LangItem::PanicCannotUnwind => return None,
+ LangItem::BeginPanic => return None,
+ LangItem::FormatAlignment => return None,
+ LangItem::FormatArgument => return None,
+ LangItem::FormatArguments => return None,
+ LangItem::FormatCount => return None,
+ LangItem::FormatPlaceholder => return None,
+ LangItem::FormatUnsafeArg => return None,
+ LangItem::ExchangeMalloc => return None,
+ LangItem::BoxFree => return None,
+ LangItem::DropInPlace => return None,
+ LangItem::AllocLayout => return None,
+ LangItem::Start => return None,
+ LangItem::EhPersonality => return None,
+ LangItem::EhCatchTypeinfo => return None,
+ LangItem::OwnedBox => return None,
+ LangItem::PhantomData => return None,
+ LangItem::ManuallyDrop => return None,
+ LangItem::MaybeUninit => return None,
+ LangItem::AlignOffset => return None,
+ LangItem::Termination => return None,
+ LangItem::Try => return None,
+ LangItem::Tuple => TraitSolverLangItem::Tuple,
+ LangItem::SliceLen => return None,
+ LangItem::TryTraitFromResidual => return None,
+ LangItem::TryTraitFromOutput => return None,
+ LangItem::TryTraitBranch => return None,
+ LangItem::TryTraitFromYeet => return None,
+ LangItem::PointerLike => return None,
+ LangItem::ConstParamTy => return None,
+ LangItem::Poll => TraitSolverLangItem::Poll,
+ LangItem::PollReady => return None,
+ LangItem::PollPending => return None,
+ LangItem::ResumeTy => return None,
+ LangItem::GetContext => return None,
+ LangItem::Context => return None,
+ LangItem::FuturePoll => return None,
+ LangItem::FutureOutput => TraitSolverLangItem::FutureOutput,
+ LangItem::Option => TraitSolverLangItem::Option,
+ LangItem::OptionSome => return None,
+ LangItem::OptionNone => return None,
+ LangItem::ResultOk => return None,
+ LangItem::ResultErr => return None,
+ LangItem::ControlFlowContinue => return None,
+ LangItem::ControlFlowBreak => return None,
+ LangItem::IntoFutureIntoFuture => return None,
+ LangItem::IntoIterIntoIter => return None,
+ LangItem::IteratorNext => return None,
+ LangItem::Iterator => TraitSolverLangItem::Iterator,
+ LangItem::PinNewUnchecked => return None,
+ LangItem::RangeFrom => return None,
+ LangItem::RangeFull => return None,
+ LangItem::RangeInclusiveStruct => return None,
+ LangItem::RangeInclusiveNew => return None,
+ LangItem::Range => return None,
+ LangItem::RangeToInclusive => return None,
+ LangItem::RangeTo => return None,
+ LangItem::String => return None,
+ LangItem::CStr => return None,
+ LangItem::AsyncFn => TraitSolverLangItem::AsyncFn,
+ LangItem::AsyncFnMut => TraitSolverLangItem::AsyncFnMut,
+ LangItem::AsyncFnOnce => TraitSolverLangItem::AsyncFnOnce,
+ LangItem::AsyncFnOnceOutput => TraitSolverLangItem::AsyncFnOnceOutput,
+ LangItem::CallRefFuture => TraitSolverLangItem::CallRefFuture,
+ LangItem::CallOnceFuture => TraitSolverLangItem::CallOnceFuture,
+ LangItem::Ordering => return None,
+ LangItem::PanicNullPointerDereference => return None,
+ LangItem::ReceiverTarget => return None,
+ LangItem::UnsafePinned => return None,
+ LangItem::AsyncFnOnceOutput => TraitSolverLangItem::AsyncFnOnceOutput,
+ })
+ }
+
+ fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId> {
+ let trait_ = match def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => unreachable!(),
+ };
+ trait_.trait_items(self.db()).associated_types().map(|id| id.into())
+ }
+
+ fn for_each_relevant_impl(
+ self,
+ trait_def_id: Self::DefId,
+ self_ty: Self::Ty,
+ mut f: impl FnMut(Self::DefId),
+ ) {
+ let trait_ = match trait_def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => panic!("for_each_relevant_impl called for non-trait"),
+ };
+
+ let self_ty_fp = TyFingerprint::for_trait_impl_ns(&self_ty);
+ let fps: &[TyFingerprint] = match self_ty.kind() {
+ TyKind::Infer(InferTy::IntVar(..)) => &ALL_INT_FPS,
+ TyKind::Infer(InferTy::FloatVar(..)) => &ALL_FLOAT_FPS,
+ _ => self_ty_fp.as_slice(),
+ };
+
+ if fps.is_empty() {
+ for_trait_impls(
+ self.db(),
+ self.krate.expect("Must have self.krate"),
+ self.block,
+ trait_,
+ self_ty_fp,
+ |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| {
+ b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break()
+ });
+ if contains_errors {
+ continue;
+ }
+
+ f(SolverDefId::ImplId(i));
+ }
+ ControlFlow::Continue(())
+ },
+ );
+ } else {
+ for_trait_impls(
+ self.db(),
+ self.krate.expect("Must have self.krate"),
+ self.block,
+ trait_,
+ self_ty_fp,
+ |impls| {
+ 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| {
+ b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break()
+ });
+ if contains_errors {
+ continue;
+ }
+
+ f(SolverDefId::ImplId(i));
+ }
+ }
+ ControlFlow::Continue(())
+ },
+ );
+ }
+ }
+
+ fn has_item_definition(self, def_id: Self::DefId) -> bool {
+ // FIXME: should check if has value
+ true
+ }
+
+ fn impl_is_default(self, impl_def_id: Self::DefId) -> bool {
+ // FIXME
+ false
+ }
+
+ #[tracing::instrument(skip(self), ret)]
+ fn impl_trait_ref(
+ self,
+ impl_def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<Self, rustc_type_ir::TraitRef<Self>> {
+ let impl_id = match impl_def_id {
+ SolverDefId::ImplId(id) => id,
+ _ => panic!("Unexpected SolverDefId in impl_trait_ref"),
+ };
+
+ let db = self.db();
+
+ db.impl_trait_ns(impl_id)
+ // ImplIds for impls where the trait ref can't be resolved should never reach trait solving
+ .expect("invalid impl passed to trait solver")
+ }
+
+ fn impl_polarity(self, impl_def_id: Self::DefId) -> rustc_type_ir::ImplPolarity {
+ let impl_id = match impl_def_id {
+ SolverDefId::ImplId(id) => id,
+ _ => unreachable!(),
+ };
+ let impl_data = self.db().impl_signature(impl_id);
+ if impl_data.flags.contains(ImplFlags::NEGATIVE) {
+ ImplPolarity::Negative
+ } else {
+ ImplPolarity::Positive
+ }
+ }
+
+ fn trait_is_auto(self, trait_def_id: Self::DefId) -> bool {
+ let trait_ = match trait_def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => panic!("Unexpected SolverDefId in trait_is_auto"),
+ };
+ let trait_data = self.db().trait_signature(trait_);
+ trait_data.flags.contains(TraitFlags::AUTO)
+ }
+
+ fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool {
+ matches!(trait_def_id, SolverDefId::TraitAliasId(_))
+ }
+
+ fn trait_is_dyn_compatible(self, trait_def_id: Self::DefId) -> bool {
+ let trait_ = match trait_def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => unreachable!(),
+ };
+ crate::dyn_compatibility::dyn_compatibility(self.db(), trait_).is_none()
+ }
+
+ fn trait_is_fundamental(self, def_id: Self::DefId) -> bool {
+ let trait_ = match def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => panic!("Unexpected SolverDefId in trait_is_fundamental"),
+ };
+ let trait_data = self.db().trait_signature(trait_);
+ trait_data.flags.contains(TraitFlags::FUNDAMENTAL)
+ }
+
+ fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool {
+ // FIXME(next-solver)
+ true
+ }
+
+ fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool {
+ // FIXME(next-solver)
+ false
+ }
+
+ fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed {
+ panic!("Bug encountered in next-trait-solver.")
+ }
+
+ fn is_general_coroutine(self, coroutine_def_id: Self::DefId) -> bool {
+ // FIXME(next-solver)
+ true
+ }
+
+ fn coroutine_is_async(self, coroutine_def_id: Self::DefId) -> bool {
+ // FIXME(next-solver)
+ true
+ }
+
+ fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool {
+ // FIXME(next-solver)
+ false
+ }
+
+ fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool {
+ // FIXME(next-solver)
+ false
+ }
+
+ fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams {
+ let id = match adt_def_id {
+ SolverDefId::AdtId(id) => id,
+ _ => unreachable!(),
+ };
+ let def = AdtDef::new(id, self);
+ let num_params = self.generics_of(adt_def_id).count();
+
+ let maybe_unsizing_param_idx = |arg: GenericArg<'db>| match arg.kind() {
+ GenericArgKind::Type(ty) => match ty.kind() {
+ rustc_type_ir::TyKind::Param(p) => Some(p.index),
+ _ => None,
+ },
+ GenericArgKind::Lifetime(_) => None,
+ GenericArgKind::Const(ct) => match ct.kind() {
+ rustc_type_ir::ConstKind::Param(p) => Some(p.index),
+ _ => None,
+ },
+ };
+
+ // The last field of the structure has to exist and contain type/const parameters.
+ let variant = def.non_enum_variant();
+ let fields = variant.fields(self.db());
+ let Some((tail_field, prefix_fields)) = fields.split_last() else {
+ return UnsizingParams(DenseBitSet::new_empty(num_params));
+ };
+
+ let field_types = self.db().field_types_ns(variant.id());
+ let mut unsizing_params = DenseBitSet::new_empty(num_params);
+ let ty = field_types[tail_field.0];
+ for arg in ty.instantiate_identity().walk() {
+ if let Some(i) = maybe_unsizing_param_idx(arg) {
+ unsizing_params.insert(i);
+ }
+ }
+
+ // Ensure none of the other fields mention the parameters used
+ // in unsizing.
+ for field in prefix_fields {
+ for arg in field_types[field.0].instantiate_identity().walk() {
+ if let Some(i) = maybe_unsizing_param_idx(arg) {
+ unsizing_params.remove(i);
+ }
+ }
+ }
+
+ UnsizingParams(unsizing_params)
+ }
+
+ fn anonymize_bound_vars<T: rustc_type_ir::TypeFoldable<Self>>(
+ self,
+ value: rustc_type_ir::Binder<Self, T>,
+ ) -> rustc_type_ir::Binder<Self, T> {
+ struct Anonymize<'a, 'db> {
+ interner: DbInterner<'db>,
+ map: &'a mut FxIndexMap<BoundVar, BoundVarKind>,
+ }
+ impl<'db> BoundVarReplacerDelegate<'db> for Anonymize<'_, 'db> {
+ fn replace_region(&mut self, br: BoundRegion) -> Region<'db> {
+ let entry = self.map.entry(br.var);
+ let index = entry.index();
+ let var = BoundVar::from_usize(index);
+ let kind = (*entry.or_insert_with(|| BoundVarKind::Region(BoundRegionKind::Anon)))
+ .expect_region();
+ let br = BoundRegion { var, kind };
+ Region::new_bound(self.interner, DebruijnIndex::ZERO, br)
+ }
+ fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> {
+ let entry = self.map.entry(bt.var);
+ let index = entry.index();
+ let var = BoundVar::from_usize(index);
+ let kind =
+ (*entry.or_insert_with(|| BoundVarKind::Ty(BoundTyKind::Anon))).expect_ty();
+ Ty::new_bound(self.interner, DebruijnIndex::ZERO, BoundTy { var, kind })
+ }
+ fn replace_const(&mut self, bv: BoundVar) -> Const<'db> {
+ let entry = self.map.entry(bv);
+ let index = entry.index();
+ let var = BoundVar::from_usize(index);
+ let () = (*entry.or_insert_with(|| BoundVarKind::Const)).expect_const();
+ Const::new_bound(self.interner, DebruijnIndex::ZERO, var)
+ }
+ }
+
+ let mut map = Default::default();
+ let delegate = Anonymize { interner: self, map: &mut map };
+ let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate);
+ let bound_vars = CollectAndApply::collect_and_apply(map.into_values(), |xs| {
+ BoundVarKinds::new_from_iter(self, xs.iter().cloned())
+ });
+ Binder::bind_with_vars(inner, bound_vars)
+ }
+
+ fn opaque_types_defined_by(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds {
+ // FIXME(next-solver)
+ SolverDefIds::new_from_iter(self, [])
+ }
+
+ fn alias_has_const_conditions(self, def_id: Self::DefId) -> bool {
+ // FIXME(next-solver)
+ false
+ }
+
+ fn explicit_implied_const_bounds(
+ self,
+ def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<
+ Self,
+ impl IntoIterator<Item = rustc_type_ir::Binder<Self, rustc_type_ir::TraitRef<Self>>>,
+ > {
+ // FIXME(next-solver)
+ rustc_type_ir::EarlyBinder::bind([])
+ }
+
+ fn fn_is_const(self, def_id: Self::DefId) -> bool {
+ let id = match def_id {
+ SolverDefId::FunctionId(id) => id,
+ _ => unreachable!(),
+ };
+ self.db().function_signature(id).flags.contains(FnFlags::CONST)
+ }
+
+ fn impl_is_const(self, def_id: Self::DefId) -> bool {
+ false
+ }
+
+ fn opt_alias_variances(
+ self,
+ kind: impl Into<rustc_type_ir::AliasTermKind>,
+ def_id: Self::DefId,
+ ) -> Option<Self::VariancesOf> {
+ None
+ }
+
+ fn type_of_opaque_hir_typeck(
+ self,
+ def_id: Self::LocalDefId,
+ ) -> rustc_type_ir::EarlyBinder<Self, Self::Ty> {
+ // FIXME(next-solver)
+ unimplemented!()
+ }
+
+ fn coroutine_hidden_types(
+ self,
+ def_id: Self::DefId,
+ ) -> rustc_type_ir::EarlyBinder<
+ Self,
+ rustc_type_ir::Binder<Self, rustc_type_ir::CoroutineWitnessTypes<Self>>,
+ > {
+ // FIXME(next-solver)
+ unimplemented!()
+ }
+
+ fn is_default_trait(self, def_id: Self::DefId) -> bool {
+ self.as_lang_item(def_id).map_or(false, |l| matches!(l, TraitSolverLangItem::Sized))
+ }
+
+ fn trait_is_coinductive(self, trait_def_id: Self::DefId) -> bool {
+ let id = match trait_def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => unreachable!(),
+ };
+ self.db().trait_signature(id).flags.contains(TraitFlags::COINDUCTIVE)
+ }
+
+ fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool {
+ let id = match trait_def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => unreachable!(),
+ };
+ self.db().trait_signature(id).flags.contains(TraitFlags::UNSAFE)
+ }
+
+ fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool {
+ false
+ }
+
+ fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool {
+ false
+ }
+
+ fn next_trait_solver_globally(self) -> bool {
+ true
+ }
+
+ fn opaque_types_and_coroutines_defined_by(
+ self,
+ defining_anchor: Self::LocalDefId,
+ ) -> Self::LocalDefIds {
+ // FIXME(next-solver)
+ unimplemented!()
+ }
+}
+
+impl<'db> DbInterner<'db> {
+ pub fn shift_bound_var_indices<T>(self, bound_vars: usize, value: T) -> T
+ where
+ T: rustc_type_ir::TypeFoldable<Self>,
+ {
+ let shift_bv = |bv: BoundVar| BoundVar::from_usize(bv.as_usize() + bound_vars);
+ self.replace_escaping_bound_vars_uncached(
+ value,
+ FnMutDelegate {
+ regions: &mut |r: BoundRegion| {
+ Region::new_bound(
+ self,
+ DebruijnIndex::ZERO,
+ BoundRegion { var: shift_bv(r.var), kind: r.kind },
+ )
+ },
+ types: &mut |t: BoundTy| {
+ Ty::new_bound(
+ self,
+ DebruijnIndex::ZERO,
+ BoundTy { var: shift_bv(t.var), kind: t.kind },
+ )
+ },
+ consts: &mut |c| Const::new_bound(self, DebruijnIndex::ZERO, shift_bv(c)),
+ },
+ )
+ }
+
+ pub fn replace_escaping_bound_vars_uncached<T: rustc_type_ir::TypeFoldable<DbInterner<'db>>>(
+ self,
+ value: T,
+ delegate: impl BoundVarReplacerDelegate<'db>,
+ ) -> T {
+ if !value.has_escaping_bound_vars() {
+ value
+ } else {
+ let mut replacer = BoundVarReplacer::new(self, delegate);
+ value.fold_with(&mut replacer)
+ }
+ }
+
+ pub fn replace_bound_vars_uncached<T: rustc_type_ir::TypeFoldable<DbInterner<'db>>>(
+ self,
+ value: Binder<'db, T>,
+ delegate: impl BoundVarReplacerDelegate<'db>,
+ ) -> T {
+ self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate)
+ }
+}
+
+macro_rules! TrivialTypeTraversalImpls {
+ ($($ty:ty,)+) => {
+ $(
+ impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for $ty {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ _: &mut F,
+ ) -> ::std::result::Result<Self, F::Error> {
+ Ok(self)
+ }
+
+ #[inline]
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(
+ self,
+ _: &mut F,
+ ) -> Self {
+ self
+ }
+ }
+
+ impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for $ty {
+ #[inline]
+ fn visit_with<F: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ _: &mut F)
+ -> F::Result
+ {
+ <F::Result as rustc_ast_ir::visit::VisitorResult>::output()
+ }
+ }
+ )+
+ };
+}
+
+TrivialTypeTraversalImpls! {
+ SolverDefId,
+ Pattern<'db>,
+ Safety,
+ FnAbi,
+ Span,
+ ParamConst,
+ ParamTy,
+ BoundRegion,
+ BoundVar,
+ Placeholder<BoundRegion>,
+ Placeholder<BoundTy>,
+ Placeholder<BoundVar>,
+}
+
+pub(crate) use tls_cache::with_new_cache;
+mod tls_cache {
+ use crate::db::HirDatabase;
+
+ use super::DbInterner;
+ use rustc_type_ir::search_graph::GlobalCache;
+ use std::cell::RefCell;
+
+ scoped_tls::scoped_thread_local!(static GLOBAL_CACHE: RefCell<rustc_type_ir::search_graph::GlobalCache<DbInterner<'static>>>);
+
+ pub(crate) fn with_new_cache<T>(f: impl FnOnce() -> T) -> T {
+ GLOBAL_CACHE.set(&RefCell::new(GlobalCache::default()), f)
+ }
+
+ pub(super) fn with_cache<'db, T>(
+ _db: &'db dyn HirDatabase,
+ f: impl FnOnce(&mut GlobalCache<DbInterner<'db>>) -> T,
+ ) -> T {
+ // SAFETY: No idea
+ let call = move |slot: &RefCell<_>| {
+ f(unsafe {
+ std::mem::transmute::<
+ &mut GlobalCache<DbInterner<'static>>,
+ &mut GlobalCache<DbInterner<'db>>,
+ >(&mut *slot.borrow_mut())
+ })
+ };
+ if GLOBAL_CACHE.is_set() {
+ GLOBAL_CACHE.with(call)
+ } else {
+ GLOBAL_CACHE.set(&RefCell::new(GlobalCache::default()), || GLOBAL_CACHE.with(call))
+ }
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/ir_print.rs b/crates/hir-ty/src/next_solver/ir_print.rs
new file mode 100644
index 0000000..4d32b27
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/ir_print.rs
@@ -0,0 +1,267 @@
+//! Things related to IR printing in the next-trait-solver.
+
+use std::any::type_name_of_val;
+
+use rustc_type_ir::inherent::SliceLike;
+use rustc_type_ir::{self as ty, ir_print::IrPrint};
+
+use crate::db::HirDatabase;
+
+use super::SolverDefId;
+use super::interner::DbInterner;
+
+impl<'db> IrPrint<ty::AliasTy<Self>> for DbInterner<'db> {
+ fn print(t: &ty::AliasTy<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(t: &ty::AliasTy<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ salsa::with_attached_database(|db| match t.def_id {
+ SolverDefId::TypeAliasId(id) => fmt.write_str(&format!(
+ "AliasTy({:?}[{:?}])",
+ db.as_view::<dyn HirDatabase>().type_alias_signature(id).name.as_str(),
+ t.args
+ )),
+ SolverDefId::InternedOpaqueTyId(id) => {
+ fmt.write_str(&format!("AliasTy({:?}[{:?}])", id, t.args))
+ }
+ _ => panic!("Expected TypeAlias or OpaqueTy."),
+ })
+ .unwrap_or_else(|| fmt.write_str(&format!("AliasTy({:?}[{:?}])", t.def_id, t.args)))
+ }
+}
+
+impl<'db> IrPrint<ty::AliasTerm<Self>> for DbInterner<'db> {
+ fn print(t: &ty::AliasTerm<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(t: &ty::AliasTerm<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ salsa::with_attached_database(|db| match t.def_id {
+ SolverDefId::TypeAliasId(id) => fmt.write_str(&format!(
+ "AliasTerm({:?}[{:?}])",
+ db.as_view::<dyn HirDatabase>().type_alias_signature(id).name.as_str(),
+ t.args
+ )),
+ SolverDefId::InternedOpaqueTyId(id) => {
+ fmt.write_str(&format!("AliasTerm({:?}[{:?}])", id, t.args))
+ }
+ _ => panic!("Expected TypeAlias or OpaqueTy."),
+ })
+ .unwrap_or_else(|| fmt.write_str(&format!("AliasTerm({:?}[{:?}])", t.def_id, t.args)))
+ }
+}
+impl<'db> IrPrint<ty::TraitRef<Self>> for DbInterner<'db> {
+ fn print(t: &ty::TraitRef<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(t: &ty::TraitRef<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ salsa::with_attached_database(|db| {
+ let trait_ = match t.def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => panic!("Expected trait."),
+ };
+ let self_ty = &t.args.as_slice()[0];
+ let trait_args = &t.args.as_slice()[1..];
+ if trait_args.is_empty() {
+ fmt.write_str(&format!(
+ "{:?}: {}",
+ self_ty,
+ db.as_view::<dyn HirDatabase>().trait_signature(trait_).name.as_str()
+ ))
+ } else {
+ fmt.write_str(&format!(
+ "{:?}: {}<{:?}>",
+ self_ty,
+ db.as_view::<dyn HirDatabase>().trait_signature(trait_).name.as_str(),
+ trait_args
+ ))
+ }
+ })
+ .unwrap_or_else(|| fmt.write_str(&format!("TraitRef({:?}[{:?}])", t.def_id, t.args)))
+ }
+}
+impl<'db> IrPrint<ty::TraitPredicate<Self>> for DbInterner<'db> {
+ fn print(t: &ty::TraitPredicate<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(
+ t: &ty::TraitPredicate<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t)))
+ }
+}
+impl<'db> IrPrint<rustc_type_ir::HostEffectPredicate<Self>> for DbInterner<'db> {
+ fn print(
+ t: &rustc_type_ir::HostEffectPredicate<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(
+ t: &rustc_type_ir::HostEffectPredicate<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t)))
+ }
+}
+impl<'db> IrPrint<ty::ExistentialTraitRef<Self>> for DbInterner<'db> {
+ fn print(
+ t: &ty::ExistentialTraitRef<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(
+ t: &ty::ExistentialTraitRef<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ salsa::with_attached_database(|db| {
+ let trait_ = match t.def_id {
+ SolverDefId::TraitId(id) => id,
+ _ => panic!("Expected trait."),
+ };
+ fmt.write_str(&format!(
+ "ExistentialTraitRef({:?}[{:?}])",
+ db.as_view::<dyn HirDatabase>().trait_signature(trait_).name.as_str(),
+ t.args
+ ))
+ })
+ .unwrap_or_else(|| {
+ fmt.write_str(&format!("ExistentialTraitRef({:?}[{:?}])", t.def_id, t.args))
+ })
+ }
+}
+impl<'db> IrPrint<ty::ExistentialProjection<Self>> for DbInterner<'db> {
+ fn print(
+ t: &ty::ExistentialProjection<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(
+ t: &ty::ExistentialProjection<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ salsa::with_attached_database(|db| {
+ let id = match t.def_id {
+ SolverDefId::TypeAliasId(id) => id,
+ _ => panic!("Expected trait."),
+ };
+ fmt.write_str(&format!(
+ "ExistentialProjection(({:?}[{:?}]) -> {:?})",
+ db.as_view::<dyn HirDatabase>().type_alias_signature(id).name.as_str(),
+ t.args,
+ t.term
+ ))
+ })
+ .unwrap_or_else(|| {
+ fmt.write_str(&format!(
+ "ExistentialProjection(({:?}[{:?}]) -> {:?})",
+ t.def_id, t.args, t.term
+ ))
+ })
+ }
+}
+impl<'db> IrPrint<ty::ProjectionPredicate<Self>> for DbInterner<'db> {
+ fn print(
+ t: &ty::ProjectionPredicate<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(
+ t: &ty::ProjectionPredicate<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ salsa::with_attached_database(|db| {
+ let id = match t.projection_term.def_id {
+ SolverDefId::TypeAliasId(id) => id,
+ _ => panic!("Expected trait."),
+ };
+ fmt.write_str(&format!(
+ "ProjectionPredicate(({:?}[{:?}]) -> {:?})",
+ db.as_view::<dyn HirDatabase>().type_alias_signature(id).name.as_str(),
+ t.projection_term.args,
+ t.term
+ ))
+ })
+ .unwrap_or_else(|| {
+ fmt.write_str(&format!(
+ "ProjectionPredicate(({:?}[{:?}]) -> {:?})",
+ t.projection_term.def_id, t.projection_term.args, t.term
+ ))
+ })
+ }
+}
+impl<'db> IrPrint<ty::NormalizesTo<Self>> for DbInterner<'db> {
+ fn print(t: &ty::NormalizesTo<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(
+ t: &ty::NormalizesTo<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t)))
+ }
+}
+impl<'db> IrPrint<ty::SubtypePredicate<Self>> for DbInterner<'db> {
+ fn print(
+ t: &ty::SubtypePredicate<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(
+ t: &ty::SubtypePredicate<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t)))
+ }
+}
+impl<'db> IrPrint<ty::CoercePredicate<Self>> for DbInterner<'db> {
+ fn print(t: &ty::CoercePredicate<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(
+ t: &ty::CoercePredicate<Self>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t)))
+ }
+}
+impl<'db> IrPrint<ty::FnSig<Self>> for DbInterner<'db> {
+ fn print(t: &ty::FnSig<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(t: &ty::FnSig<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t)))
+ }
+}
+
+impl<'db> IrPrint<rustc_type_ir::PatternKind<DbInterner<'db>>> for DbInterner<'db> {
+ fn print(
+ t: &rustc_type_ir::PatternKind<DbInterner<'db>>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ Self::print_debug(t, fmt)
+ }
+
+ fn print_debug(
+ t: &rustc_type_ir::PatternKind<DbInterner<'db>>,
+ fmt: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
+ fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t)))
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/mapping.rs b/crates/hir-ty/src/next_solver/mapping.rs
new file mode 100644
index 0000000..3a3206b
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/mapping.rs
@@ -0,0 +1,1368 @@
+//! Things useful for mapping to/from Chalk and next-trait-solver types.
+
+use base_db::Crate;
+use chalk_ir::{
+ CanonicalVarKind, CanonicalVarKinds, ForeignDefId, InferenceVar, Substitution, TyVariableKind,
+ WellFormed, fold::Shift, interner::HasInterner,
+};
+use hir_def::{
+ CallableDefId, ConstParamId, FunctionId, GeneralConstId, LifetimeParamId, TypeAliasId,
+ TypeOrConstParamId, TypeParamId, signatures::TraitFlags,
+};
+use intern::sym;
+use rustc_type_ir::{
+ AliasTerm, BoundVar, DebruijnIndex, ExistentialProjection, ExistentialTraitRef, Interner as _,
+ OutlivesPredicate, ProjectionPredicate, TypeFoldable, TypeSuperFoldable, TypeVisitable,
+ TypeVisitableExt, UniverseIndex, elaborate,
+ inherent::{BoundVarLike, Clause as _, IntoKind, PlaceholderLike, SliceLike, Ty as _},
+ shift_vars,
+ solve::Goal,
+};
+use salsa::plumbing::AsId;
+
+use crate::{
+ ConcreteConst, ConstScalar, ImplTraitId, Interner,
+ db::{
+ HirDatabase, InternedClosureId, InternedCoroutineId, InternedOpaqueTyId,
+ InternedTypeOrConstParamId,
+ },
+ from_assoc_type_id, from_chalk_trait_id,
+ mapping::ToChalk,
+ next_solver::{
+ Binder, ClauseKind, ConstBytes, TraitPredicate, UnevaluatedConst,
+ interner::{AdtDef, BoundVarKind, BoundVarKinds, DbInterner},
+ },
+ to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id,
+};
+
+use super::{
+ BoundExistentialPredicate, BoundExistentialPredicates, BoundRegion, BoundRegionKind, BoundTy,
+ BoundTyKind, Canonical, CanonicalVars, Clause, Clauses, Const, Ctor, EarlyParamRegion,
+ ErrorGuaranteed, ExistentialPredicate, GenericArg, GenericArgs, ParamConst, ParamEnv, ParamTy,
+ Placeholder, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Predicate, PredicateKind,
+ Region, SolverDefId, SubtypePredicate, Term, TraitRef, Ty, Tys, ValueConst, VariancesOf,
+};
+
+pub fn to_placeholder_idx<T: Clone + std::fmt::Debug>(
+ db: &dyn HirDatabase,
+ id: TypeOrConstParamId,
+ map: impl Fn(BoundVar) -> T,
+) -> Placeholder<T> {
+ let interned_id = InternedTypeOrConstParamId::new(db, id);
+ Placeholder {
+ universe: UniverseIndex::ZERO,
+ bound: map(BoundVar::from_usize(interned_id.as_id().index() as usize)),
+ }
+}
+
+pub fn convert_binder_to_early_binder<'db, T: rustc_type_ir::TypeFoldable<DbInterner<'db>>>(
+ interner: DbInterner<'db>,
+ binder: rustc_type_ir::Binder<DbInterner<'db>, T>,
+) -> rustc_type_ir::EarlyBinder<DbInterner<'db>, T> {
+ let mut folder = BinderToEarlyBinder { interner, debruijn: rustc_type_ir::DebruijnIndex::ZERO };
+ rustc_type_ir::EarlyBinder::bind(binder.skip_binder().fold_with(&mut folder))
+}
+
+struct BinderToEarlyBinder<'db> {
+ interner: DbInterner<'db>,
+ debruijn: rustc_type_ir::DebruijnIndex,
+}
+
+impl<'db> rustc_type_ir::TypeFolder<DbInterner<'db>> for BinderToEarlyBinder<'db> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.interner
+ }
+
+ fn fold_binder<T>(
+ &mut self,
+ t: rustc_type_ir::Binder<DbInterner<'db>, T>,
+ ) -> rustc_type_ir::Binder<DbInterner<'db>, T>
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ self.debruijn.shift_in(1);
+ let result = t.super_fold_with(self);
+ self.debruijn.shift_out(1);
+ result
+ }
+
+ fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
+ match t.kind() {
+ rustc_type_ir::TyKind::Bound(debruijn, bound_ty) if self.debruijn == debruijn => {
+ let var: rustc_type_ir::BoundVar = bound_ty.var();
+ Ty::new(self.cx(), rustc_type_ir::TyKind::Param(ParamTy { index: var.as_u32() }))
+ }
+ _ => t.super_fold_with(self),
+ }
+ }
+
+ fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
+ match r.kind() {
+ rustc_type_ir::ReBound(debruijn, bound_region) if self.debruijn == debruijn => {
+ let var: rustc_type_ir::BoundVar = bound_region.var();
+ Region::new(
+ self.cx(),
+ rustc_type_ir::RegionKind::ReEarlyParam(EarlyParamRegion {
+ index: var.as_u32(),
+ }),
+ )
+ }
+ _ => r,
+ }
+ }
+
+ fn fold_const(&mut self, c: Const<'db>) -> Const<'db> {
+ match c.kind() {
+ rustc_type_ir::ConstKind::Bound(debruijn, var) if self.debruijn == debruijn => {
+ Const::new(
+ self.cx(),
+ rustc_type_ir::ConstKind::Param(ParamConst { index: var.as_u32() }),
+ )
+ }
+ _ => c.super_fold_with(self),
+ }
+ }
+}
+
+pub trait ChalkToNextSolver<'db, Out> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out;
+}
+
+impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> Ty<'db> {
+ Ty::new(
+ interner,
+ match self.kind(Interner) {
+ chalk_ir::TyKind::Adt(adt_id, substitution) => {
+ let def = AdtDef::new(adt_id.0, interner);
+ let args = substitution.to_nextsolver(interner);
+ rustc_type_ir::TyKind::Adt(def, args)
+ }
+ chalk_ir::TyKind::AssociatedType(assoc_type_id, substitution) => {
+ let def_id = SolverDefId::TypeAliasId(from_assoc_type_id(*assoc_type_id));
+ let args: GenericArgs<'db> = substitution.to_nextsolver(interner);
+ let alias_ty = rustc_type_ir::AliasTy::new(interner, def_id, args.iter());
+ rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Projection, alias_ty)
+ }
+ chalk_ir::TyKind::Scalar(scalar) => match scalar {
+ chalk_ir::Scalar::Bool => rustc_type_ir::TyKind::Bool,
+ chalk_ir::Scalar::Char => rustc_type_ir::TyKind::Char,
+ chalk_ir::Scalar::Int(chalk_ir::IntTy::Isize) => {
+ rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize)
+ }
+ chalk_ir::Scalar::Int(chalk_ir::IntTy::I8) => {
+ rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8)
+ }
+ chalk_ir::Scalar::Int(chalk_ir::IntTy::I16) => {
+ rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16)
+ }
+ chalk_ir::Scalar::Int(chalk_ir::IntTy::I32) => {
+ rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32)
+ }
+ chalk_ir::Scalar::Int(chalk_ir::IntTy::I64) => {
+ rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64)
+ }
+ chalk_ir::Scalar::Int(chalk_ir::IntTy::I128) => {
+ rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128)
+ }
+ chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize) => {
+ rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize)
+ }
+ chalk_ir::Scalar::Uint(chalk_ir::UintTy::U8) => {
+ rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8)
+ }
+ chalk_ir::Scalar::Uint(chalk_ir::UintTy::U16) => {
+ rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16)
+ }
+ chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32) => {
+ rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32)
+ }
+ chalk_ir::Scalar::Uint(chalk_ir::UintTy::U64) => {
+ rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64)
+ }
+ chalk_ir::Scalar::Uint(chalk_ir::UintTy::U128) => {
+ rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128)
+ }
+ chalk_ir::Scalar::Float(chalk_ir::FloatTy::F16) => {
+ rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16)
+ }
+ chalk_ir::Scalar::Float(chalk_ir::FloatTy::F32) => {
+ rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32)
+ }
+ chalk_ir::Scalar::Float(chalk_ir::FloatTy::F64) => {
+ rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64)
+ }
+ chalk_ir::Scalar::Float(chalk_ir::FloatTy::F128) => {
+ rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128)
+ }
+ },
+ chalk_ir::TyKind::Tuple(_, substitution) => {
+ let args = substitution.to_nextsolver(interner);
+ rustc_type_ir::TyKind::Tuple(args)
+ }
+ chalk_ir::TyKind::Array(ty, len) => rustc_type_ir::TyKind::Array(
+ ty.to_nextsolver(interner),
+ len.to_nextsolver(interner),
+ ),
+ chalk_ir::TyKind::Slice(ty) => {
+ rustc_type_ir::TyKind::Slice(ty.to_nextsolver(interner))
+ }
+ chalk_ir::TyKind::Raw(mutability, ty) => rustc_type_ir::RawPtr(
+ ty.to_nextsolver(interner),
+ mutability.to_nextsolver(interner),
+ ),
+ chalk_ir::TyKind::Ref(mutability, lifetime, ty) => rustc_type_ir::TyKind::Ref(
+ lifetime.to_nextsolver(interner),
+ ty.to_nextsolver(interner),
+ mutability.to_nextsolver(interner),
+ ),
+ chalk_ir::TyKind::OpaqueType(def_id, substitution) => {
+ let id: InternedOpaqueTyId = (*def_id).into();
+ let args: GenericArgs<'db> = substitution.to_nextsolver(interner);
+ let alias_ty = rustc_type_ir::AliasTy::new(interner, id.into(), args);
+ rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Opaque, alias_ty)
+ }
+ chalk_ir::TyKind::FnDef(fn_def_id, substitution) => {
+ let def_id = CallableDefId::from_chalk(interner.db(), *fn_def_id);
+ let id: SolverDefId = match def_id {
+ CallableDefId::FunctionId(id) => id.into(),
+ CallableDefId::StructId(id) => SolverDefId::Ctor(Ctor::Struct(id)),
+ CallableDefId::EnumVariantId(id) => SolverDefId::Ctor(Ctor::Enum(id)),
+ };
+ rustc_type_ir::TyKind::FnDef(id, substitution.to_nextsolver(interner))
+ }
+ chalk_ir::TyKind::Str => rustc_type_ir::TyKind::Str,
+ chalk_ir::TyKind::Never => rustc_type_ir::TyKind::Never,
+ chalk_ir::TyKind::Closure(closure_id, substitution) => {
+ let id: InternedClosureId = (*closure_id).into();
+ rustc_type_ir::TyKind::Closure(id.into(), substitution.to_nextsolver(interner))
+ }
+ chalk_ir::TyKind::Coroutine(coroutine_id, substitution) => {
+ let id: InternedCoroutineId = (*coroutine_id).into();
+ rustc_type_ir::TyKind::Coroutine(
+ id.into(),
+ substitution.to_nextsolver(interner),
+ )
+ }
+ chalk_ir::TyKind::CoroutineWitness(coroutine_id, substitution) => {
+ let id: InternedCoroutineId = (*coroutine_id).into();
+ rustc_type_ir::TyKind::CoroutineWitness(
+ id.into(),
+ substitution.to_nextsolver(interner),
+ )
+ }
+ chalk_ir::TyKind::Foreign(foreign_def_id) => rustc_type_ir::TyKind::Foreign(
+ SolverDefId::ForeignId(crate::from_foreign_def_id(*foreign_def_id)),
+ ),
+ chalk_ir::TyKind::Error => rustc_type_ir::TyKind::Error(ErrorGuaranteed),
+ chalk_ir::TyKind::Placeholder(placeholder_index) => {
+ rustc_type_ir::TyKind::Placeholder(PlaceholderTy::new_anon(
+ placeholder_index.ui.to_nextsolver(interner),
+ rustc_type_ir::BoundVar::from_usize(placeholder_index.idx),
+ ))
+ }
+ chalk_ir::TyKind::Dyn(dyn_ty) => {
+ // exists<type> { for<...> ^1.0: ... }
+ let bounds = BoundExistentialPredicates::new_from_iter(
+ interner,
+ dyn_ty.bounds.skip_binders().iter(Interner).filter_map(|pred| {
+ // for<...> ^1.0: ...
+ let (val, binders) = pred.clone().into_value_and_skipped_binders();
+ let bound_vars = binders.to_nextsolver(interner);
+ let clause = match val {
+ chalk_ir::WhereClause::Implemented(trait_ref) => {
+ let trait_id = from_chalk_trait_id(trait_ref.trait_id);
+ if interner
+ .db()
+ .trait_signature(trait_id)
+ .flags
+ .contains(TraitFlags::AUTO)
+ {
+ ExistentialPredicate::AutoTrait(SolverDefId::TraitId(
+ trait_id,
+ ))
+ } else {
+ let def_id = SolverDefId::TraitId(trait_id);
+ let args = GenericArgs::new_from_iter(
+ interner,
+ trait_ref
+ .substitution
+ .iter(Interner)
+ .skip(1)
+ .map(|a| a.clone().shifted_out(Interner).unwrap())
+ .map(|a| a.to_nextsolver(interner)),
+ );
+ let trait_ref = ExistentialTraitRef::new_from_args(
+ interner, def_id, args,
+ );
+ ExistentialPredicate::Trait(trait_ref)
+ }
+ }
+ chalk_ir::WhereClause::AliasEq(alias_eq) => {
+ let (def_id, args) = match &alias_eq.alias {
+ chalk_ir::AliasTy::Projection(projection) => {
+ let id =
+ from_assoc_type_id(projection.associated_ty_id);
+ let def_id = SolverDefId::TypeAliasId(id);
+ let generics = interner.generics_of(def_id);
+ let parent_len = generics.parent_count;
+ let substs = projection.substitution.iter(Interner).skip(1);
+
+ let args = GenericArgs::new_from_iter(
+ interner,
+ substs
+ .map(|a| {
+ a.clone().shifted_out(Interner).unwrap()
+ })
+ .map(|a| a.to_nextsolver(interner)),
+ );
+ (def_id, args)
+ }
+ chalk_ir::AliasTy::Opaque(opaque_ty) => {
+ panic!("Invalid ExistentialPredicate (opaques can't be named).");
+ }
+ };
+ let term = alias_eq
+ .ty
+ .clone()
+ .shifted_out(Interner)
+ .unwrap()
+ .to_nextsolver(interner)
+ .into();
+ let projection = ExistentialProjection::new_from_args(
+ interner, def_id, args, term,
+ );
+ ExistentialPredicate::Projection(projection)
+ }
+ chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => {
+ return None;
+ }
+ chalk_ir::WhereClause::TypeOutlives(type_outlives) => return None,
+ };
+
+ Some(Binder::bind_with_vars(clause, bound_vars))
+ }),
+ );
+ let region = dyn_ty.lifetime.to_nextsolver(interner);
+ let kind = rustc_type_ir::DynKind::Dyn;
+ rustc_type_ir::TyKind::Dynamic(bounds, region, kind)
+ }
+ chalk_ir::TyKind::Alias(alias_ty) => match alias_ty {
+ chalk_ir::AliasTy::Projection(projection_ty) => {
+ let def_id = SolverDefId::TypeAliasId(from_assoc_type_id(
+ projection_ty.associated_ty_id,
+ ));
+ let alias_ty = rustc_type_ir::AliasTy::new_from_args(
+ interner,
+ def_id,
+ projection_ty.substitution.to_nextsolver(interner),
+ );
+ rustc_type_ir::TyKind::Alias(
+ rustc_type_ir::AliasTyKind::Projection,
+ alias_ty,
+ )
+ }
+ chalk_ir::AliasTy::Opaque(opaque_ty) => {
+ let id: InternedOpaqueTyId = opaque_ty.opaque_ty_id.into();
+ let def_id = SolverDefId::InternedOpaqueTyId(id);
+ let alias_ty = rustc_type_ir::AliasTy::new_from_args(
+ interner,
+ def_id,
+ opaque_ty.substitution.to_nextsolver(interner),
+ );
+ rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Opaque, alias_ty)
+ }
+ },
+ chalk_ir::TyKind::Function(fn_pointer) => {
+ let sig_tys = fn_pointer.clone().into_binders(Interner).to_nextsolver(interner);
+ let header = rustc_type_ir::FnHeader {
+ abi: fn_pointer.sig.abi,
+ c_variadic: fn_pointer.sig.variadic,
+ safety: match fn_pointer.sig.safety {
+ chalk_ir::Safety::Safe => super::abi::Safety::Safe,
+ chalk_ir::Safety::Unsafe => super::abi::Safety::Unsafe,
+ },
+ };
+
+ rustc_type_ir::TyKind::FnPtr(sig_tys, header)
+ }
+ chalk_ir::TyKind::BoundVar(bound_var) => rustc_type_ir::TyKind::Bound(
+ bound_var.debruijn.to_nextsolver(interner),
+ BoundTy {
+ var: rustc_type_ir::BoundVar::from_usize(bound_var.index),
+ kind: BoundTyKind::Anon,
+ },
+ ),
+ chalk_ir::TyKind::InferenceVar(inference_var, ty_variable_kind) => {
+ rustc_type_ir::TyKind::Infer(
+ (*inference_var, *ty_variable_kind).to_nextsolver(interner),
+ )
+ }
+ },
+ )
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> Region<'db> {
+ Region::new(
+ interner,
+ match self.data(Interner) {
+ chalk_ir::LifetimeData::BoundVar(bound_var) => rustc_type_ir::RegionKind::ReBound(
+ bound_var.debruijn.to_nextsolver(interner),
+ BoundRegion {
+ var: rustc_type_ir::BoundVar::from_u32(bound_var.index as u32),
+ kind: BoundRegionKind::Anon,
+ },
+ ),
+ chalk_ir::LifetimeData::InferenceVar(inference_var) => {
+ rustc_type_ir::RegionKind::ReVar(rustc_type_ir::RegionVid::from_u32(
+ inference_var.index(),
+ ))
+ }
+ chalk_ir::LifetimeData::Placeholder(placeholder_index) => {
+ rustc_type_ir::RegionKind::RePlaceholder(PlaceholderRegion::new_anon(
+ rustc_type_ir::UniverseIndex::from_u32(placeholder_index.ui.counter as u32),
+ rustc_type_ir::BoundVar::from_u32(placeholder_index.idx as u32),
+ ))
+ }
+ chalk_ir::LifetimeData::Static => rustc_type_ir::RegionKind::ReStatic,
+ chalk_ir::LifetimeData::Erased => rustc_type_ir::RegionKind::ReErased,
+ chalk_ir::LifetimeData::Phantom(_, _) => {
+ unreachable!()
+ }
+ chalk_ir::LifetimeData::Error => {
+ rustc_type_ir::RegionKind::ReError(ErrorGuaranteed)
+ }
+ },
+ )
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> Const<'db> {
+ let data = self.data(Interner);
+ Const::new(
+ interner,
+ match &data.value {
+ chalk_ir::ConstValue::BoundVar(bound_var) => rustc_type_ir::ConstKind::Bound(
+ bound_var.debruijn.to_nextsolver(interner),
+ rustc_type_ir::BoundVar::from_usize(bound_var.index),
+ ),
+ chalk_ir::ConstValue::InferenceVar(inference_var) => {
+ rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(
+ rustc_type_ir::ConstVid::from_u32(inference_var.index()),
+ ))
+ }
+ chalk_ir::ConstValue::Placeholder(placeholder_index) => {
+ rustc_type_ir::ConstKind::Placeholder(PlaceholderConst::new(
+ placeholder_index.ui.to_nextsolver(interner),
+ rustc_type_ir::BoundVar::from_usize(placeholder_index.idx),
+ ))
+ }
+ chalk_ir::ConstValue::Concrete(concrete_const) => match &concrete_const.interned {
+ ConstScalar::Bytes(bytes, memory) => {
+ rustc_type_ir::ConstKind::Value(ValueConst::new(
+ data.ty.to_nextsolver(interner),
+ ConstBytes(bytes.clone(), memory.clone()),
+ ))
+ }
+ ConstScalar::UnevaluatedConst(c, subst) => {
+ let def = match *c {
+ GeneralConstId::ConstId(id) => SolverDefId::ConstId(id),
+ GeneralConstId::StaticId(id) => SolverDefId::StaticId(id),
+ };
+ let args = subst.to_nextsolver(interner);
+ rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new(def, args))
+ }
+ ConstScalar::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
+ },
+ },
+ )
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, rustc_type_ir::FnSigTys<DbInterner<'db>>>
+ for chalk_ir::FnSubst<Interner>
+{
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::FnSigTys<DbInterner<'db>> {
+ rustc_type_ir::FnSigTys {
+ inputs_and_output: Tys::new_from_iter(
+ interner,
+ self.0.iter(Interner).map(|g| g.assert_ty_ref(Interner).to_nextsolver(interner)),
+ ),
+ }
+ }
+}
+
+impl<
+ 'db,
+ U: TypeVisitable<DbInterner<'db>>,
+ T: Clone + ChalkToNextSolver<'db, U> + HasInterner<Interner = Interner>,
+> ChalkToNextSolver<'db, rustc_type_ir::Binder<DbInterner<'db>, U>> for chalk_ir::Binders<T>
+{
+ fn to_nextsolver(
+ &self,
+ interner: DbInterner<'db>,
+ ) -> rustc_type_ir::Binder<DbInterner<'db>, U> {
+ let (val, binders) = self.clone().into_value_and_skipped_binders();
+ rustc_type_ir::Binder::bind_with_vars(
+ val.to_nextsolver(interner),
+ binders.to_nextsolver(interner),
+ )
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKinds {
+ BoundVarKinds::new_from_iter(
+ interner,
+ self.iter(Interner).map(|v| v.to_nextsolver(interner)),
+ )
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKind {
+ match self {
+ chalk_ir::VariableKind::Ty(_ty_variable_kind) => BoundVarKind::Ty(BoundTyKind::Anon),
+ chalk_ir::VariableKind::Lifetime => BoundVarKind::Region(BoundRegionKind::Anon),
+ chalk_ir::VariableKind::Const(_ty) => BoundVarKind::Const,
+ }
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArg<'db> {
+ match self.data(Interner) {
+ chalk_ir::GenericArgData::Ty(ty) => ty.to_nextsolver(interner).into(),
+ chalk_ir::GenericArgData::Lifetime(lifetime) => lifetime.to_nextsolver(interner).into(),
+ chalk_ir::GenericArgData::Const(const_) => const_.to_nextsolver(interner).into(),
+ }
+ }
+}
+impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArgs<'db> {
+ GenericArgs::new_from_iter(
+ interner,
+ self.iter(Interner).map(|arg| -> GenericArg<'db> { arg.to_nextsolver(interner) }),
+ )
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> Tys<'db> {
+ Tys::new_from_iter(
+ interner,
+ self.iter(Interner).map(|arg| -> Ty<'db> {
+ match arg.data(Interner) {
+ chalk_ir::GenericArgData::Ty(ty) => ty.to_nextsolver(interner),
+ chalk_ir::GenericArgData::Lifetime(_) => unreachable!(),
+ chalk_ir::GenericArgData::Const(_) => unreachable!(),
+ }
+ }),
+ )
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, rustc_type_ir::DebruijnIndex> for chalk_ir::DebruijnIndex {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex {
+ rustc_type_ir::DebruijnIndex::from_u32(self.depth())
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, rustc_type_ir::UniverseIndex> for chalk_ir::UniverseIndex {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::UniverseIndex {
+ rustc_type_ir::UniverseIndex::from_u32(self.counter as u32)
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy>
+ for (chalk_ir::InferenceVar, chalk_ir::TyVariableKind)
+{
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::InferTy {
+ match self.1 {
+ chalk_ir::TyVariableKind::General => {
+ rustc_type_ir::InferTy::TyVar(rustc_type_ir::TyVid::from_u32(self.0.index()))
+ }
+ chalk_ir::TyVariableKind::Integer => {
+ rustc_type_ir::InferTy::IntVar(rustc_type_ir::IntVid::from_u32(self.0.index()))
+ }
+ chalk_ir::TyVariableKind::Float => {
+ rustc_type_ir::InferTy::FloatVar(rustc_type_ir::FloatVid::from_u32(self.0.index()))
+ }
+ }
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutability {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_ast_ir::Mutability {
+ match self {
+ chalk_ir::Mutability::Mut => rustc_ast_ir::Mutability::Mut,
+ chalk_ir::Mutability::Not => rustc_ast_ir::Mutability::Not,
+ }
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::Variance {
+ match self {
+ crate::Variance::Covariant => rustc_type_ir::Variance::Covariant,
+ crate::Variance::Invariant => rustc_type_ir::Variance::Invariant,
+ crate::Variance::Contravariant => rustc_type_ir::Variance::Contravariant,
+ crate::Variance::Bivariant => rustc_type_ir::Variance::Bivariant,
+ }
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, Canonical<'db, Goal<DbInterner<'db>, Predicate<'db>>>>
+ for chalk_ir::Canonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>
+{
+ fn to_nextsolver(
+ &self,
+ interner: DbInterner<'db>,
+ ) -> Canonical<'db, Goal<DbInterner<'db>, Predicate<'db>>> {
+ let param_env = self.value.environment.to_nextsolver(interner);
+ let variables = CanonicalVars::new_from_iter(
+ interner,
+ self.binders.iter(Interner).map(|k| match &k.kind {
+ chalk_ir::VariableKind::Ty(ty_variable_kind) => match ty_variable_kind {
+ TyVariableKind::General => rustc_type_ir::CanonicalVarKind::Ty(
+ rustc_type_ir::CanonicalTyVarKind::General(UniverseIndex::ROOT),
+ ),
+ TyVariableKind::Integer => {
+ rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int)
+ }
+ TyVariableKind::Float => rustc_type_ir::CanonicalVarKind::Ty(
+ rustc_type_ir::CanonicalTyVarKind::Float,
+ ),
+ },
+ chalk_ir::VariableKind::Lifetime => {
+ rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ROOT)
+ }
+ chalk_ir::VariableKind::Const(ty) => {
+ rustc_type_ir::CanonicalVarKind::Const(UniverseIndex::ROOT)
+ }
+ }),
+ );
+ Canonical {
+ max_universe: UniverseIndex::ROOT,
+ value: Goal::new(interner, param_env, self.value.goal.to_nextsolver(interner)),
+ variables,
+ }
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> Predicate<'db> {
+ match self.data(Interner) {
+ chalk_ir::GoalData::Quantified(quantifier_kind, binders) => {
+ if !binders.binders.is_empty(Interner) {
+ panic!("Should not be constructed.");
+ }
+ let (val, _) = binders.clone().into_value_and_skipped_binders();
+ val.shifted_out(Interner).unwrap().to_nextsolver(interner)
+ }
+ chalk_ir::GoalData::Implies(program_clauses, goal) => {
+ panic!("Should not be constructed.")
+ }
+ chalk_ir::GoalData::All(goals) => panic!("Should not be constructed."),
+ chalk_ir::GoalData::Not(goal) => panic!("Should not be constructed."),
+ chalk_ir::GoalData::EqGoal(eq_goal) => panic!("Should not be constructed."),
+ chalk_ir::GoalData::SubtypeGoal(subtype_goal) => {
+ let subtype_predicate = SubtypePredicate {
+ a: subtype_goal.a.to_nextsolver(interner),
+ b: subtype_goal.b.to_nextsolver(interner),
+ a_is_expected: true,
+ };
+ let pred_kind = PredicateKind::Subtype(subtype_predicate);
+ let pred_kind = Binder::bind_with_vars(
+ shift_vars(interner, pred_kind, 1),
+ BoundVarKinds::new_from_iter(interner, []),
+ );
+ Predicate::new(interner, pred_kind)
+ }
+ chalk_ir::GoalData::DomainGoal(domain_goal) => {
+ let pred_kind = domain_goal.to_nextsolver(interner);
+ let pred_kind = Binder::bind_with_vars(
+ shift_vars(interner, pred_kind, 1),
+ BoundVarKinds::new_from_iter(interner, []),
+ );
+ Predicate::new(interner, pred_kind)
+ }
+ chalk_ir::GoalData::CannotProve => panic!("Should not be constructed."),
+ }
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> ParamEnv<'db> {
+ let clauses = Clauses::new_from_iter(
+ interner,
+ self.clauses.iter(Interner).map(|c| c.to_nextsolver(interner)),
+ );
+ let clauses =
+ Clauses::new_from_iter(interner, elaborate::elaborate(interner, clauses.iter()));
+ ParamEnv { clauses }
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, Clause<'db>> for chalk_ir::ProgramClause<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> Clause<'db> {
+ Clause(Predicate::new(interner, self.data(Interner).0.to_nextsolver(interner)))
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>>
+ for chalk_ir::ProgramClauseImplication<Interner>
+{
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> {
+ assert!(self.conditions.is_empty(Interner));
+ assert!(self.constraints.is_empty(Interner));
+ self.consequence.to_nextsolver(interner)
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> {
+ match self {
+ chalk_ir::DomainGoal::Holds(where_clause) => match where_clause {
+ chalk_ir::WhereClause::Implemented(trait_ref) => {
+ let predicate = TraitPredicate {
+ trait_ref: trait_ref.to_nextsolver(interner),
+ polarity: rustc_type_ir::PredicatePolarity::Positive,
+ };
+ PredicateKind::Clause(ClauseKind::Trait(predicate))
+ }
+ chalk_ir::WhereClause::AliasEq(alias_eq) => match &alias_eq.alias {
+ chalk_ir::AliasTy::Projection(p) => {
+ let def_id =
+ SolverDefId::TypeAliasId(from_assoc_type_id(p.associated_ty_id));
+ let args = p.substitution.to_nextsolver(interner);
+ let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner);
+ let term: Term<'db> = term.into();
+ let predicate = ProjectionPredicate {
+ projection_term: AliasTerm::new_from_args(interner, def_id, args),
+ term,
+ };
+ PredicateKind::Clause(ClauseKind::Projection(predicate))
+ }
+ chalk_ir::AliasTy::Opaque(opaque) => {
+ let id: InternedOpaqueTyId = opaque.opaque_ty_id.into();
+ let def_id = SolverDefId::InternedOpaqueTyId(id);
+ let args = opaque.substitution.to_nextsolver(interner);
+ let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner);
+ let term: Term<'db> = term.into();
+ let opaque_ty = Ty::new(
+ interner,
+ rustc_type_ir::TyKind::Alias(
+ rustc_type_ir::AliasTyKind::Opaque,
+ rustc_type_ir::AliasTy::new_from_args(interner, def_id, args),
+ ),
+ )
+ .into();
+ PredicateKind::AliasRelate(
+ opaque_ty,
+ term,
+ rustc_type_ir::AliasRelationDirection::Equate,
+ )
+ }
+ },
+ chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => {
+ let predicate = OutlivesPredicate(
+ lifetime_outlives.a.to_nextsolver(interner),
+ lifetime_outlives.b.to_nextsolver(interner),
+ );
+ PredicateKind::Clause(ClauseKind::RegionOutlives(predicate))
+ }
+ chalk_ir::WhereClause::TypeOutlives(type_outlives) => {
+ let predicate = OutlivesPredicate(
+ type_outlives.ty.to_nextsolver(interner),
+ type_outlives.lifetime.to_nextsolver(interner),
+ );
+ PredicateKind::Clause(ClauseKind::TypeOutlives(predicate))
+ }
+ },
+ chalk_ir::DomainGoal::Normalize(normalize) => {
+ let proj_ty = match &normalize.alias {
+ chalk_ir::AliasTy::Projection(proj) => proj,
+ _ => unimplemented!(),
+ };
+ let args: GenericArgs<'db> = proj_ty.substitution.to_nextsolver(interner);
+ let alias = rustc_type_ir::AliasTerm::new(
+ interner,
+ from_assoc_type_id(proj_ty.associated_ty_id).into(),
+ args,
+ );
+ let term = normalize.ty.to_nextsolver(interner).into();
+ let normalizes_to = rustc_type_ir::NormalizesTo { alias, term };
+ PredicateKind::NormalizesTo(normalizes_to)
+ }
+ chalk_ir::DomainGoal::WellFormed(well_formed) => {
+ let term = match well_formed {
+ WellFormed::Trait(_) => panic!("Should not be constructed."),
+ WellFormed::Ty(ty) => Term::Ty(ty.to_nextsolver(interner)),
+ };
+ PredicateKind::Clause(rustc_type_ir::ClauseKind::WellFormed(term))
+ }
+ chalk_ir::DomainGoal::FromEnv(from_env) => match from_env {
+ chalk_ir::FromEnv::Trait(trait_ref) => {
+ let predicate = TraitPredicate {
+ trait_ref: trait_ref.to_nextsolver(interner),
+ polarity: rustc_type_ir::PredicatePolarity::Positive,
+ };
+ PredicateKind::Clause(ClauseKind::Trait(predicate))
+ }
+ chalk_ir::FromEnv::Ty(ty) => PredicateKind::Clause(ClauseKind::WellFormed(
+ Term::Ty(ty.to_nextsolver(interner)),
+ )),
+ },
+ chalk_ir::DomainGoal::IsLocal(ty) => panic!("Should not be constructed."),
+ chalk_ir::DomainGoal::IsUpstream(ty) => panic!("Should not be constructed."),
+ chalk_ir::DomainGoal::IsFullyVisible(ty) => panic!("Should not be constructed."),
+ chalk_ir::DomainGoal::LocalImplAllowed(trait_ref) => {
+ panic!("Should not be constructed.")
+ }
+ chalk_ir::DomainGoal::Compatible => panic!("Should not be constructed."),
+ chalk_ir::DomainGoal::DownstreamType(ty) => panic!("Should not be constructed."),
+ chalk_ir::DomainGoal::Reveal => panic!("Should not be constructed."),
+ chalk_ir::DomainGoal::ObjectSafe(trait_id) => panic!("Should not be constructed."),
+ }
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> TraitRef<'db> {
+ let args = self.substitution.to_nextsolver(interner);
+ TraitRef::new_from_args(
+ interner,
+ SolverDefId::TraitId(from_chalk_trait_id(self.trait_id)),
+ args,
+ )
+ }
+}
+
+impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::WhereClause<Interner> {
+ fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> {
+ match self {
+ chalk_ir::WhereClause::Implemented(trait_ref) => {
+ let predicate = TraitPredicate {
+ trait_ref: trait_ref.to_nextsolver(interner),
+ polarity: rustc_type_ir::PredicatePolarity::Positive,
+ };
+ PredicateKind::Clause(ClauseKind::Trait(predicate))
+ }
+ chalk_ir::WhereClause::AliasEq(alias_eq) => {
+ let projection = match &alias_eq.alias {
+ chalk_ir::AliasTy::Projection(p) => p,
+ _ => unimplemented!(),
+ };
+ let def_id =
+ SolverDefId::TypeAliasId(from_assoc_type_id(projection.associated_ty_id));
+ let args = projection.substitution.to_nextsolver(interner);
+ let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner);
+ let term: Term<'db> = term.into();
+ let predicate = ProjectionPredicate {
+ projection_term: AliasTerm::new_from_args(interner, def_id, args),
+ term,
+ };
+ PredicateKind::Clause(ClauseKind::Projection(predicate))
+ }
+ chalk_ir::WhereClause::TypeOutlives(type_outlives) => {
+ let ty = type_outlives.ty.to_nextsolver(interner);
+ let r = type_outlives.lifetime.to_nextsolver(interner);
+ PredicateKind::Clause(ClauseKind::TypeOutlives(OutlivesPredicate(ty, r)))
+ }
+ chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => {
+ let a = lifetime_outlives.a.to_nextsolver(interner);
+ let b = lifetime_outlives.b.to_nextsolver(interner);
+ PredicateKind::Clause(ClauseKind::RegionOutlives(OutlivesPredicate(a, b)))
+ }
+ }
+ }
+}
+
+pub fn convert_canonical_args_for_result<'db>(
+ interner: DbInterner<'db>,
+ args: Canonical<'db, Vec<GenericArg<'db>>>,
+) -> chalk_ir::Canonical<chalk_ir::ConstrainedSubst<Interner>> {
+ let Canonical { value, variables, max_universe } = args;
+ let binders = CanonicalVarKinds::from_iter(
+ Interner,
+ variables.iter().map(|v| match v {
+ rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::General(_)) => {
+ CanonicalVarKind::new(
+ chalk_ir::VariableKind::Ty(TyVariableKind::General),
+ chalk_ir::UniverseIndex::ROOT,
+ )
+ }
+ rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) => {
+ CanonicalVarKind::new(
+ chalk_ir::VariableKind::Ty(TyVariableKind::Integer),
+ chalk_ir::UniverseIndex::ROOT,
+ )
+ }
+ rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Float) => {
+ CanonicalVarKind::new(
+ chalk_ir::VariableKind::Ty(TyVariableKind::Float),
+ chalk_ir::UniverseIndex::ROOT,
+ )
+ }
+ rustc_type_ir::CanonicalVarKind::Region(universe_index) => CanonicalVarKind::new(
+ chalk_ir::VariableKind::Lifetime,
+ chalk_ir::UniverseIndex::ROOT,
+ ),
+ rustc_type_ir::CanonicalVarKind::Const(universe_index) => CanonicalVarKind::new(
+ chalk_ir::VariableKind::Const(crate::TyKind::Error.intern(Interner)),
+ chalk_ir::UniverseIndex::ROOT,
+ ),
+ rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => unimplemented!(),
+ rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => unimplemented!(),
+ rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => unimplemented!(),
+ }),
+ );
+ chalk_ir::Canonical {
+ binders,
+ value: chalk_ir::ConstrainedSubst {
+ constraints: chalk_ir::Constraints::empty(Interner),
+ subst: convert_args_for_result(interner, &value),
+ },
+ }
+}
+
+pub fn convert_args_for_result<'db>(
+ interner: DbInterner<'db>,
+ args: &[GenericArg<'db>],
+) -> crate::Substitution {
+ let mut substs = Vec::with_capacity(args.len());
+ for arg in args {
+ match (*arg).kind() {
+ rustc_type_ir::GenericArgKind::Type(ty) => {
+ let ty = convert_ty_for_result(interner, ty);
+ substs.push(chalk_ir::GenericArgData::Ty(ty).intern(Interner));
+ }
+ rustc_type_ir::GenericArgKind::Lifetime(region) => {
+ let lifetime = convert_region_for_result(region);
+ substs.push(chalk_ir::GenericArgData::Lifetime(lifetime).intern(Interner));
+ }
+ rustc_type_ir::GenericArgKind::Const(const_) => {
+ substs.push(
+ chalk_ir::GenericArgData::Const(convert_const_for_result(interner, const_))
+ .intern(Interner),
+ );
+ }
+ }
+ }
+ Substitution::from_iter(Interner, substs)
+}
+
+pub(crate) 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() {
+ rustc_type_ir::TyKind::Bool => TyKind::Scalar(Scalar::Bool),
+ rustc_type_ir::TyKind::Char => TyKind::Scalar(Scalar::Char),
+ rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) => {
+ TyKind::Scalar(Scalar::Int(IntTy::I8))
+ }
+ rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) => {
+ TyKind::Scalar(Scalar::Int(IntTy::I16))
+ }
+ rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) => {
+ TyKind::Scalar(Scalar::Int(IntTy::I32))
+ }
+ rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) => {
+ TyKind::Scalar(Scalar::Int(IntTy::I64))
+ }
+ rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) => {
+ TyKind::Scalar(Scalar::Int(IntTy::I128))
+ }
+ rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) => {
+ TyKind::Scalar(Scalar::Int(IntTy::Isize))
+ }
+ rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) => {
+ TyKind::Scalar(Scalar::Uint(UintTy::U8))
+ }
+ rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) => {
+ TyKind::Scalar(Scalar::Uint(UintTy::U16))
+ }
+ rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) => {
+ TyKind::Scalar(Scalar::Uint(UintTy::U32))
+ }
+ rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) => {
+ TyKind::Scalar(Scalar::Uint(UintTy::U64))
+ }
+ rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) => {
+ TyKind::Scalar(Scalar::Uint(UintTy::U128))
+ }
+ rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) => {
+ TyKind::Scalar(Scalar::Uint(UintTy::Usize))
+ }
+ rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) => {
+ TyKind::Scalar(Scalar::Float(FloatTy::F16))
+ }
+ rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) => {
+ TyKind::Scalar(Scalar::Float(FloatTy::F32))
+ }
+ rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) => {
+ TyKind::Scalar(Scalar::Float(FloatTy::F64))
+ }
+ rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) => {
+ TyKind::Scalar(Scalar::Float(FloatTy::F128))
+ }
+ rustc_type_ir::TyKind::Str => TyKind::Str,
+ rustc_type_ir::TyKind::Error(_) => TyKind::Error,
+ rustc_type_ir::TyKind::Never => TyKind::Never,
+
+ rustc_type_ir::TyKind::Adt(def, args) => {
+ let adt_id = def.inner().id;
+ let subst = convert_args_for_result(interner, args.as_slice());
+ TyKind::Adt(chalk_ir::AdtId(adt_id), subst)
+ }
+
+ rustc_type_ir::TyKind::Infer(infer_ty) => {
+ let (var, kind) = match infer_ty {
+ rustc_type_ir::InferTy::TyVar(var) => {
+ (InferenceVar::from(var.as_u32()), TyVariableKind::General)
+ }
+ rustc_type_ir::InferTy::IntVar(var) => {
+ (InferenceVar::from(var.as_u32()), TyVariableKind::Integer)
+ }
+ rustc_type_ir::InferTy::FloatVar(var) => {
+ (InferenceVar::from(var.as_u32()), TyVariableKind::Float)
+ }
+ rustc_type_ir::InferTy::FreshFloatTy(..)
+ | rustc_type_ir::InferTy::FreshIntTy(..)
+ | rustc_type_ir::InferTy::FreshTy(..) => {
+ panic!("Freshening shouldn't happen.")
+ }
+ };
+ TyKind::InferenceVar(var, kind)
+ }
+
+ rustc_type_ir::TyKind::Ref(r, ty, mutability) => {
+ let mutability = match mutability {
+ rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut,
+ rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not,
+ };
+ let r = convert_region_for_result(r);
+ let ty = convert_ty_for_result(interner, ty);
+ TyKind::Ref(mutability, r, ty)
+ }
+
+ rustc_type_ir::TyKind::Tuple(tys) => {
+ let size = tys.len();
+ let subst = Substitution::from_iter(
+ Interner,
+ tys.iter().map(|ty| {
+ chalk_ir::GenericArgData::Ty(convert_ty_for_result(interner, ty))
+ .intern(Interner)
+ }),
+ );
+ TyKind::Tuple(size, subst)
+ }
+
+ rustc_type_ir::TyKind::Array(ty, const_) => {
+ let ty = convert_ty_for_result(interner, ty);
+ let const_ = convert_const_for_result(interner, const_);
+ TyKind::Array(ty, const_)
+ }
+
+ rustc_type_ir::TyKind::Alias(alias_ty_kind, alias_ty) => match alias_ty_kind {
+ rustc_type_ir::AliasTyKind::Projection => {
+ let assoc_ty_id = match alias_ty.def_id {
+ SolverDefId::TypeAliasId(id) => id,
+ _ => unreachable!(),
+ };
+ let associated_ty_id = to_assoc_type_id(assoc_ty_id);
+ let substitution = convert_args_for_result(interner, alias_ty.args.as_slice());
+ TyKind::AssociatedType(associated_ty_id, substitution)
+ }
+ rustc_type_ir::AliasTyKind::Opaque => {
+ let opaque_ty_id = match alias_ty.def_id {
+ SolverDefId::InternedOpaqueTyId(id) => id,
+ _ => unreachable!(),
+ };
+ let substitution = convert_args_for_result(interner, alias_ty.args.as_slice());
+ TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy {
+ opaque_ty_id: opaque_ty_id.into(),
+ substitution,
+ }))
+ }
+ rustc_type_ir::AliasTyKind::Inherent => unimplemented!(),
+ rustc_type_ir::AliasTyKind::Free => unimplemented!(),
+ },
+
+ rustc_type_ir::TyKind::Placeholder(placeholder) => {
+ let ui = chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() };
+ let placeholder_index =
+ chalk_ir::PlaceholderIndex { idx: placeholder.bound.var.as_usize(), ui };
+ TyKind::Placeholder(placeholder_index)
+ }
+
+ rustc_type_ir::TyKind::Bound(debruijn_index, ty) => TyKind::BoundVar(chalk_ir::BoundVar {
+ debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()),
+ index: ty.var.as_usize(),
+ }),
+
+ rustc_type_ir::TyKind::FnPtr(bound_sig, fn_header) => {
+ let num_binders = bound_sig.bound_vars().len();
+ let sig = chalk_ir::FnSig {
+ abi: fn_header.abi,
+ safety: match fn_header.safety {
+ crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe,
+ crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe,
+ },
+ variadic: fn_header.c_variadic,
+ };
+ let args = GenericArgs::new_from_iter(
+ interner,
+ bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()),
+ );
+ let substitution = convert_args_for_result(interner, args.as_slice());
+ let substitution = chalk_ir::FnSubst(substitution);
+ let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution };
+ TyKind::Function(fnptr)
+ }
+
+ rustc_type_ir::TyKind::Dynamic(preds, region, dyn_kind) => {
+ assert!(matches!(dyn_kind, rustc_type_ir::DynKind::Dyn));
+ let self_ty = Ty::new_bound(
+ interner,
+ DebruijnIndex::from_u32(1),
+ BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_u32(0) },
+ );
+ let bounds = chalk_ir::QuantifiedWhereClauses::from_iter(
+ Interner,
+ preds.iter().map(|p| {
+ let binders = chalk_ir::VariableKinds::from_iter(
+ Interner,
+ p.bound_vars().iter().map(|b| match b {
+ BoundVarKind::Ty(kind) => {
+ chalk_ir::VariableKind::Ty(TyVariableKind::General)
+ }
+ BoundVarKind::Region(kind) => chalk_ir::VariableKind::Lifetime,
+ BoundVarKind::Const => {
+ chalk_ir::VariableKind::Const(crate::TyKind::Error.intern(Interner))
+ }
+ }),
+ );
+ let where_clause = match p.skip_binder() {
+ rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => {
+ let trait_ref = TraitRef::new(
+ interner,
+ trait_ref.def_id,
+ [self_ty.into()].into_iter().chain(trait_ref.args.iter()),
+ );
+ let trait_id = match trait_ref.def_id {
+ SolverDefId::TraitId(id) => to_chalk_trait_id(id),
+ _ => unreachable!(),
+ };
+ let substitution =
+ convert_args_for_result(interner, trait_ref.args.as_slice());
+ let trait_ref = chalk_ir::TraitRef { trait_id, substitution };
+ chalk_ir::WhereClause::Implemented(trait_ref)
+ }
+ rustc_type_ir::ExistentialPredicate::AutoTrait(trait_) => {
+ let trait_id = match trait_ {
+ SolverDefId::TraitId(id) => to_chalk_trait_id(id),
+ _ => unreachable!(),
+ };
+ let substitution = chalk_ir::Substitution::empty(Interner);
+ let trait_ref = chalk_ir::TraitRef { trait_id, substitution };
+ chalk_ir::WhereClause::Implemented(trait_ref)
+ }
+ rustc_type_ir::ExistentialPredicate::Projection(existential_projection) => {
+ let projection = ProjectionPredicate {
+ projection_term: AliasTerm::new(
+ interner,
+ existential_projection.def_id,
+ [self_ty.into()]
+ .iter()
+ .chain(existential_projection.args.iter()),
+ ),
+ term: existential_projection.term,
+ };
+ let associated_ty_id = match projection.projection_term.def_id {
+ SolverDefId::TypeAliasId(id) => to_assoc_type_id(id),
+ _ => unreachable!(),
+ };
+ let substitution = convert_args_for_result(
+ interner,
+ projection.projection_term.args.as_slice(),
+ );
+ let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
+ associated_ty_id,
+ substitution,
+ });
+ let ty = match projection.term {
+ Term::Ty(ty) => ty,
+ _ => unreachable!(),
+ };
+ let ty = convert_ty_for_result(interner, ty);
+ let alias_eq = chalk_ir::AliasEq { alias, ty };
+ chalk_ir::WhereClause::AliasEq(alias_eq)
+ }
+ };
+ chalk_ir::Binders::new(binders, where_clause)
+ }),
+ );
+ let binders = chalk_ir::VariableKinds::from1(
+ Interner,
+ chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
+ );
+ let bounds = chalk_ir::Binders::new(binders, bounds);
+ let dyn_ty = chalk_ir::DynTy { bounds, lifetime: convert_region_for_result(region) };
+ TyKind::Dyn(dyn_ty)
+ }
+
+ rustc_type_ir::TyKind::Slice(ty) => {
+ let ty = convert_ty_for_result(interner, ty);
+ TyKind::Slice(ty)
+ }
+
+ rustc_type_ir::TyKind::Foreign(foreign) => {
+ let def_id = match foreign {
+ SolverDefId::ForeignId(id) => id,
+ _ => unreachable!(),
+ };
+ TyKind::Foreign(to_foreign_def_id(def_id))
+ }
+ rustc_type_ir::TyKind::Pat(_, _) => unimplemented!(),
+ rustc_type_ir::TyKind::RawPtr(ty, mutability) => {
+ let mutability = match mutability {
+ rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut,
+ rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not,
+ };
+ let ty = convert_ty_for_result(interner, ty);
+ TyKind::Raw(mutability, ty)
+ }
+ rustc_type_ir::TyKind::FnDef(def_id, args) => {
+ let id = match def_id {
+ SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id),
+ SolverDefId::Ctor(Ctor::Struct(id)) => CallableDefId::StructId(id),
+ SolverDefId::Ctor(Ctor::Enum(id)) => CallableDefId::EnumVariantId(id),
+ _ => unreachable!(),
+ };
+ let subst = convert_args_for_result(interner, args.as_slice());
+ TyKind::FnDef(id.to_chalk(interner.db()), subst)
+ }
+
+ rustc_type_ir::TyKind::Closure(def_id, args) => {
+ let id = match def_id {
+ SolverDefId::InternedClosureId(id) => id,
+ _ => unreachable!(),
+ };
+ let subst = convert_args_for_result(interner, args.as_slice());
+ TyKind::Closure(id.into(), subst)
+ }
+ rustc_type_ir::TyKind::CoroutineClosure(_, _) => unimplemented!(),
+ rustc_type_ir::TyKind::Coroutine(def_id, args) => {
+ let id = match def_id {
+ SolverDefId::InternedCoroutineId(id) => id,
+ _ => unreachable!(),
+ };
+ let subst = convert_args_for_result(interner, args.as_slice());
+ TyKind::Coroutine(id.into(), subst)
+ }
+ rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => {
+ let id = match def_id {
+ SolverDefId::InternedCoroutineId(id) => id,
+ _ => unreachable!(),
+ };
+ let subst = convert_args_for_result(interner, args.as_slice());
+ TyKind::CoroutineWitness(id.into(), subst)
+ }
+
+ rustc_type_ir::TyKind::Param(_) => unimplemented!(),
+ rustc_type_ir::TyKind::UnsafeBinder(_) => unimplemented!(),
+ }
+ .intern(Interner)
+}
+
+fn convert_const_for_result<'db>(interner: DbInterner<'db>, const_: Const<'db>) -> crate::Const {
+ let value: chalk_ir::ConstValue<Interner> = match const_.kind() {
+ rustc_type_ir::ConstKind::Param(_) => unimplemented!(),
+ rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => {
+ chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(var.as_u32()))
+ }
+ rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(fresh)) => {
+ panic!("Vars should not be freshened.")
+ }
+ rustc_type_ir::ConstKind::Bound(debruijn_index, var) => {
+ chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new(
+ chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()),
+ var.index(),
+ ))
+ }
+ rustc_type_ir::ConstKind::Placeholder(placeholder_const) => {
+ chalk_ir::ConstValue::Placeholder(chalk_ir::PlaceholderIndex {
+ ui: chalk_ir::UniverseIndex { counter: placeholder_const.universe.as_usize() },
+ idx: placeholder_const.bound.as_usize(),
+ })
+ }
+ rustc_type_ir::ConstKind::Unevaluated(unevaluated_const) => {
+ let id = match unevaluated_const.def {
+ SolverDefId::ConstId(id) => GeneralConstId::ConstId(id),
+ SolverDefId::StaticId(id) => GeneralConstId::StaticId(id),
+ _ => unreachable!(),
+ };
+ let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice());
+ chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst {
+ interned: ConstScalar::UnevaluatedConst(id, subst),
+ })
+ }
+ rustc_type_ir::ConstKind::Value(value_const) => {
+ let bytes = value_const.value.inner();
+ let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst {
+ interned: ConstScalar::Bytes(bytes.0.clone(), bytes.1.clone()),
+ });
+ return chalk_ir::ConstData {
+ ty: convert_ty_for_result(interner, value_const.ty),
+ value,
+ }
+ .intern(Interner);
+ }
+ rustc_type_ir::ConstKind::Error(_) => {
+ chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst {
+ interned: ConstScalar::Unknown,
+ })
+ }
+ rustc_type_ir::ConstKind::Expr(_) => unimplemented!(),
+ };
+ chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner)
+}
+
+fn convert_region_for_result<'db>(region: Region<'db>) -> crate::Lifetime {
+ match region.kind() {
+ rustc_type_ir::RegionKind::ReEarlyParam(early) => unimplemented!(),
+ rustc_type_ir::RegionKind::ReBound(db, bound) => chalk_ir::Lifetime::new(
+ Interner,
+ chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new(
+ chalk_ir::DebruijnIndex::new(db.as_u32()),
+ bound.var.as_usize(),
+ )),
+ ),
+ rustc_type_ir::RegionKind::ReLateParam(_) => unimplemented!(),
+ rustc_type_ir::RegionKind::ReStatic => {
+ chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Static)
+ }
+ rustc_type_ir::RegionKind::ReVar(vid) => chalk_ir::Lifetime::new(
+ Interner,
+ chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())),
+ ),
+ rustc_type_ir::RegionKind::RePlaceholder(placeholder) => chalk_ir::Lifetime::new(
+ Interner,
+ chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex {
+ idx: placeholder.bound.var.as_usize(),
+ ui: chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() },
+ }),
+ ),
+ rustc_type_ir::RegionKind::ReErased => {
+ chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Erased)
+ }
+ rustc_type_ir::RegionKind::ReError(_) => {
+ chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Error)
+ }
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/opaques.rs b/crates/hir-ty/src/next_solver/opaques.rs
new file mode 100644
index 0000000..43589ab
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/opaques.rs
@@ -0,0 +1,167 @@
+//! Things related to opaques in the next-trait-solver.
+
+use intern::Interned;
+use rustc_ast_ir::try_visit;
+
+use crate::next_solver::SolverDefId;
+
+use super::{CanonicalVarKind, DbInterner, interned_vec_nolifetime_salsa};
+
+pub type OpaqueTypeKey<'db> = rustc_type_ir::OpaqueTypeKey<DbInterner<'db>>;
+pub type PredefinedOpaquesData<'db> = rustc_type_ir::solve::PredefinedOpaquesData<DbInterner<'db>>;
+pub type ExternalConstraintsData<'db> =
+ rustc_type_ir::solve::ExternalConstraintsData<DbInterner<'db>>;
+
+#[salsa::interned(constructor = new_, debug)]
+pub struct PredefinedOpaques<'db> {
+ #[returns(ref)]
+ kind_: rustc_type_ir::solve::PredefinedOpaquesData<DbInterner<'db>>,
+}
+
+impl<'db> PredefinedOpaques<'db> {
+ pub fn new(interner: DbInterner<'db>, data: PredefinedOpaquesData<'db>) -> Self {
+ PredefinedOpaques::new_(interner.db(), data)
+ }
+
+ pub fn inner(&self) -> &PredefinedOpaquesData<'db> {
+ salsa::with_attached_database(|db| {
+ let inner = self.kind_(db);
+ // SAFETY: ¯\_(ツ)_/¯
+ unsafe { std::mem::transmute(inner) }
+ })
+ .unwrap()
+ }
+}
+
+impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for PredefinedOpaques<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ self.opaque_types.visit_with(visitor)
+ }
+}
+
+impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for PredefinedOpaques<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(PredefinedOpaques::new(
+ folder.cx(),
+ PredefinedOpaquesData {
+ opaque_types: self
+ .opaque_types
+ .iter()
+ .cloned()
+ .map(|opaque| opaque.try_fold_with(folder))
+ .collect::<Result<_, F::Error>>()?,
+ },
+ ))
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ PredefinedOpaques::new(
+ folder.cx(),
+ PredefinedOpaquesData {
+ opaque_types: self
+ .opaque_types
+ .iter()
+ .cloned()
+ .map(|opaque| opaque.fold_with(folder))
+ .collect(),
+ },
+ )
+ }
+}
+
+impl<'db> std::ops::Deref for PredefinedOpaques<'db> {
+ type Target = PredefinedOpaquesData<'db>;
+
+ fn deref(&self) -> &Self::Target {
+ self.inner()
+ }
+}
+
+interned_vec_nolifetime_salsa!(SolverDefIds, SolverDefId);
+
+#[salsa::interned(constructor = new_, debug)]
+pub struct ExternalConstraints<'db> {
+ #[returns(ref)]
+ kind_: rustc_type_ir::solve::ExternalConstraintsData<DbInterner<'db>>,
+}
+
+impl<'db> ExternalConstraints<'db> {
+ pub fn new(interner: DbInterner<'db>, data: ExternalConstraintsData<'db>) -> Self {
+ ExternalConstraints::new_(interner.db(), data)
+ }
+
+ pub fn inner(&self) -> &ExternalConstraintsData<'db> {
+ salsa::with_attached_database(|db| {
+ let inner = self.kind_(db);
+ // SAFETY: ¯\_(ツ)_/¯
+ unsafe { std::mem::transmute(inner) }
+ })
+ .unwrap()
+ }
+}
+
+impl<'db> std::ops::Deref for ExternalConstraints<'db> {
+ type Target = ExternalConstraintsData<'db>;
+
+ fn deref(&self) -> &Self::Target {
+ self.inner()
+ }
+}
+
+impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for ExternalConstraints<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ try_visit!(self.region_constraints.visit_with(visitor));
+ try_visit!(self.opaque_types.visit_with(visitor));
+ self.normalization_nested_goals.visit_with(visitor)
+ }
+}
+
+impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for ExternalConstraints<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(ExternalConstraints::new(
+ folder.cx(),
+ ExternalConstraintsData {
+ region_constraints: self.region_constraints.clone().try_fold_with(folder)?,
+ opaque_types: self
+ .opaque_types
+ .iter()
+ .cloned()
+ .map(|opaque| opaque.try_fold_with(folder))
+ .collect::<Result<_, F::Error>>()?,
+ normalization_nested_goals: self
+ .normalization_nested_goals
+ .clone()
+ .try_fold_with(folder)?,
+ },
+ ))
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ ExternalConstraints::new(
+ folder.cx(),
+ ExternalConstraintsData {
+ region_constraints: self.region_constraints.clone().fold_with(folder),
+ opaque_types: self
+ .opaque_types
+ .iter()
+ .cloned()
+ .map(|opaque| opaque.fold_with(folder))
+ .collect(),
+ normalization_nested_goals: self
+ .normalization_nested_goals
+ .clone()
+ .fold_with(folder),
+ },
+ )
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/predicate.rs b/crates/hir-ty/src/next_solver/predicate.rs
new file mode 100644
index 0000000..50261bb
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/predicate.rs
@@ -0,0 +1,894 @@
+//! Things related to predicates.
+
+use std::cmp::Ordering;
+
+use intern::Interned;
+use rustc_ast_ir::try_visit;
+use rustc_type_ir::{
+ self as ty, CollectAndApply, DebruijnIndex, EarlyBinder, FlagComputation, Flags,
+ PredicatePolarity, TypeFlags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
+ TypeVisitable, Upcast, UpcastFrom, VisitorResult, WithCachedTypeInfo,
+ elaborate::Elaboratable,
+ error::{ExpectedFound, TypeError},
+ inherent::{IntoKind, SliceLike},
+ relate::Relate,
+};
+use smallvec::{SmallVec, smallvec};
+
+use super::{Binder, BoundVarKinds, DbInterner, Region, Ty, interned_vec_db};
+
+pub type BoundExistentialPredicate<'db> = Binder<'db, ExistentialPredicate<'db>>;
+
+pub type TraitRef<'db> = ty::TraitRef<DbInterner<'db>>;
+pub type AliasTerm<'db> = ty::AliasTerm<DbInterner<'db>>;
+pub type ProjectionPredicate<'db> = ty::ProjectionPredicate<DbInterner<'db>>;
+pub type ExistentialPredicate<'db> = ty::ExistentialPredicate<DbInterner<'db>>;
+pub type ExistentialTraitRef<'db> = ty::ExistentialTraitRef<DbInterner<'db>>;
+pub type ExistentialProjection<'db> = ty::ExistentialProjection<DbInterner<'db>>;
+pub type TraitPredicate<'db> = ty::TraitPredicate<DbInterner<'db>>;
+pub type ClauseKind<'db> = ty::ClauseKind<DbInterner<'db>>;
+pub type PredicateKind<'db> = ty::PredicateKind<DbInterner<'db>>;
+pub type NormalizesTo<'db> = ty::NormalizesTo<DbInterner<'db>>;
+pub type CoercePredicate<'db> = ty::CoercePredicate<DbInterner<'db>>;
+pub type SubtypePredicate<'db> = ty::SubtypePredicate<DbInterner<'db>>;
+pub type OutlivesPredicate<'db, T> = ty::OutlivesPredicate<DbInterner<'db>, T>;
+pub type RegionOutlivesPredicate<'db> = OutlivesPredicate<'db, Region<'db>>;
+pub type TypeOutlivesPredicate<'db> = OutlivesPredicate<'db, Ty<'db>>;
+pub type PolyTraitPredicate<'db> = Binder<'db, TraitPredicate<'db>>;
+pub type PolyRegionOutlivesPredicate<'db> = Binder<'db, RegionOutlivesPredicate<'db>>;
+pub type PolyTypeOutlivesPredicate<'db> = Binder<'db, TypeOutlivesPredicate<'db>>;
+pub type PolySubtypePredicate<'db> = Binder<'db, SubtypePredicate<'db>>;
+pub type PolyCoercePredicate<'db> = Binder<'db, CoercePredicate<'db>>;
+pub type PolyProjectionPredicate<'db> = Binder<'db, ProjectionPredicate<'db>>;
+pub type PolyTraitRef<'db> = Binder<'db, TraitRef<'db>>;
+pub type PolyExistentialTraitRef<'db> = Binder<'db, ExistentialTraitRef<'db>>;
+pub type PolyExistentialProjection<'db> = Binder<'db, ExistentialProjection<'db>>;
+
+/// Compares via an ordering that will not change if modules are reordered or other changes are
+/// made to the tree. In particular, this ordering is preserved across incremental compilations.
+fn stable_cmp_existential_predicate<'db>(
+ a: &ExistentialPredicate<'db>,
+ b: &ExistentialPredicate<'db>,
+) -> Ordering {
+ // FIXME: this is actual unstable - see impl in predicate.rs in `rustc_middle`
+ match (a, b) {
+ (ExistentialPredicate::Trait(_), ExistentialPredicate::Trait(_)) => Ordering::Equal,
+ (ExistentialPredicate::Projection(a), ExistentialPredicate::Projection(b)) => {
+ // Should sort by def path hash
+ Ordering::Equal
+ }
+ (ExistentialPredicate::AutoTrait(a), ExistentialPredicate::AutoTrait(b)) => {
+ // Should sort by def path hash
+ Ordering::Equal
+ }
+ (ExistentialPredicate::Trait(_), _) => Ordering::Less,
+ (ExistentialPredicate::Projection(_), ExistentialPredicate::Trait(_)) => Ordering::Greater,
+ (ExistentialPredicate::Projection(_), _) => Ordering::Less,
+ (ExistentialPredicate::AutoTrait(_), _) => Ordering::Greater,
+ }
+}
+interned_vec_db!(BoundExistentialPredicates, BoundExistentialPredicate);
+
+impl<'db> rustc_type_ir::inherent::BoundExistentialPredicates<DbInterner<'db>>
+ for BoundExistentialPredicates<'db>
+{
+ fn principal_def_id(self) -> Option<<DbInterner<'db> as rustc_type_ir::Interner>::DefId> {
+ self.principal().map(|trait_ref| trait_ref.skip_binder().def_id)
+ }
+
+ fn principal(
+ self,
+ ) -> Option<
+ rustc_type_ir::Binder<DbInterner<'db>, rustc_type_ir::ExistentialTraitRef<DbInterner<'db>>>,
+ > {
+ self.inner()[0]
+ .map_bound(|this| match this {
+ ExistentialPredicate::Trait(tr) => Some(tr),
+ _ => None,
+ })
+ .transpose()
+ }
+
+ fn auto_traits(
+ self,
+ ) -> impl IntoIterator<Item = <DbInterner<'db> as rustc_type_ir::Interner>::DefId> {
+ self.iter().filter_map(|predicate| match predicate.skip_binder() {
+ ExistentialPredicate::AutoTrait(did) => Some(did),
+ _ => None,
+ })
+ }
+
+ fn projection_bounds(
+ self,
+ ) -> impl IntoIterator<
+ Item = rustc_type_ir::Binder<
+ DbInterner<'db>,
+ rustc_type_ir::ExistentialProjection<DbInterner<'db>>,
+ >,
+ > {
+ self.iter().filter_map(|predicate| {
+ predicate
+ .map_bound(|pred| match pred {
+ ExistentialPredicate::Projection(projection) => Some(projection),
+ _ => None,
+ })
+ .transpose()
+ })
+ }
+}
+
+impl<'db> rustc_type_ir::relate::Relate<DbInterner<'db>> for BoundExistentialPredicates<'db> {
+ fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
+ relation: &mut R,
+ a: Self,
+ b: Self,
+ ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
+ let interner = relation.cx();
+
+ // We need to perform this deduplication as we sometimes generate duplicate projections in `a`.
+ let mut a_v: Vec<_> = a.into_iter().collect();
+ let mut b_v: Vec<_> = b.into_iter().collect();
+ // `skip_binder` here is okay because `stable_cmp` doesn't look at binders
+ a_v.sort_by(|a, b| {
+ stable_cmp_existential_predicate(a.as_ref().skip_binder(), b.as_ref().skip_binder())
+ });
+ a_v.dedup();
+ b_v.sort_by(|a, b| {
+ stable_cmp_existential_predicate(a.as_ref().skip_binder(), b.as_ref().skip_binder())
+ });
+ b_v.dedup();
+ if a_v.len() != b_v.len() {
+ return Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b)));
+ }
+
+ let v = std::iter::zip(a_v, b_v).map(
+ |(ep_a, ep_b): (
+ Binder<'_, ty::ExistentialPredicate<_>>,
+ Binder<'_, ty::ExistentialPredicate<_>>,
+ )| {
+ match (ep_a.skip_binder(), ep_b.skip_binder()) {
+ (ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => {
+ Ok(ep_a.rebind(ty::ExistentialPredicate::Trait(
+ relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
+ )))
+ }
+ (
+ ty::ExistentialPredicate::Projection(a),
+ ty::ExistentialPredicate::Projection(b),
+ ) => Ok(ep_a.rebind(ty::ExistentialPredicate::Projection(
+ relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
+ ))),
+ (
+ ty::ExistentialPredicate::AutoTrait(a),
+ ty::ExistentialPredicate::AutoTrait(b),
+ ) if a == b => Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))),
+ _ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))),
+ }
+ },
+ );
+
+ CollectAndApply::collect_and_apply(v, |g| {
+ BoundExistentialPredicates::new_from_iter(interner, g.iter().cloned())
+ })
+ }
+}
+
+#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
+pub struct InternedWrapperNoDebug<T>(pub(crate) T);
+
+#[salsa::interned(constructor = new_)]
+pub struct Predicate<'db> {
+ #[returns(ref)]
+ kind_: InternedWrapperNoDebug<WithCachedTypeInfo<Binder<'db, PredicateKind<'db>>>>,
+}
+
+impl<'db> std::fmt::Debug for Predicate<'db> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.inner().internee.fmt(f)
+ }
+}
+
+impl<'db> std::fmt::Debug
+ for InternedWrapperNoDebug<WithCachedTypeInfo<Binder<'db, PredicateKind<'db>>>>
+{
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "Binder<")?;
+ match self.0.internee.skip_binder() {
+ rustc_type_ir::PredicateKind::Clause(clause_kind) => {
+ write!(f, "{clause_kind:?}")
+ }
+ rustc_type_ir::PredicateKind::DynCompatible(trait_def_id) => {
+ write!(f, "the trait `{trait_def_id:?}` is dyn-compatible")
+ }
+ rustc_type_ir::PredicateKind::Subtype(subtype_predicate) => {
+ write!(f, "{subtype_predicate:?}")
+ }
+ rustc_type_ir::PredicateKind::Coerce(coerce_predicate) => {
+ write!(f, "{coerce_predicate:?}")
+ }
+ rustc_type_ir::PredicateKind::ConstEquate(c1, c2) => {
+ write!(f, "the constant `{c1:?}` equals `{c2:?}`")
+ }
+ rustc_type_ir::PredicateKind::Ambiguous => write!(f, "ambiguous"),
+ rustc_type_ir::PredicateKind::NormalizesTo(data) => write!(f, "{data:?}"),
+ rustc_type_ir::PredicateKind::AliasRelate(t1, t2, dir) => {
+ write!(f, "{t1:?} {dir:?} {t2:?}")
+ }
+ }?;
+ write!(f, ", [{:?}]>", self.0.internee.bound_vars())?;
+ Ok(())
+ }
+}
+
+impl<'db> Predicate<'db> {
+ pub fn new(interner: DbInterner<'db>, kind: Binder<'db, PredicateKind<'db>>) -> Self {
+ let flags = FlagComputation::for_predicate(kind);
+ let cached = WithCachedTypeInfo {
+ internee: kind,
+ flags: flags.flags,
+ outer_exclusive_binder: flags.outer_exclusive_binder,
+ };
+ Predicate::new_(interner.db(), InternedWrapperNoDebug(cached))
+ }
+
+ pub fn inner(&self) -> &WithCachedTypeInfo<Binder<'db, PredicateKind<'db>>> {
+ salsa::with_attached_database(|db| {
+ let inner = &self.kind_(db).0;
+ // SAFETY: The caller already has access to a `Predicate<'db>`, so borrowchecking will
+ // make sure that our returned value is valid for the lifetime `'db`.
+ unsafe { std::mem::transmute(inner) }
+ })
+ .unwrap()
+ }
+
+ /// Flips the polarity of a Predicate.
+ ///
+ /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`.
+ pub fn flip_polarity(self) -> Option<Predicate<'db>> {
+ let kind = self
+ .kind()
+ .map_bound(|kind| match kind {
+ PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
+ trait_ref,
+ polarity,
+ })) => Some(PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
+ trait_ref,
+ polarity: polarity.flip(),
+ }))),
+
+ _ => None,
+ })
+ .transpose()?;
+
+ Some(Predicate::new(DbInterner::conjure(), kind))
+ }
+
+ pub fn as_trait_clause(self) -> Option<PolyTraitPredicate<'db>> {
+ let predicate = self.kind();
+ match predicate.skip_binder() {
+ PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)),
+ PredicateKind::Clause(ClauseKind::Projection(..))
+ | PredicateKind::Clause(ClauseKind::HostEffect(..))
+ | PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
+ | PredicateKind::Clause(ClauseKind::UnstableFeature(_))
+ | PredicateKind::NormalizesTo(..)
+ | PredicateKind::AliasRelate(..)
+ | PredicateKind::Subtype(..)
+ | PredicateKind::Coerce(..)
+ | PredicateKind::Clause(ClauseKind::RegionOutlives(..))
+ | PredicateKind::Clause(ClauseKind::WellFormed(..))
+ | PredicateKind::DynCompatible(..)
+ | PredicateKind::Clause(ClauseKind::TypeOutlives(..))
+ | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..))
+ | PredicateKind::ConstEquate(..)
+ | PredicateKind::Ambiguous => None,
+ }
+ }
+}
+
+// FIXME: should make a "header" in interned_vec
+
+#[derive(Debug, Clone)]
+pub struct InternedClausesWrapper<'db>(SmallVec<[Clause<'db>; 2]>, TypeFlags, DebruijnIndex);
+
+impl<'db> PartialEq for InternedClausesWrapper<'db> {
+ fn eq(&self, other: &Self) -> bool {
+ self.0.eq(&other.0)
+ }
+}
+
+impl<'db> Eq for InternedClausesWrapper<'db> {}
+
+impl<'db> std::hash::Hash for InternedClausesWrapper<'db> {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.0.hash(state)
+ }
+}
+
+type InternedClauses<'db> = Interned<InternedClausesWrapper<'db>>;
+
+#[salsa::interned(constructor = new_)]
+pub struct Clauses<'db> {
+ #[returns(ref)]
+ inner_: InternedClausesWrapper<'db>,
+}
+
+impl<'db> Clauses<'db> {
+ pub fn new_from_iter(
+ interner: DbInterner<'db>,
+ data: impl IntoIterator<Item = Clause<'db>>,
+ ) -> Self {
+ let clauses: SmallVec<_> = data.into_iter().collect();
+ let flags = FlagComputation::<DbInterner<'db>>::for_clauses(&clauses);
+ let wrapper = InternedClausesWrapper(clauses, flags.flags, flags.outer_exclusive_binder);
+ Clauses::new_(interner.db(), wrapper)
+ }
+
+ pub fn inner(&self) -> &InternedClausesWrapper<'db> {
+ salsa::with_attached_database(|db| {
+ let inner = self.inner_(db);
+ // SAFETY: The caller already has access to a `Clauses<'db>`, so borrowchecking will
+ // make sure that our returned value is valid for the lifetime `'db`.
+ unsafe { std::mem::transmute(inner) }
+ })
+ .unwrap()
+ }
+}
+
+impl<'db> std::fmt::Debug for Clauses<'db> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.inner().0.fmt(f)
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::Clauses<DbInterner<'db>> for Clauses<'db> {}
+
+impl<'db> rustc_type_ir::inherent::SliceLike for Clauses<'db> {
+ type Item = Clause<'db>;
+
+ type IntoIter = <smallvec::SmallVec<[Clause<'db>; 2]> as IntoIterator>::IntoIter;
+
+ fn iter(self) -> Self::IntoIter {
+ self.inner().0.clone().into_iter()
+ }
+
+ fn as_slice(&self) -> &[Self::Item] {
+ self.inner().0.as_slice()
+ }
+}
+
+impl<'db> IntoIterator for Clauses<'db> {
+ type Item = Clause<'db>;
+ type IntoIter = <Self as rustc_type_ir::inherent::SliceLike>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ rustc_type_ir::inherent::SliceLike::iter(self)
+ }
+}
+
+impl<'db> Default for Clauses<'db> {
+ fn default() -> Self {
+ Clauses::new_from_iter(DbInterner::conjure(), [])
+ }
+}
+
+impl<'db> rustc_type_ir::TypeSuperFoldable<DbInterner<'db>> for Clauses<'db> {
+ fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ let mut clauses: SmallVec<[_; 2]> = SmallVec::with_capacity(self.inner().0.len());
+ for c in self {
+ clauses.push(c.try_fold_with(folder)?);
+ }
+ Ok(Clauses::new_from_iter(folder.cx(), clauses))
+ }
+
+ fn super_fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Self {
+ let mut clauses: SmallVec<[_; 2]> = SmallVec::with_capacity(self.inner().0.len());
+ for c in self {
+ clauses.push(c.fold_with(folder));
+ }
+ Clauses::new_from_iter(folder.cx(), clauses)
+ }
+}
+
+impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for Clauses<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ use rustc_type_ir::inherent::SliceLike as _;
+ let inner: smallvec::SmallVec<[_; 2]> =
+ self.iter().map(|v| v.try_fold_with(folder)).collect::<Result<_, _>>()?;
+ Ok(Clauses::new_from_iter(folder.cx(), inner))
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ use rustc_type_ir::inherent::SliceLike as _;
+ let inner: smallvec::SmallVec<[_; 2]> = self.iter().map(|v| v.fold_with(folder)).collect();
+ Clauses::new_from_iter(folder.cx(), inner)
+ }
+}
+
+impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for Clauses<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ use rustc_ast_ir::visit::VisitorResult;
+ use rustc_type_ir::inherent::SliceLike as _;
+ rustc_ast_ir::walk_visitable_list!(visitor, self.as_slice().iter());
+ V::Result::output()
+ }
+}
+
+impl<'db> rustc_type_ir::Flags for Clauses<'db> {
+ fn flags(&self) -> rustc_type_ir::TypeFlags {
+ self.inner().1
+ }
+
+ fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
+ self.inner().2
+ }
+}
+
+impl<'db> rustc_type_ir::TypeSuperVisitable<DbInterner<'db>> for Clauses<'db> {
+ fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ self.as_slice().visit_with(visitor)
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] // TODO implement Debug by hand
+pub struct Clause<'db>(pub(crate) Predicate<'db>);
+
+// We could cram the reveal into the clauses like rustc does, probably
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct ParamEnv<'db> {
+ pub(crate) clauses: Clauses<'db>,
+}
+
+impl<'db> ParamEnv<'db> {
+ pub fn empty() -> Self {
+ ParamEnv { clauses: Clauses::new_from_iter(DbInterner::conjure(), []) }
+ }
+}
+
+impl<'db> TypeVisitable<DbInterner<'db>> for ParamEnv<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ try_visit!(self.clauses.visit_with(visitor));
+ V::Result::output()
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for ParamEnv<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(ParamEnv { clauses: self.clauses.try_fold_with(folder)? })
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ ParamEnv { clauses: self.clauses.fold_with(folder) }
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::ParamEnv<DbInterner<'db>> for ParamEnv<'db> {
+ fn caller_bounds(self) -> impl rustc_type_ir::inherent::SliceLike<Item = Clause<'db>> {
+ self.clauses
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct ParamEnvAnd<'db, T> {
+ pub param_env: ParamEnv<'db>,
+ pub value: T,
+}
+
+impl<'db, T> ParamEnvAnd<'db, T> {
+ pub fn into_parts(self) -> (ParamEnv<'db>, T) {
+ (self.param_env, self.value)
+ }
+}
+
+impl<'db> TypeVisitable<DbInterner<'db>> for Predicate<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ visitor.visit_predicate(*self)
+ }
+}
+
+impl<'db> TypeSuperVisitable<DbInterner<'db>> for Predicate<'db> {
+ fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ (*self).kind().visit_with(visitor)
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for Predicate<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ folder.try_fold_predicate(self)
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ folder.fold_predicate(self)
+ }
+}
+
+impl<'db> TypeSuperFoldable<DbInterner<'db>> for Predicate<'db> {
+ fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ let new = self.kind().try_fold_with(folder)?;
+ Ok(Predicate::new(folder.cx(), new))
+ }
+ fn super_fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Self {
+ let new = self.kind().fold_with(folder);
+ Predicate::new(folder.cx(), new)
+ }
+}
+
+impl<'db> Elaboratable<DbInterner<'db>> for Predicate<'db> {
+ fn predicate(&self) -> <DbInterner<'db> as rustc_type_ir::Interner>::Predicate {
+ *self
+ }
+
+ fn child(&self, clause: <DbInterner<'db> as rustc_type_ir::Interner>::Clause) -> Self {
+ clause.as_predicate()
+ }
+
+ fn child_with_derived_cause(
+ &self,
+ clause: <DbInterner<'db> as rustc_type_ir::Interner>::Clause,
+ _span: <DbInterner<'db> as rustc_type_ir::Interner>::Span,
+ _parent_trait_pred: rustc_type_ir::Binder<
+ DbInterner<'db>,
+ rustc_type_ir::TraitPredicate<DbInterner<'db>>,
+ >,
+ _index: usize,
+ ) -> Self {
+ clause.as_predicate()
+ }
+}
+
+impl<'db> Flags for Predicate<'db> {
+ fn flags(&self) -> rustc_type_ir::TypeFlags {
+ self.inner().flags
+ }
+
+ fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
+ self.inner().outer_exclusive_binder
+ }
+}
+
+impl<'db> IntoKind for Predicate<'db> {
+ type Kind = Binder<'db, PredicateKind<'db>>;
+
+ fn kind(self) -> Self::Kind {
+ self.inner().internee
+ }
+}
+
+impl<'db> UpcastFrom<DbInterner<'db>, ty::PredicateKind<DbInterner<'db>>> for Predicate<'db> {
+ fn upcast_from(from: ty::PredicateKind<DbInterner<'db>>, interner: DbInterner<'db>) -> Self {
+ Binder::dummy(from).upcast(interner)
+ }
+}
+impl<'db>
+ UpcastFrom<DbInterner<'db>, ty::Binder<DbInterner<'db>, ty::PredicateKind<DbInterner<'db>>>>
+ for Predicate<'db>
+{
+ fn upcast_from(
+ from: ty::Binder<DbInterner<'db>, ty::PredicateKind<DbInterner<'db>>>,
+ interner: DbInterner<'db>,
+ ) -> Self {
+ Predicate::new(interner, from)
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ty::ClauseKind<DbInterner<'db>>> for Predicate<'db> {
+ fn upcast_from(from: ty::ClauseKind<DbInterner<'db>>, interner: DbInterner<'db>) -> Self {
+ Binder::dummy(PredicateKind::Clause(from)).upcast(interner)
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ty::Binder<DbInterner<'db>, ty::ClauseKind<DbInterner<'db>>>>
+ for Predicate<'db>
+{
+ fn upcast_from(
+ from: ty::Binder<DbInterner<'db>, ty::ClauseKind<DbInterner<'db>>>,
+ interner: DbInterner<'db>,
+ ) -> Self {
+ from.map_bound(PredicateKind::Clause).upcast(interner)
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, Clause<'db>> for Predicate<'db> {
+ fn upcast_from(from: Clause<'db>, _interner: DbInterner<'db>) -> Self {
+ from.0
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ty::NormalizesTo<DbInterner<'db>>> for Predicate<'db> {
+ fn upcast_from(from: ty::NormalizesTo<DbInterner<'db>>, interner: DbInterner<'db>) -> Self {
+ PredicateKind::NormalizesTo(from).upcast(interner)
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ty::TraitRef<DbInterner<'db>>> for Predicate<'db> {
+ fn upcast_from(from: ty::TraitRef<DbInterner<'db>>, interner: DbInterner<'db>) -> Self {
+ Binder::dummy(from).upcast(interner)
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ty::Binder<DbInterner<'db>, ty::TraitRef<DbInterner<'db>>>>
+ for Predicate<'db>
+{
+ fn upcast_from(
+ from: ty::Binder<DbInterner<'db>, ty::TraitRef<DbInterner<'db>>>,
+ interner: DbInterner<'db>,
+ ) -> Self {
+ from.map_bound(|trait_ref| TraitPredicate {
+ trait_ref,
+ polarity: PredicatePolarity::Positive,
+ })
+ .upcast(interner)
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, Binder<'db, ty::TraitPredicate<DbInterner<'db>>>>
+ for Predicate<'db>
+{
+ fn upcast_from(
+ from: Binder<'db, ty::TraitPredicate<DbInterner<'db>>>,
+ interner: DbInterner<'db>,
+ ) -> Self {
+ from.map_bound(|it| PredicateKind::Clause(ClauseKind::Trait(it))).upcast(interner)
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, Binder<'db, ProjectionPredicate<'db>>> for Predicate<'db> {
+ fn upcast_from(from: Binder<'db, ProjectionPredicate<'db>>, interner: DbInterner<'db>) -> Self {
+ from.map_bound(|it| PredicateKind::Clause(ClauseKind::Projection(it))).upcast(interner)
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ProjectionPredicate<'db>> for Predicate<'db> {
+ fn upcast_from(from: ProjectionPredicate<'db>, interner: DbInterner<'db>) -> Self {
+ PredicateKind::Clause(ClauseKind::Projection(from)).upcast(interner)
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ty::TraitPredicate<DbInterner<'db>>> for Predicate<'db> {
+ fn upcast_from(from: ty::TraitPredicate<DbInterner<'db>>, interner: DbInterner<'db>) -> Self {
+ PredicateKind::Clause(ClauseKind::Trait(from)).upcast(interner)
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ty::OutlivesPredicate<DbInterner<'db>, Ty<'db>>>
+ for Predicate<'db>
+{
+ fn upcast_from(
+ from: ty::OutlivesPredicate<DbInterner<'db>, Ty<'db>>,
+ interner: DbInterner<'db>,
+ ) -> Self {
+ PredicateKind::Clause(ClauseKind::TypeOutlives(from)).upcast(interner)
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ty::OutlivesPredicate<DbInterner<'db>, Region<'db>>>
+ for Predicate<'db>
+{
+ fn upcast_from(
+ from: ty::OutlivesPredicate<DbInterner<'db>, Region<'db>>,
+ interner: DbInterner<'db>,
+ ) -> Self {
+ PredicateKind::Clause(ClauseKind::RegionOutlives(from)).upcast(interner)
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::Predicate<DbInterner<'db>> for Predicate<'db> {
+ fn as_clause(self) -> Option<<DbInterner<'db> as rustc_type_ir::Interner>::Clause> {
+ match self.kind().skip_binder() {
+ PredicateKind::Clause(..) => Some(self.expect_clause()),
+ _ => None,
+ }
+ }
+
+ /// Whether this projection can be soundly normalized.
+ ///
+ /// Wf predicates must not be normalized, as normalization
+ /// can remove required bounds which would cause us to
+ /// unsoundly accept some programs. See #91068.
+ fn allow_normalization(self) -> bool {
+ // TODO: this should probably live in rustc_type_ir
+ match self.inner().as_ref().skip_binder() {
+ PredicateKind::Clause(ClauseKind::WellFormed(_))
+ | PredicateKind::AliasRelate(..)
+ | PredicateKind::NormalizesTo(..) => false,
+ PredicateKind::Clause(ClauseKind::Trait(_))
+ | PredicateKind::Clause(ClauseKind::RegionOutlives(_))
+ | PredicateKind::Clause(ClauseKind::TypeOutlives(_))
+ | PredicateKind::Clause(ClauseKind::Projection(_))
+ | PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
+ | PredicateKind::Clause(ClauseKind::HostEffect(..))
+ | PredicateKind::Clause(ClauseKind::UnstableFeature(_))
+ | PredicateKind::DynCompatible(_)
+ | PredicateKind::Subtype(_)
+ | PredicateKind::Coerce(_)
+ | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_))
+ | PredicateKind::ConstEquate(_, _)
+ | PredicateKind::Ambiguous => true,
+ }
+ }
+}
+
+impl<'db> Predicate<'db> {
+ /// Assert that the predicate is a clause.
+ pub fn expect_clause(self) -> Clause<'db> {
+ match self.kind().skip_binder() {
+ PredicateKind::Clause(..) => Clause(self),
+ _ => panic!("{self:?} is not a clause"),
+ }
+ }
+}
+
+impl<'db> TypeVisitable<DbInterner<'db>> for Clause<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ visitor.visit_predicate((*self).as_predicate())
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for Clause<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(folder.try_fold_predicate(self.as_predicate())?.expect_clause())
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ folder.fold_predicate(self.as_predicate()).expect_clause()
+ }
+}
+
+impl<'db> IntoKind for Clause<'db> {
+ type Kind = Binder<'db, ClauseKind<'db>>;
+
+ fn kind(self) -> Self::Kind {
+ self.0.kind().map_bound(|pk| match pk {
+ PredicateKind::Clause(kind) => kind,
+ _ => unreachable!(),
+ })
+ }
+}
+
+impl<'db> Clause<'db> {
+ pub fn as_predicate(self) -> Predicate<'db> {
+ self.0
+ }
+}
+
+impl<'db> Elaboratable<DbInterner<'db>> for Clause<'db> {
+ fn predicate(&self) -> <DbInterner<'db> as rustc_type_ir::Interner>::Predicate {
+ self.0
+ }
+
+ fn child(&self, clause: <DbInterner<'db> as rustc_type_ir::Interner>::Clause) -> Self {
+ clause
+ }
+
+ fn child_with_derived_cause(
+ &self,
+ clause: <DbInterner<'db> as rustc_type_ir::Interner>::Clause,
+ _span: <DbInterner<'db> as rustc_type_ir::Interner>::Span,
+ _parent_trait_pred: rustc_type_ir::Binder<
+ DbInterner<'db>,
+ rustc_type_ir::TraitPredicate<DbInterner<'db>>,
+ >,
+ _index: usize,
+ ) -> Self {
+ clause
+ }
+}
+
+impl<'db> UpcastFrom<DbInterner<'db>, ty::Binder<DbInterner<'db>, ty::ClauseKind<DbInterner<'db>>>>
+ for Clause<'db>
+{
+ fn upcast_from(
+ from: ty::Binder<DbInterner<'db>, ty::ClauseKind<DbInterner<'db>>>,
+ interner: DbInterner<'db>,
+ ) -> Self {
+ Clause(from.map_bound(PredicateKind::Clause).upcast(interner))
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ty::TraitRef<DbInterner<'db>>> for Clause<'db> {
+ fn upcast_from(from: ty::TraitRef<DbInterner<'db>>, interner: DbInterner<'db>) -> Self {
+ Clause(from.upcast(interner))
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ty::Binder<DbInterner<'db>, ty::TraitRef<DbInterner<'db>>>>
+ for Clause<'db>
+{
+ fn upcast_from(
+ from: ty::Binder<DbInterner<'db>, ty::TraitRef<DbInterner<'db>>>,
+ interner: DbInterner<'db>,
+ ) -> Self {
+ Clause(from.upcast(interner))
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ty::TraitPredicate<DbInterner<'db>>> for Clause<'db> {
+ fn upcast_from(from: ty::TraitPredicate<DbInterner<'db>>, interner: DbInterner<'db>) -> Self {
+ Clause(from.upcast(interner))
+ }
+}
+impl<'db>
+ UpcastFrom<DbInterner<'db>, ty::Binder<DbInterner<'db>, ty::TraitPredicate<DbInterner<'db>>>>
+ for Clause<'db>
+{
+ fn upcast_from(
+ from: ty::Binder<DbInterner<'db>, ty::TraitPredicate<DbInterner<'db>>>,
+ interner: DbInterner<'db>,
+ ) -> Self {
+ Clause(from.upcast(interner))
+ }
+}
+impl<'db> UpcastFrom<DbInterner<'db>, ty::ProjectionPredicate<DbInterner<'db>>> for Clause<'db> {
+ fn upcast_from(
+ from: ty::ProjectionPredicate<DbInterner<'db>>,
+ interner: DbInterner<'db>,
+ ) -> Self {
+ Clause(from.upcast(interner))
+ }
+}
+impl<'db>
+ UpcastFrom<
+ DbInterner<'db>,
+ ty::Binder<DbInterner<'db>, ty::ProjectionPredicate<DbInterner<'db>>>,
+ > for Clause<'db>
+{
+ fn upcast_from(
+ from: ty::Binder<DbInterner<'db>, ty::ProjectionPredicate<DbInterner<'db>>>,
+ interner: DbInterner<'db>,
+ ) -> Self {
+ Clause(from.upcast(interner))
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::Clause<DbInterner<'db>> for Clause<'db> {
+ fn as_predicate(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::Predicate {
+ self.0
+ }
+
+ fn instantiate_supertrait(
+ self,
+ cx: DbInterner<'db>,
+ trait_ref: rustc_type_ir::Binder<DbInterner<'db>, rustc_type_ir::TraitRef<DbInterner<'db>>>,
+ ) -> Self {
+ tracing::debug!(?self, ?trait_ref);
+ // See the rustc impl for a long comment
+ let bound_pred = self.kind();
+ let pred_bound_vars = bound_pred.bound_vars();
+ let trait_bound_vars = trait_ref.bound_vars();
+ // 1) Self: Bar1<'a, '^0.0> -> Self: Bar1<'a, '^0.1>
+ let shifted_pred =
+ cx.shift_bound_var_indices(trait_bound_vars.len(), bound_pred.skip_binder());
+ // 2) Self: Bar1<'a, '^0.1> -> T: Bar1<'^0.0, '^0.1>
+ let new = EarlyBinder::bind(shifted_pred).instantiate(cx, trait_ref.skip_binder().args);
+ // 3) ['x] + ['b] -> ['x, 'b]
+ let bound_vars =
+ BoundVarKinds::new_from_iter(cx, trait_bound_vars.iter().chain(pred_bound_vars.iter()));
+
+ let predicate: Predicate<'db> =
+ ty::Binder::bind_with_vars(PredicateKind::Clause(new), bound_vars).upcast(cx);
+ predicate.expect_clause()
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/project.rs b/crates/hir-ty/src/next_solver/project.rs
new file mode 100644
index 0000000..e578087
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/project.rs
@@ -0,0 +1,3 @@
+//! Projection code for next-solver.
+
+pub(crate) mod solve_normalize;
diff --git a/crates/hir-ty/src/next_solver/project/solve_normalize.rs b/crates/hir-ty/src/next_solver/project/solve_normalize.rs
new file mode 100644
index 0000000..a8fb2ae
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/project/solve_normalize.rs
@@ -0,0 +1,264 @@
+//! Normalization within a next-solver infer context.
+
+use std::fmt::Debug;
+
+use rustc_next_trait_solver::placeholder::BoundVarReplacer;
+use rustc_type_ir::{
+ AliasRelationDirection, FallibleTypeFolder, Flags, Interner, TermKind, TypeFoldable,
+ TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex,
+ inherent::{IntoKind, Span as _, Term as _},
+};
+
+use crate::next_solver::{
+ Binder, Const, ConstKind, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Span, Term, Ty,
+ TyKind,
+ fulfill::{FulfillmentCtxt, NextSolverError},
+ infer::{
+ InferCtxt,
+ at::At,
+ traits::{Obligation, ObligationCause},
+ },
+ util::PlaceholderReplacer,
+};
+
+/// Deeply normalize all aliases in `value`. This does not handle inference and expects
+/// its input to be already fully resolved.
+pub fn deeply_normalize<'db, T>(at: At<'_, 'db>, value: T) -> Result<T, Vec<NextSolverError<'db>>>
+where
+ T: TypeFoldable<DbInterner<'db>>,
+{
+ assert!(!value.has_escaping_bound_vars());
+ deeply_normalize_with_skipped_universes(at, value, vec![])
+}
+
+/// Deeply normalize all aliases in `value`. This does not handle inference and expects
+/// its input to be already fully resolved.
+///
+/// Additionally takes a list of universes which represents the binders which have been
+/// entered before passing `value` to the function. This is currently needed for
+/// `normalize_erasing_regions`, which skips binders as it walks through a type.
+pub fn deeply_normalize_with_skipped_universes<'db, T>(
+ at: At<'_, 'db>,
+ value: T,
+ universes: Vec<Option<UniverseIndex>>,
+) -> Result<T, Vec<NextSolverError<'db>>>
+where
+ T: TypeFoldable<DbInterner<'db>>,
+{
+ let (value, coroutine_goals) =
+ deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
+ at, value, universes,
+ )?;
+ assert_eq!(coroutine_goals, vec![]);
+
+ Ok(value)
+}
+
+/// Deeply normalize all aliases in `value`. This does not handle inference and expects
+/// its input to be already fully resolved.
+///
+/// Additionally takes a list of universes which represents the binders which have been
+/// entered before passing `value` to the function. This is currently needed for
+/// `normalize_erasing_regions`, which skips binders as it walks through a type.
+///
+/// This returns a set of stalled obligations involving coroutines if the typing mode of
+/// the underlying infcx has any stalled coroutine def ids.
+pub fn deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals<'db, T>(
+ at: At<'_, 'db>,
+ value: T,
+ universes: Vec<Option<UniverseIndex>>,
+) -> Result<(T, Vec<Goal<'db, Predicate<'db>>>), Vec<NextSolverError<'db>>>
+where
+ T: TypeFoldable<DbInterner<'db>>,
+{
+ let fulfill_cx = FulfillmentCtxt::new(at.infcx);
+ let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes };
+ let value = value.try_fold_with(&mut folder)?;
+ let errors = folder.fulfill_cx.select_all_or_error(at.infcx);
+ if errors.is_empty() { Ok((value, vec![])) } else { Err(errors) }
+}
+
+struct NormalizationFolder<'me, 'db> {
+ at: At<'me, 'db>,
+ fulfill_cx: FulfillmentCtxt<'db>,
+ depth: usize,
+ universes: Vec<Option<UniverseIndex>>,
+}
+
+impl<'db> NormalizationFolder<'_, 'db> {
+ fn normalize_alias_term(
+ &mut self,
+ alias_term: Term<'db>,
+ ) -> Result<Term<'db>, Vec<NextSolverError<'db>>> {
+ let infcx = self.at.infcx;
+ let tcx = infcx.interner;
+ let recursion_limit = tcx.recursion_limit();
+ if self.depth > recursion_limit {
+ return Err(vec![]);
+ }
+
+ self.depth += 1;
+
+ let infer_term = infcx.next_term_var_of_kind(alias_term);
+ let obligation = Obligation::new(
+ tcx,
+ self.at.cause.clone(),
+ self.at.param_env,
+ PredicateKind::AliasRelate(alias_term, infer_term, AliasRelationDirection::Equate),
+ );
+
+ self.fulfill_cx.register_predicate_obligation(infcx, obligation);
+ self.select_all_and_stall_coroutine_predicates()?;
+
+ // Alias is guaranteed to be fully structurally resolved,
+ // so we can super fold here.
+ let term = infcx.resolve_vars_if_possible(infer_term);
+ // super-folding the `term` will directly fold the `Ty` or `Const` so
+ // we have to match on the term and super-fold them manually.
+ let result = match term.kind() {
+ TermKind::Ty(ty) => ty.try_super_fold_with(self)?.into(),
+ TermKind::Const(ct) => ct.try_super_fold_with(self)?.into(),
+ };
+ self.depth -= 1;
+ Ok(result)
+ }
+
+ fn select_all_and_stall_coroutine_predicates(
+ &mut self,
+ ) -> Result<(), Vec<NextSolverError<'db>>> {
+ let errors = self.fulfill_cx.select_where_possible(self.at.infcx);
+ if !errors.is_empty() {
+ return Err(errors);
+ }
+
+ let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx);
+ if !errors.is_empty() {
+ return Err(errors);
+ }
+
+ Ok(())
+ }
+}
+
+impl<'db> FallibleTypeFolder<DbInterner<'db>> for NormalizationFolder<'_, 'db> {
+ type Error = Vec<NextSolverError<'db>>;
+
+ fn cx(&self) -> DbInterner<'db> {
+ self.at.infcx.interner
+ }
+
+ fn try_fold_binder<T: TypeFoldable<DbInterner<'db>>>(
+ &mut self,
+ t: Binder<'db, T>,
+ ) -> Result<Binder<'db, T>, Self::Error> {
+ self.universes.push(None);
+ let t = t.try_super_fold_with(self)?;
+ self.universes.pop();
+ Ok(t)
+ }
+
+ #[tracing::instrument(level = "trace", skip(self), ret)]
+ fn try_fold_ty(&mut self, ty: Ty<'db>) -> Result<Ty<'db>, Self::Error> {
+ let infcx = self.at.infcx;
+ debug_assert_eq!(ty, infcx.shallow_resolve(ty));
+ if !ty.has_aliases() {
+ return Ok(ty);
+ }
+
+ let TyKind::Alias(..) = ty.kind() else { return ty.try_super_fold_with(self) };
+
+ if ty.has_escaping_bound_vars() {
+ let (ty, mapped_regions, mapped_types, mapped_consts) =
+ BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ty);
+ let result = self.normalize_alias_term(ty.into())?.expect_type();
+ Ok(PlaceholderReplacer::replace_placeholders(
+ infcx,
+ mapped_regions,
+ mapped_types,
+ mapped_consts,
+ &self.universes,
+ result,
+ ))
+ } else {
+ Ok(self.normalize_alias_term(ty.into())?.expect_type())
+ }
+ }
+
+ #[tracing::instrument(level = "trace", skip(self), ret)]
+ fn try_fold_const(&mut self, ct: Const<'db>) -> Result<Const<'db>, Self::Error> {
+ let infcx = self.at.infcx;
+ debug_assert_eq!(ct, infcx.shallow_resolve_const(ct));
+ if !ct.has_aliases() {
+ return Ok(ct);
+ }
+
+ let ConstKind::Unevaluated(..) = ct.kind() else { return ct.try_super_fold_with(self) };
+
+ if ct.has_escaping_bound_vars() {
+ let (ct, mapped_regions, mapped_types, mapped_consts) =
+ BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ct);
+ let result = self.normalize_alias_term(ct.into())?.expect_const();
+ Ok(PlaceholderReplacer::replace_placeholders(
+ infcx,
+ mapped_regions,
+ mapped_types,
+ mapped_consts,
+ &self.universes,
+ result,
+ ))
+ } else {
+ Ok(self.normalize_alias_term(ct.into())?.expect_const())
+ }
+ }
+}
+
+// Deeply normalize a value and return it
+pub(crate) fn deeply_normalize_for_diagnostics<'db, T: TypeFoldable<DbInterner<'db>>>(
+ infcx: &InferCtxt<'db>,
+ param_env: ParamEnv<'db>,
+ t: T,
+) -> T {
+ t.fold_with(&mut DeeplyNormalizeForDiagnosticsFolder {
+ at: infcx.at(&ObligationCause::dummy(), param_env),
+ })
+}
+
+struct DeeplyNormalizeForDiagnosticsFolder<'a, 'db> {
+ at: At<'a, 'db>,
+}
+
+impl<'db> TypeFolder<DbInterner<'db>> for DeeplyNormalizeForDiagnosticsFolder<'_, 'db> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.at.infcx.interner
+ }
+
+ fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
+ let infcx = self.at.infcx;
+ let result: Result<_, Vec<NextSolverError<'db>>> = infcx.commit_if_ok(|_| {
+ deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
+ self.at,
+ ty,
+ vec![None; ty.outer_exclusive_binder().as_usize()],
+ )
+ });
+ match result {
+ Ok((ty, _)) => ty,
+ Err(_) => ty.super_fold_with(self),
+ }
+ }
+
+ fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
+ let infcx = self.at.infcx;
+ let result: Result<_, Vec<NextSolverError<'db>>> = infcx.commit_if_ok(|_| {
+ deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
+ self.at,
+ ct,
+ vec![None; ct.outer_exclusive_binder().as_usize()],
+ )
+ });
+ match result {
+ Ok((ct, _)) => ct,
+ Err(_) => ct.super_fold_with(self),
+ }
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/region.rs b/crates/hir-ty/src/next_solver/region.rs
new file mode 100644
index 0000000..c59cdac
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/region.rs
@@ -0,0 +1,332 @@
+//! Things related to regions.
+
+use intern::{Interned, Symbol};
+use rustc_type_ir::{
+ BoundVar, Flags, INNERMOST, RegionVid, TypeFlags, TypeFoldable, TypeVisitable, VisitorResult,
+ inherent::{IntoKind, PlaceholderLike, SliceLike},
+ relate::Relate,
+};
+
+use crate::next_solver::{GenericArg, OutlivesPredicate};
+
+use super::{
+ ErrorGuaranteed, SolverDefId, interned_vec_db,
+ interner::{BoundVarKind, DbInterner, Placeholder},
+};
+
+type RegionKind<'db> = rustc_type_ir::RegionKind<DbInterner<'db>>;
+
+#[salsa::interned(constructor = new_, debug)]
+pub struct Region<'db> {
+ #[returns(ref)]
+ kind_: RegionKind<'db>,
+}
+
+impl<'db> Region<'db> {
+ pub fn new(interner: DbInterner<'db>, kind: RegionKind<'db>) -> Self {
+ Region::new_(interner.db(), kind)
+ }
+
+ pub fn inner(&self) -> &RegionKind<'db> {
+ salsa::with_attached_database(|db| {
+ let inner = self.kind_(db);
+ // SAFETY: The caller already has access to a `Region<'db>`, so borrowchecking will
+ // make sure that our returned value is valid for the lifetime `'db`.
+ unsafe { std::mem::transmute::<&RegionKind<'_>, &RegionKind<'db>>(inner) }
+ })
+ .unwrap()
+ }
+
+ pub fn new_early_param(
+ interner: DbInterner<'db>,
+ early_bound_region: EarlyParamRegion,
+ ) -> Self {
+ Region::new(interner, RegionKind::ReEarlyParam(early_bound_region))
+ }
+
+ pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderRegion) -> Self {
+ Region::new(interner, RegionKind::RePlaceholder(placeholder))
+ }
+
+ pub fn new_var(interner: DbInterner<'db>, v: RegionVid) -> Region<'db> {
+ Region::new(interner, RegionKind::ReVar(v))
+ }
+
+ pub fn is_placeholder(&self) -> bool {
+ matches!(self.inner(), RegionKind::RePlaceholder(..))
+ }
+
+ pub fn is_static(&self) -> bool {
+ matches!(self.inner(), RegionKind::ReStatic)
+ }
+
+ pub fn error(interner: DbInterner<'db>) -> Self {
+ Region::new(interner, RegionKind::ReError(ErrorGuaranteed))
+ }
+
+ pub fn type_flags(&self) -> TypeFlags {
+ let mut flags = TypeFlags::empty();
+
+ match &self.inner() {
+ RegionKind::ReVar(..) => {
+ flags |= TypeFlags::HAS_FREE_REGIONS;
+ flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS;
+ flags |= TypeFlags::HAS_RE_INFER;
+ }
+ RegionKind::RePlaceholder(..) => {
+ flags |= TypeFlags::HAS_FREE_REGIONS;
+ flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS;
+ flags |= TypeFlags::HAS_RE_PLACEHOLDER;
+ }
+ RegionKind::ReEarlyParam(..) => {
+ flags |= TypeFlags::HAS_FREE_REGIONS;
+ flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS;
+ flags |= TypeFlags::HAS_RE_PARAM;
+ }
+ RegionKind::ReLateParam(..) => {
+ flags |= TypeFlags::HAS_FREE_REGIONS;
+ flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS;
+ }
+ RegionKind::ReStatic => {
+ flags |= TypeFlags::HAS_FREE_REGIONS;
+ }
+ RegionKind::ReBound(..) => {
+ flags |= TypeFlags::HAS_RE_BOUND;
+ }
+ RegionKind::ReErased => {
+ flags |= TypeFlags::HAS_RE_ERASED;
+ }
+ RegionKind::ReError(..) => {
+ flags |= TypeFlags::HAS_FREE_REGIONS;
+ flags |= TypeFlags::HAS_ERROR;
+ }
+ }
+
+ flags
+ }
+}
+
+pub type PlaceholderRegion = Placeholder<BoundRegion>;
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct EarlyParamRegion {
+ pub index: u32,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+/// The parameter representation of late-bound function parameters, "some region
+/// at least as big as the scope `fr.scope`".
+///
+/// Similar to a placeholder region as we create `LateParam` regions when entering a binder
+/// except they are always in the root universe and instead of using a boundvar to distinguish
+/// between others we use the `DefId` of the parameter. For this reason the `bound_region` field
+/// should basically always be `BoundRegionKind::Named` as otherwise there is no way of telling
+/// different parameters apart.
+pub struct LateParamRegion {
+ pub scope: SolverDefId,
+ pub bound_region: BoundRegionKind,
+}
+
+impl std::fmt::Debug for LateParamRegion {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "ReLateParam({:?}, {:?})", self.scope, self.bound_region)
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub enum BoundRegionKind {
+ /// An anonymous region parameter for a given fn (&T)
+ Anon,
+
+ /// Named region parameters for functions (a in &'a T)
+ ///
+ /// The `DefId` is needed to distinguish free regions in
+ /// the event of shadowing.
+ Named(SolverDefId),
+
+ /// Anonymous region for the implicit env pointer parameter
+ /// to a closure
+ ClosureEnv,
+}
+
+impl std::fmt::Debug for BoundRegionKind {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match *self {
+ BoundRegionKind::Anon => write!(f, "BrAnon"),
+ BoundRegionKind::Named(did) => {
+ write!(f, "BrNamed({did:?})")
+ }
+ BoundRegionKind::ClosureEnv => write!(f, "BrEnv"),
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct BoundRegion {
+ pub var: BoundVar,
+ pub kind: BoundRegionKind,
+}
+
+impl rustc_type_ir::inherent::ParamLike for EarlyParamRegion {
+ fn index(self) -> u32 {
+ self.index
+ }
+}
+
+impl std::fmt::Debug for EarlyParamRegion {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "#{}", self.index)
+ // write!(f, "{}/#{}", self.name, self.index)
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::BoundVarLike<DbInterner<'db>> for BoundRegion {
+ fn var(self) -> BoundVar {
+ self.var
+ }
+
+ fn assert_eq(self, var: BoundVarKind) {
+ assert_eq!(self.kind, var.expect_region())
+ }
+}
+
+impl core::fmt::Debug for BoundRegion {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match &self.kind {
+ BoundRegionKind::Anon => write!(f, "{:?}", self.var),
+ BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var),
+ BoundRegionKind::Named(def) => {
+ write!(f, "{:?}.Named({:?})", self.var, def)
+ }
+ }
+ }
+}
+
+impl BoundRegionKind {
+ pub fn is_named(&self) -> bool {
+ matches!(self, BoundRegionKind::Named(_))
+ }
+
+ pub fn get_name(&self) -> Option<Symbol> {
+ None
+ }
+
+ pub fn get_id(&self) -> Option<SolverDefId> {
+ match self {
+ BoundRegionKind::Named(id) => Some(*id),
+ _ => None,
+ }
+ }
+}
+
+impl<'db> IntoKind for Region<'db> {
+ type Kind = RegionKind<'db>;
+
+ fn kind(self) -> Self::Kind {
+ *self.inner()
+ }
+}
+
+impl<'db> TypeVisitable<DbInterner<'db>> for Region<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ visitor.visit_region(*self)
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for Region<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ folder.try_fold_region(self)
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ folder.fold_region(self)
+ }
+}
+
+impl<'db> Relate<DbInterner<'db>> for Region<'db> {
+ fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
+ relation: &mut R,
+ a: Self,
+ b: Self,
+ ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
+ relation.regions(a, b)
+ }
+}
+
+impl<'db> Flags for Region<'db> {
+ fn flags(&self) -> rustc_type_ir::TypeFlags {
+ self.type_flags()
+ }
+
+ fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
+ match &self.inner() {
+ RegionKind::ReBound(debruijn, _) => debruijn.shifted_in(1),
+ _ => INNERMOST,
+ }
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::Region<DbInterner<'db>> for Region<'db> {
+ fn new_bound(
+ interner: DbInterner<'db>,
+ debruijn: rustc_type_ir::DebruijnIndex,
+ var: BoundRegion,
+ ) -> Self {
+ Region::new(interner, RegionKind::ReBound(debruijn, var))
+ }
+
+ fn new_anon_bound(
+ interner: DbInterner<'db>,
+ debruijn: rustc_type_ir::DebruijnIndex,
+ var: rustc_type_ir::BoundVar,
+ ) -> Self {
+ Region::new(
+ interner,
+ RegionKind::ReBound(debruijn, BoundRegion { var, kind: BoundRegionKind::Anon }),
+ )
+ }
+
+ fn new_static(interner: DbInterner<'db>) -> Self {
+ Region::new(interner, RegionKind::ReStatic)
+ }
+
+ fn new_placeholder(
+ interner: DbInterner<'db>,
+ var: <DbInterner<'db> as rustc_type_ir::Interner>::PlaceholderRegion,
+ ) -> Self {
+ Region::new(interner, RegionKind::RePlaceholder(var))
+ }
+}
+
+impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderRegion {
+ type Bound = BoundRegion;
+
+ fn universe(self) -> rustc_type_ir::UniverseIndex {
+ self.universe
+ }
+
+ fn var(self) -> rustc_type_ir::BoundVar {
+ self.bound.var
+ }
+
+ fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self {
+ Placeholder { universe: ui, bound: self.bound }
+ }
+
+ fn new(ui: rustc_type_ir::UniverseIndex, bound: Self::Bound) -> Self {
+ Placeholder { universe: ui, bound }
+ }
+
+ fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self {
+ Placeholder { universe: ui, bound: BoundRegion { var, kind: BoundRegionKind::Anon } }
+ }
+}
+
+type GenericArgOutlivesPredicate<'db> = OutlivesPredicate<'db, GenericArg<'db>>;
+
+interned_vec_db!(RegionAssumptions, GenericArgOutlivesPredicate);
diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs
new file mode 100644
index 0000000..45888ac
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/solver.rs
@@ -0,0 +1,289 @@
+//! Defining `SolverContext` for next-trait-solver.
+
+use hir_def::{AssocItemId, GeneralConstId, TypeAliasId};
+use rustc_next_trait_solver::delegate::SolverDelegate;
+use rustc_type_ir::{
+ InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, UniverseIndex,
+ inherent::{IntoKind, SliceLike, Span as _, Term as _, Ty as _},
+ lang_items::TraitSolverLangItem,
+ solve::{Certainty, NoSolution},
+};
+
+use crate::{
+ TraitRefExt,
+ db::HirDatabase,
+ next_solver::{
+ ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate,
+ mapping::{ChalkToNextSolver, convert_args_for_result},
+ util::sizedness_fast_path,
+ },
+};
+
+use super::{
+ Canonical, CanonicalVarValues, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
+ ParamEnv, Predicate, SolverDefId, Span, Ty, UnevaluatedConst,
+ infer::{DbInternerInferExt, InferCtxt, canonical::instantiate::CanonicalExt},
+};
+
+pub type Goal<'db, P> = rustc_type_ir::solve::Goal<DbInterner<'db>, P>;
+
+#[repr(transparent)]
+pub(crate) struct SolverContext<'db>(pub(crate) InferCtxt<'db>);
+
+impl<'a, 'db> From<&'a InferCtxt<'db>> for &'a SolverContext<'db> {
+ fn from(infcx: &'a InferCtxt<'db>) -> Self {
+ // SAFETY: `repr(transparent)`
+ unsafe { std::mem::transmute(infcx) }
+ }
+}
+
+impl<'db> std::ops::Deref for SolverContext<'db> {
+ type Target = InferCtxt<'db>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl<'db> SolverDelegate for SolverContext<'db> {
+ type Interner = DbInterner<'db>;
+ type Infcx = InferCtxt<'db>;
+
+ fn cx(&self) -> Self::Interner {
+ self.0.interner
+ }
+
+ fn build_with_canonical<V>(
+ cx: Self::Interner,
+ canonical: &rustc_type_ir::CanonicalQueryInput<Self::Interner, V>,
+ ) -> (Self, V, rustc_type_ir::CanonicalVarValues<Self::Interner>)
+ where
+ V: rustc_type_ir::TypeFoldable<Self::Interner>,
+ {
+ let (infcx, value, vars) = cx.infer_ctxt().build_with_canonical(canonical);
+ (SolverContext(infcx), value, vars)
+ }
+
+ fn fresh_var_for_kind_with_span(
+ &self,
+ arg: <Self::Interner as rustc_type_ir::Interner>::GenericArg,
+ span: <Self::Interner as rustc_type_ir::Interner>::Span,
+ ) -> <Self::Interner as rustc_type_ir::Interner>::GenericArg {
+ unimplemented!()
+ }
+
+ fn leak_check(
+ &self,
+ max_input_universe: rustc_type_ir::UniverseIndex,
+ ) -> Result<(), NoSolution> {
+ Ok(())
+ }
+
+ fn well_formed_goals(
+ &self,
+ param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv,
+ arg: <Self::Interner as rustc_type_ir::Interner>::Term,
+ ) -> Option<
+ Vec<
+ rustc_type_ir::solve::Goal<
+ Self::Interner,
+ <Self::Interner as rustc_type_ir::Interner>::Predicate,
+ >,
+ >,
+ > {
+ unimplemented!()
+ }
+
+ fn make_deduplicated_outlives_constraints(
+ &self,
+ ) -> Vec<
+ rustc_type_ir::OutlivesPredicate<
+ Self::Interner,
+ <Self::Interner as rustc_type_ir::Interner>::GenericArg,
+ >,
+ > {
+ // FIXME: add if we care about regions
+ vec![]
+ }
+
+ fn instantiate_canonical<V>(
+ &self,
+ canonical: rustc_type_ir::Canonical<Self::Interner, V>,
+ values: rustc_type_ir::CanonicalVarValues<Self::Interner>,
+ ) -> V
+ where
+ V: rustc_type_ir::TypeFoldable<Self::Interner>,
+ {
+ canonical.instantiate(self.cx(), &values)
+ }
+
+ fn instantiate_canonical_var_with_infer(
+ &self,
+ cv_info: rustc_type_ir::CanonicalVarKind<Self::Interner>,
+ _span: <Self::Interner as rustc_type_ir::Interner>::Span,
+ universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex,
+ ) -> <Self::Interner as rustc_type_ir::Interner>::GenericArg {
+ self.0.instantiate_canonical_var(cv_info, universe_map)
+ }
+
+ fn add_item_bounds_for_hidden_type(
+ &self,
+ def_id: <Self::Interner as rustc_type_ir::Interner>::DefId,
+ args: <Self::Interner as rustc_type_ir::Interner>::GenericArgs,
+ param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv,
+ hidden_ty: <Self::Interner as rustc_type_ir::Interner>::Ty,
+ goals: &mut Vec<
+ rustc_type_ir::solve::Goal<
+ Self::Interner,
+ <Self::Interner as rustc_type_ir::Interner>::Predicate,
+ >,
+ >,
+ ) {
+ unimplemented!()
+ }
+
+ fn fetch_eligible_assoc_item(
+ &self,
+ goal_trait_ref: rustc_type_ir::TraitRef<Self::Interner>,
+ trait_assoc_def_id: <Self::Interner as rustc_type_ir::Interner>::DefId,
+ impl_def_id: <Self::Interner as rustc_type_ir::Interner>::DefId,
+ ) -> Result<Option<<Self::Interner as rustc_type_ir::Interner>::DefId>, ErrorGuaranteed> {
+ let impl_id = match impl_def_id {
+ SolverDefId::ImplId(id) => id,
+ _ => panic!("Unexpected SolverDefId"),
+ };
+ let trait_assoc_id = match trait_assoc_def_id {
+ SolverDefId::TypeAliasId(id) => id,
+ _ => panic!("Unexpected SolverDefId"),
+ };
+ let trait_ref = self
+ .0
+ .interner
+ .db()
+ .impl_trait(impl_id)
+ // 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()
+ .0;
+ let trait_ = trait_ref.hir_trait_id();
+ let trait_data = trait_.trait_items(self.0.interner.db());
+ let id =
+ impl_id.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> {
+ match item {
+ (_, AssocItemId::TypeAliasId(type_alias)) => {
+ let name = &self.0.interner.db().type_alias_signature(*type_alias).name;
+ let found_trait_assoc_id = trait_data.associated_type_by_name(name)?;
+ (found_trait_assoc_id == trait_assoc_id).then_some(*type_alias)
+ }
+ _ => None,
+ }
+ });
+ Ok(id.map(SolverDefId::TypeAliasId))
+ }
+
+ fn is_transmutable(
+ &self,
+ dst: <Self::Interner as rustc_type_ir::Interner>::Ty,
+ src: <Self::Interner as rustc_type_ir::Interner>::Ty,
+ assume: <Self::Interner as rustc_type_ir::Interner>::Const,
+ ) -> Result<Certainty, NoSolution> {
+ unimplemented!()
+ }
+
+ fn evaluate_const(
+ &self,
+ param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv,
+ uv: rustc_type_ir::UnevaluatedConst<Self::Interner>,
+ ) -> Option<<Self::Interner as rustc_type_ir::Interner>::Const> {
+ let c = match uv.def {
+ SolverDefId::ConstId(c) => GeneralConstId::ConstId(c),
+ SolverDefId::StaticId(c) => GeneralConstId::StaticId(c),
+ _ => unreachable!(),
+ };
+ let subst = convert_args_for_result(self.interner, uv.args.as_slice());
+ let ec = self.cx().db.const_eval(c, subst, None).ok()?;
+ Some(ec.to_nextsolver(self.interner))
+ }
+
+ fn compute_goal_fast_path(
+ &self,
+ goal: rustc_type_ir::solve::Goal<
+ Self::Interner,
+ <Self::Interner as rustc_type_ir::Interner>::Predicate,
+ >,
+ span: <Self::Interner as rustc_type_ir::Interner>::Span,
+ ) -> Option<Certainty> {
+ if let Some(trait_pred) = goal.predicate.as_trait_clause() {
+ if self.shallow_resolve(trait_pred.self_ty().skip_binder()).is_ty_var()
+ // We don't do this fast path when opaques are defined since we may
+ // eventually use opaques to incompletely guide inference via ty var
+ // self types.
+ // FIXME: Properly consider opaques here.
+ && self.inner.borrow_mut().opaque_types().is_empty()
+ {
+ return Some(Certainty::AMBIGUOUS);
+ }
+
+ if trait_pred.polarity() == PredicatePolarity::Positive {
+ match self.0.cx().as_lang_item(trait_pred.def_id()) {
+ Some(TraitSolverLangItem::Sized) | Some(TraitSolverLangItem::MetaSized) => {
+ let predicate = self.resolve_vars_if_possible(goal.predicate);
+ if sizedness_fast_path(self.cx(), predicate, goal.param_env) {
+ return Some(Certainty::Yes);
+ }
+ }
+ Some(TraitSolverLangItem::Copy | TraitSolverLangItem::Clone) => {
+ let self_ty =
+ self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder());
+ // Unlike `Sized` traits, which always prefer the built-in impl,
+ // `Copy`/`Clone` may be shadowed by a param-env candidate which
+ // could force a lifetime error or guide inference. While that's
+ // not generally desirable, it is observable, so for now let's
+ // ignore this fast path for types that have regions or infer.
+ if !self_ty
+ .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER)
+ && self_ty.is_trivially_pure_clone_copy()
+ {
+ return Some(Certainty::Yes);
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+
+ let pred = goal.predicate.kind();
+ match pred.no_bound_vars()? {
+ PredicateKind::Clause(ClauseKind::RegionOutlives(outlives)) => Some(Certainty::Yes),
+ PredicateKind::Clause(ClauseKind::TypeOutlives(outlives)) => Some(Certainty::Yes),
+ PredicateKind::Subtype(SubtypePredicate { a, b, .. })
+ | PredicateKind::Coerce(CoercePredicate { a, b }) => {
+ if self.shallow_resolve(a).is_ty_var() && self.shallow_resolve(b).is_ty_var() {
+ // FIXME: We also need to register a subtype relation between these vars
+ // when those are added, and if they aren't in the same sub root then
+ // we should mark this goal as `has_changed`.
+ Some(Certainty::AMBIGUOUS)
+ } else {
+ None
+ }
+ }
+ PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, _)) => {
+ if self.shallow_resolve_const(ct).is_ct_infer() {
+ Some(Certainty::AMBIGUOUS)
+ } else {
+ None
+ }
+ }
+ PredicateKind::Clause(ClauseKind::WellFormed(arg)) => {
+ if arg.is_trivially_wf(self.interner) {
+ Some(Certainty::Yes)
+ } else if arg.is_infer() {
+ Some(Certainty::AMBIGUOUS)
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs
new file mode 100644
index 0000000..0c0fe68
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/ty.rs
@@ -0,0 +1,943 @@
+//! Things related to tys in the next-trait-solver.
+
+use intern::{Interned, Symbol, sym};
+use rustc_abi::{Float, Integer, Size};
+use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult};
+use rustc_type_ir::{
+ BoundVar, ClosureKind, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy, IntVid,
+ TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, UintTy,
+ WithCachedTypeInfo,
+ inherent::{
+ AdtDef, BoundVarLike, GenericArgs as _, IntoKind, ParamLike, PlaceholderLike, SliceLike,
+ },
+ relate::Relate,
+ solve::SizedTraitKind,
+ walk::TypeWalker,
+};
+use salsa::plumbing::{AsId, FromId};
+use smallvec::SmallVec;
+
+use crate::{
+ db::HirDatabase,
+ interner::InternedWrapperNoDebug,
+ next_solver::util::{CoroutineArgsExt, IntegerTypeExt},
+};
+
+use super::{
+ BoundVarKind, DbInterner, GenericArgs, Placeholder, SolverDefId, interned_vec_db,
+ util::{FloatExt, IntegerExt},
+};
+
+pub type TyKind<'db> = rustc_type_ir::TyKind<DbInterner<'db>>;
+pub type FnHeader<'db> = rustc_type_ir::FnHeader<DbInterner<'db>>;
+
+#[salsa::interned(constructor = new_)]
+pub struct Ty<'db> {
+ #[returns(ref)]
+ kind_: InternedWrapperNoDebug<WithCachedTypeInfo<TyKind<'db>>>,
+}
+
+const _: () = {
+ const fn is_copy<T: Copy>() {}
+ is_copy::<Ty<'static>>();
+};
+
+impl<'db> Ty<'db> {
+ pub fn new(interner: DbInterner<'db>, kind: TyKind<'db>) -> Self {
+ let flags = FlagComputation::for_kind(&kind);
+ let cached = WithCachedTypeInfo {
+ internee: kind,
+ flags: flags.flags,
+ outer_exclusive_binder: flags.outer_exclusive_binder,
+ };
+ Ty::new_(interner.db(), InternedWrapperNoDebug(cached))
+ }
+
+ pub fn inner(&self) -> &WithCachedTypeInfo<TyKind<'db>> {
+ salsa::with_attached_database(|db| {
+ let inner = &self.kind_(db).0;
+ // SAFETY: The caller already has access to a `Ty<'db>`, so borrowchecking will
+ // make sure that our returned value is valid for the lifetime `'db`.
+ unsafe { std::mem::transmute(inner) }
+ })
+ .unwrap()
+ }
+
+ pub fn new_param(interner: DbInterner<'db>, index: u32, name: Symbol) -> Self {
+ Ty::new(interner, TyKind::Param(ParamTy { index }))
+ }
+
+ pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderTy) -> Self {
+ Ty::new(interner, TyKind::Placeholder(placeholder))
+ }
+
+ pub fn new_infer(interner: DbInterner<'db>, infer: InferTy) -> Self {
+ Ty::new(interner, TyKind::Infer(infer))
+ }
+
+ pub fn new_int_var(interner: DbInterner<'db>, v: IntVid) -> Self {
+ Ty::new_infer(interner, InferTy::IntVar(v))
+ }
+
+ pub fn new_float_var(interner: DbInterner<'db>, v: FloatVid) -> Self {
+ Ty::new_infer(interner, InferTy::FloatVar(v))
+ }
+
+ pub fn new_int(interner: DbInterner<'db>, i: IntTy) -> Self {
+ Ty::new(interner, TyKind::Int(i))
+ }
+
+ pub fn new_uint(interner: DbInterner<'db>, ui: UintTy) -> Self {
+ Ty::new(interner, TyKind::Uint(ui))
+ }
+
+ pub fn new_float(interner: DbInterner<'db>, f: FloatTy) -> Self {
+ Ty::new(interner, TyKind::Float(f))
+ }
+
+ pub fn new_fresh(interner: DbInterner<'db>, n: u32) -> Self {
+ Ty::new_infer(interner, InferTy::FreshTy(n))
+ }
+
+ pub fn new_fresh_int(interner: DbInterner<'db>, n: u32) -> Self {
+ Ty::new_infer(interner, InferTy::FreshIntTy(n))
+ }
+
+ pub fn new_fresh_float(interner: DbInterner<'db>, n: u32) -> Self {
+ Ty::new_infer(interner, InferTy::FreshFloatTy(n))
+ }
+
+ /// Returns the `Size` for primitive types (bool, uint, int, char, float).
+ pub fn primitive_size(self, interner: DbInterner<'db>) -> Size {
+ match self.kind() {
+ TyKind::Bool => Size::from_bytes(1),
+ TyKind::Char => Size::from_bytes(4),
+ TyKind::Int(ity) => Integer::from_int_ty(&interner, ity).size(),
+ TyKind::Uint(uty) => Integer::from_uint_ty(&interner, uty).size(),
+ TyKind::Float(fty) => Float::from_float_ty(fty).size(),
+ _ => panic!("non primitive type"),
+ }
+ }
+
+ pub fn int_size_and_signed(self, interner: DbInterner<'db>) -> (Size, bool) {
+ match self.kind() {
+ TyKind::Int(ity) => (Integer::from_int_ty(&interner, ity).size(), true),
+ TyKind::Uint(uty) => (Integer::from_uint_ty(&interner, uty).size(), false),
+ _ => panic!("non integer discriminant"),
+ }
+ }
+
+ pub fn walk(self) -> TypeWalker<DbInterner<'db>> {
+ TypeWalker::new(self.into())
+ }
+
+ /// Fast path helper for testing if a type is `Sized` or `MetaSized`.
+ ///
+ /// Returning true means the type is known to implement the sizedness trait. Returning `false`
+ /// means nothing -- could be sized, might not be.
+ ///
+ /// Note that we could never rely on the fact that a type such as `[_]` is trivially `!Sized`
+ /// because we could be in a type environment with a bound such as `[_]: Copy`. A function with
+ /// such a bound obviously never can be called, but that doesn't mean it shouldn't typecheck.
+ /// This is why this method doesn't return `Option<bool>`.
+ #[tracing::instrument(skip(tcx), level = "debug")]
+ pub fn has_trivial_sizedness(self, tcx: DbInterner<'db>, sizedness: SizedTraitKind) -> bool {
+ match self.kind() {
+ TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_))
+ | TyKind::Uint(_)
+ | TyKind::Int(_)
+ | TyKind::Bool
+ | TyKind::Float(_)
+ | TyKind::FnDef(..)
+ | TyKind::FnPtr(..)
+ | TyKind::UnsafeBinder(_)
+ | TyKind::RawPtr(..)
+ | TyKind::Char
+ | TyKind::Ref(..)
+ | TyKind::Coroutine(..)
+ | TyKind::CoroutineWitness(..)
+ | TyKind::Array(..)
+ | TyKind::Pat(..)
+ | TyKind::Closure(..)
+ | TyKind::CoroutineClosure(..)
+ | TyKind::Never
+ | TyKind::Error(_) => true,
+
+ TyKind::Str | TyKind::Slice(_) | TyKind::Dynamic(_, _, _) => match sizedness {
+ SizedTraitKind::Sized => false,
+ SizedTraitKind::MetaSized => true,
+ },
+
+ TyKind::Foreign(..) => match sizedness {
+ SizedTraitKind::Sized | SizedTraitKind::MetaSized => false,
+ },
+
+ TyKind::Tuple(tys) => {
+ tys.last().is_none_or(|ty| ty.has_trivial_sizedness(tcx, sizedness))
+ }
+
+ TyKind::Adt(def, args) => def
+ .sizedness_constraint(tcx, sizedness)
+ .is_none_or(|ty| ty.instantiate(tcx, args).has_trivial_sizedness(tcx, sizedness)),
+
+ TyKind::Alias(..) | TyKind::Param(_) | TyKind::Placeholder(..) | TyKind::Bound(..) => {
+ false
+ }
+
+ TyKind::Infer(InferTy::TyVar(_)) => false,
+
+ TyKind::Infer(
+ InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_),
+ ) => {
+ panic!("`has_trivial_sizedness` applied to unexpected type: {self:?}")
+ }
+ }
+ }
+
+ /// Fast path helper for primitives which are always `Copy` and which
+ /// have a side-effect-free `Clone` impl.
+ ///
+ /// Returning true means the type is known to be pure and `Copy+Clone`.
+ /// Returning `false` means nothing -- could be `Copy`, might not be.
+ ///
+ /// This is mostly useful for optimizations, as these are the types
+ /// on which we can replace cloning with dereferencing.
+ pub fn is_trivially_pure_clone_copy(self) -> bool {
+ match self.kind() {
+ TyKind::Bool | TyKind::Char | TyKind::Never => true,
+
+ // These aren't even `Clone`
+ TyKind::Str | TyKind::Slice(..) | TyKind::Foreign(..) | TyKind::Dynamic(..) => false,
+
+ TyKind::Infer(InferTy::FloatVar(_) | InferTy::IntVar(_))
+ | TyKind::Int(..)
+ | TyKind::Uint(..)
+ | TyKind::Float(..) => true,
+
+ // ZST which can't be named are fine.
+ TyKind::FnDef(..) => true,
+
+ TyKind::Array(element_ty, _len) => element_ty.is_trivially_pure_clone_copy(),
+
+ // A 100-tuple isn't "trivial", so doing this only for reasonable sizes.
+ TyKind::Tuple(field_tys) => {
+ field_tys.len() <= 3 && field_tys.iter().all(Self::is_trivially_pure_clone_copy)
+ }
+
+ TyKind::Pat(ty, _) => ty.is_trivially_pure_clone_copy(),
+
+ // Sometimes traits aren't implemented for every ABI or arity,
+ // because we can't be generic over everything yet.
+ TyKind::FnPtr(..) => false,
+
+ // Definitely absolutely not copy.
+ TyKind::Ref(_, _, Mutability::Mut) => false,
+
+ // The standard library has a blanket Copy impl for shared references and raw pointers,
+ // for all unsized types.
+ TyKind::Ref(_, _, Mutability::Not) | TyKind::RawPtr(..) => true,
+
+ TyKind::Coroutine(..) | TyKind::CoroutineWitness(..) => false,
+
+ // Might be, but not "trivial" so just giving the safe answer.
+ TyKind::Adt(..) | TyKind::Closure(..) | TyKind::CoroutineClosure(..) => false,
+
+ TyKind::UnsafeBinder(_) => false,
+
+ // Needs normalization or revealing to determine, so no is the safe answer.
+ TyKind::Alias(..) => false,
+
+ TyKind::Param(..)
+ | TyKind::Placeholder(..)
+ | TyKind::Bound(..)
+ | TyKind::Infer(..)
+ | TyKind::Error(..) => false,
+ }
+ }
+
+ pub fn is_trivially_wf(self, tcx: DbInterner<'db>) -> bool {
+ match self.kind() {
+ TyKind::Bool
+ | TyKind::Char
+ | TyKind::Int(_)
+ | TyKind::Uint(_)
+ | TyKind::Float(_)
+ | TyKind::Str
+ | TyKind::Never
+ | TyKind::Param(_)
+ | TyKind::Placeholder(_)
+ | TyKind::Bound(..) => true,
+
+ TyKind::Slice(ty) => {
+ ty.is_trivially_wf(tcx) && ty.has_trivial_sizedness(tcx, SizedTraitKind::Sized)
+ }
+ TyKind::RawPtr(ty, _) => ty.is_trivially_wf(tcx),
+
+ TyKind::FnPtr(sig_tys, _) => {
+ sig_tys.skip_binder().inputs_and_output.iter().all(|ty| ty.is_trivially_wf(tcx))
+ }
+ TyKind::Ref(_, ty, _) => ty.is_global() && ty.is_trivially_wf(tcx),
+
+ TyKind::Infer(infer) => match infer {
+ InferTy::TyVar(_) => false,
+ InferTy::IntVar(_) | InferTy::FloatVar(_) => true,
+ InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => true,
+ },
+
+ TyKind::Adt(_, _)
+ | TyKind::Tuple(_)
+ | TyKind::Array(..)
+ | TyKind::Foreign(_)
+ | TyKind::Pat(_, _)
+ | TyKind::FnDef(..)
+ | TyKind::UnsafeBinder(..)
+ | TyKind::Dynamic(..)
+ | TyKind::Closure(..)
+ | TyKind::CoroutineClosure(..)
+ | TyKind::Coroutine(..)
+ | TyKind::CoroutineWitness(..)
+ | TyKind::Alias(..)
+ | TyKind::Error(_) => false,
+ }
+ }
+}
+
+impl<'db> std::fmt::Debug for Ty<'db> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.inner().internee.fmt(f)
+ }
+}
+
+impl<'db> std::fmt::Debug for InternedWrapperNoDebug<WithCachedTypeInfo<TyKind<'db>>> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.0.internee.fmt(f)
+ }
+}
+
+impl<'db> IntoKind for Ty<'db> {
+ type Kind = TyKind<'db>;
+
+ fn kind(self) -> Self::Kind {
+ self.inner().internee
+ }
+}
+
+impl<'db> TypeVisitable<DbInterner<'db>> for Ty<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ visitor.visit_ty(*self)
+ }
+}
+
+impl<'db> TypeSuperVisitable<DbInterner<'db>> for Ty<'db> {
+ fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ match (*self).kind() {
+ TyKind::RawPtr(ty, _mutbl) => ty.visit_with(visitor),
+ TyKind::Array(typ, sz) => {
+ try_visit!(typ.visit_with(visitor));
+ sz.visit_with(visitor)
+ }
+ TyKind::Slice(typ) => typ.visit_with(visitor),
+ TyKind::Adt(_, args) => args.visit_with(visitor),
+ TyKind::Dynamic(ref trait_ty, ref reg, _) => {
+ try_visit!(trait_ty.visit_with(visitor));
+ reg.visit_with(visitor)
+ }
+ TyKind::Tuple(ts) => ts.visit_with(visitor),
+ TyKind::FnDef(_, args) => args.visit_with(visitor),
+ TyKind::FnPtr(ref sig_tys, _) => sig_tys.visit_with(visitor),
+ TyKind::UnsafeBinder(f) => f.visit_with(visitor),
+ TyKind::Ref(r, ty, _) => {
+ try_visit!(r.visit_with(visitor));
+ ty.visit_with(visitor)
+ }
+ TyKind::Coroutine(_did, ref args) => args.visit_with(visitor),
+ TyKind::CoroutineWitness(_did, ref args) => args.visit_with(visitor),
+ TyKind::Closure(_did, ref args) => args.visit_with(visitor),
+ TyKind::CoroutineClosure(_did, ref args) => args.visit_with(visitor),
+ TyKind::Alias(_, ref data) => data.visit_with(visitor),
+
+ TyKind::Pat(ty, pat) => {
+ try_visit!(ty.visit_with(visitor));
+ pat.visit_with(visitor)
+ }
+
+ TyKind::Error(guar) => guar.visit_with(visitor),
+
+ TyKind::Bool
+ | TyKind::Char
+ | TyKind::Str
+ | TyKind::Int(_)
+ | TyKind::Uint(_)
+ | TyKind::Float(_)
+ | TyKind::Infer(_)
+ | TyKind::Bound(..)
+ | TyKind::Placeholder(..)
+ | TyKind::Param(..)
+ | TyKind::Never
+ | TyKind::Foreign(..) => V::Result::output(),
+ }
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for Ty<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ folder.try_fold_ty(self)
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ folder.fold_ty(self)
+ }
+}
+
+impl<'db> TypeSuperFoldable<DbInterner<'db>> for Ty<'db> {
+ fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ let kind = match self.kind() {
+ TyKind::RawPtr(ty, mutbl) => TyKind::RawPtr(ty.try_fold_with(folder)?, mutbl),
+ TyKind::Array(typ, sz) => {
+ TyKind::Array(typ.try_fold_with(folder)?, sz.try_fold_with(folder)?)
+ }
+ TyKind::Slice(typ) => TyKind::Slice(typ.try_fold_with(folder)?),
+ TyKind::Adt(tid, args) => TyKind::Adt(tid, args.try_fold_with(folder)?),
+ TyKind::Dynamic(trait_ty, region, representation) => TyKind::Dynamic(
+ trait_ty.try_fold_with(folder)?,
+ region.try_fold_with(folder)?,
+ representation,
+ ),
+ TyKind::Tuple(ts) => TyKind::Tuple(ts.try_fold_with(folder)?),
+ TyKind::FnDef(def_id, args) => TyKind::FnDef(def_id, args.try_fold_with(folder)?),
+ TyKind::FnPtr(sig_tys, hdr) => TyKind::FnPtr(sig_tys.try_fold_with(folder)?, hdr),
+ TyKind::UnsafeBinder(f) => TyKind::UnsafeBinder(f.try_fold_with(folder)?),
+ TyKind::Ref(r, ty, mutbl) => {
+ TyKind::Ref(r.try_fold_with(folder)?, ty.try_fold_with(folder)?, mutbl)
+ }
+ TyKind::Coroutine(did, args) => TyKind::Coroutine(did, args.try_fold_with(folder)?),
+ TyKind::CoroutineWitness(did, args) => {
+ TyKind::CoroutineWitness(did, args.try_fold_with(folder)?)
+ }
+ TyKind::Closure(did, args) => TyKind::Closure(did, args.try_fold_with(folder)?),
+ TyKind::CoroutineClosure(did, args) => {
+ TyKind::CoroutineClosure(did, args.try_fold_with(folder)?)
+ }
+ TyKind::Alias(kind, data) => TyKind::Alias(kind, data.try_fold_with(folder)?),
+ TyKind::Pat(ty, pat) => {
+ TyKind::Pat(ty.try_fold_with(folder)?, pat.try_fold_with(folder)?)
+ }
+
+ TyKind::Bool
+ | TyKind::Char
+ | TyKind::Str
+ | TyKind::Int(_)
+ | TyKind::Uint(_)
+ | TyKind::Float(_)
+ | TyKind::Error(_)
+ | TyKind::Infer(_)
+ | TyKind::Param(..)
+ | TyKind::Bound(..)
+ | TyKind::Placeholder(..)
+ | TyKind::Never
+ | TyKind::Foreign(..) => return Ok(self),
+ };
+
+ Ok(if self.kind() == kind { self } else { Ty::new(folder.cx(), kind) })
+ }
+ fn super_fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Self {
+ let kind = match self.kind() {
+ TyKind::RawPtr(ty, mutbl) => TyKind::RawPtr(ty.fold_with(folder), mutbl),
+ TyKind::Array(typ, sz) => TyKind::Array(typ.fold_with(folder), sz.fold_with(folder)),
+ TyKind::Slice(typ) => TyKind::Slice(typ.fold_with(folder)),
+ TyKind::Adt(tid, args) => TyKind::Adt(tid, args.fold_with(folder)),
+ TyKind::Dynamic(trait_ty, region, representation) => TyKind::Dynamic(
+ trait_ty.fold_with(folder),
+ region.fold_with(folder),
+ representation,
+ ),
+ TyKind::Tuple(ts) => TyKind::Tuple(ts.fold_with(folder)),
+ TyKind::FnDef(def_id, args) => TyKind::FnDef(def_id, args.fold_with(folder)),
+ TyKind::FnPtr(sig_tys, hdr) => TyKind::FnPtr(sig_tys.fold_with(folder), hdr),
+ TyKind::UnsafeBinder(f) => TyKind::UnsafeBinder(f.fold_with(folder)),
+ TyKind::Ref(r, ty, mutbl) => {
+ TyKind::Ref(r.fold_with(folder), ty.fold_with(folder), mutbl)
+ }
+ TyKind::Coroutine(did, args) => TyKind::Coroutine(did, args.fold_with(folder)),
+ TyKind::CoroutineWitness(did, args) => {
+ TyKind::CoroutineWitness(did, args.fold_with(folder))
+ }
+ TyKind::Closure(did, args) => TyKind::Closure(did, args.fold_with(folder)),
+ TyKind::CoroutineClosure(did, args) => {
+ TyKind::CoroutineClosure(did, args.fold_with(folder))
+ }
+ TyKind::Alias(kind, data) => TyKind::Alias(kind, data.fold_with(folder)),
+ TyKind::Pat(ty, pat) => TyKind::Pat(ty.fold_with(folder), pat.fold_with(folder)),
+
+ TyKind::Bool
+ | TyKind::Char
+ | TyKind::Str
+ | TyKind::Int(_)
+ | TyKind::Uint(_)
+ | TyKind::Float(_)
+ | TyKind::Error(_)
+ | TyKind::Infer(_)
+ | TyKind::Param(..)
+ | TyKind::Bound(..)
+ | TyKind::Placeholder(..)
+ | TyKind::Never
+ | TyKind::Foreign(..) => return self,
+ };
+
+ if self.kind() == kind { self } else { Ty::new(folder.cx(), kind) }
+ }
+}
+
+impl<'db> Relate<DbInterner<'db>> for Ty<'db> {
+ fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
+ relation: &mut R,
+ a: Self,
+ b: Self,
+ ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
+ relation.tys(a, b)
+ }
+}
+
+impl<'db> Flags for Ty<'db> {
+ fn flags(&self) -> rustc_type_ir::TypeFlags {
+ self.inner().flags
+ }
+
+ fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
+ self.inner().outer_exclusive_binder
+ }
+}
+
+impl<'db> rustc_type_ir::inherent::Ty<DbInterner<'db>> for Ty<'db> {
+ fn new_unit(interner: DbInterner<'db>) -> Self {
+ Ty::new(interner, TyKind::Tuple(Default::default()))
+ }
+
+ fn new_bool(interner: DbInterner<'db>) -> Self {
+ Ty::new(interner, TyKind::Bool)
+ }
+
+ fn new_u8(interner: DbInterner<'db>) -> Self {
+ Ty::new(interner, TyKind::Uint(rustc_type_ir::UintTy::U8))
+ }
+
+ fn new_usize(interner: DbInterner<'db>) -> Self {
+ Ty::new(interner, TyKind::Uint(rustc_type_ir::UintTy::Usize))
+ }
+
+ fn new_infer(interner: DbInterner<'db>, var: rustc_type_ir::InferTy) -> Self {
+ Ty::new(interner, TyKind::Infer(var))
+ }
+
+ fn new_var(interner: DbInterner<'db>, var: rustc_type_ir::TyVid) -> Self {
+ Ty::new(interner, TyKind::Infer(rustc_type_ir::InferTy::TyVar(var)))
+ }
+
+ fn new_param(interner: DbInterner<'db>, param: ParamTy) -> Self {
+ Ty::new(interner, TyKind::Param(param))
+ }
+
+ fn new_placeholder(interner: DbInterner<'db>, param: PlaceholderTy) -> Self {
+ Ty::new(interner, TyKind::Placeholder(param))
+ }
+
+ fn new_bound(
+ interner: DbInterner<'db>,
+ debruijn: rustc_type_ir::DebruijnIndex,
+ var: BoundTy,
+ ) -> Self {
+ Ty::new(interner, TyKind::Bound(debruijn, var))
+ }
+
+ fn new_anon_bound(
+ interner: DbInterner<'db>,
+ debruijn: rustc_type_ir::DebruijnIndex,
+ var: BoundVar,
+ ) -> Self {
+ Ty::new(interner, TyKind::Bound(debruijn, BoundTy { var, kind: BoundTyKind::Anon }))
+ }
+
+ fn new_alias(
+ interner: DbInterner<'db>,
+ kind: rustc_type_ir::AliasTyKind,
+ alias_ty: rustc_type_ir::AliasTy<DbInterner<'db>>,
+ ) -> Self {
+ Ty::new(interner, TyKind::Alias(kind, alias_ty))
+ }
+
+ fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self {
+ Ty::new(interner, TyKind::Error(guar))
+ }
+
+ fn new_adt(
+ interner: DbInterner<'db>,
+ adt_def: <DbInterner<'db> as rustc_type_ir::Interner>::AdtDef,
+ args: GenericArgs<'db>,
+ ) -> Self {
+ Ty::new(interner, TyKind::Adt(adt_def, args))
+ }
+
+ fn new_foreign(
+ interner: DbInterner<'db>,
+ def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId,
+ ) -> Self {
+ Ty::new(interner, TyKind::Foreign(def_id))
+ }
+
+ fn new_dynamic(
+ interner: DbInterner<'db>,
+ preds: <DbInterner<'db> as rustc_type_ir::Interner>::BoundExistentialPredicates,
+ region: <DbInterner<'db> as rustc_type_ir::Interner>::Region,
+ kind: rustc_type_ir::DynKind,
+ ) -> Self {
+ Ty::new(interner, TyKind::Dynamic(preds, region, kind))
+ }
+
+ fn new_coroutine(
+ interner: DbInterner<'db>,
+ def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId,
+ args: <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs,
+ ) -> Self {
+ Ty::new(interner, TyKind::Coroutine(def_id, args))
+ }
+
+ fn new_coroutine_closure(
+ interner: DbInterner<'db>,
+ def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId,
+ args: <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs,
+ ) -> Self {
+ Ty::new(interner, TyKind::CoroutineClosure(def_id, args))
+ }
+
+ fn new_closure(
+ interner: DbInterner<'db>,
+ def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId,
+ args: <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs,
+ ) -> Self {
+ Ty::new(interner, TyKind::Closure(def_id, args))
+ }
+
+ fn new_coroutine_witness(
+ interner: DbInterner<'db>,
+ def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId,
+ args: <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs,
+ ) -> Self {
+ Ty::new(interner, TyKind::CoroutineWitness(def_id, args))
+ }
+
+ fn new_ptr(interner: DbInterner<'db>, ty: Self, mutbl: rustc_ast_ir::Mutability) -> Self {
+ Ty::new(interner, TyKind::RawPtr(ty, mutbl))
+ }
+
+ fn new_ref(
+ interner: DbInterner<'db>,
+ region: <DbInterner<'db> as rustc_type_ir::Interner>::Region,
+ ty: Self,
+ mutbl: rustc_ast_ir::Mutability,
+ ) -> Self {
+ Ty::new(interner, TyKind::Ref(region, ty, mutbl))
+ }
+
+ fn new_array_with_const_len(
+ interner: DbInterner<'db>,
+ ty: Self,
+ len: <DbInterner<'db> as rustc_type_ir::Interner>::Const,
+ ) -> Self {
+ Ty::new(interner, TyKind::Array(ty, len))
+ }
+
+ fn new_slice(interner: DbInterner<'db>, ty: Self) -> Self {
+ Ty::new(interner, TyKind::Slice(ty))
+ }
+
+ fn new_tup(
+ interner: DbInterner<'db>,
+ tys: &[<DbInterner<'db> as rustc_type_ir::Interner>::Ty],
+ ) -> Self {
+ Ty::new(interner, TyKind::Tuple(Tys::new_from_iter(interner, tys.iter().cloned())))
+ }
+
+ fn new_tup_from_iter<It, T>(interner: DbInterner<'db>, iter: It) -> T::Output
+ where
+ It: Iterator<Item = T>,
+ T: rustc_type_ir::CollectAndApply<Self, Self>,
+ {
+ T::collect_and_apply(iter, |ts| Ty::new_tup(interner, ts))
+ }
+
+ fn new_fn_def(
+ interner: DbInterner<'db>,
+ def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId,
+ args: <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs,
+ ) -> Self {
+ Ty::new(interner, TyKind::FnDef(def_id, args))
+ }
+
+ fn new_fn_ptr(
+ interner: DbInterner<'db>,
+ sig: rustc_type_ir::Binder<DbInterner<'db>, rustc_type_ir::FnSig<DbInterner<'db>>>,
+ ) -> Self {
+ let (sig_tys, header) = sig.split();
+ Ty::new(interner, TyKind::FnPtr(sig_tys, header))
+ }
+
+ fn new_pat(
+ interner: DbInterner<'db>,
+ ty: Self,
+ pat: <DbInterner<'db> as rustc_type_ir::Interner>::Pat,
+ ) -> Self {
+ Ty::new(interner, TyKind::Pat(ty, pat))
+ }
+
+ fn tuple_fields(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::Tys {
+ match self.kind() {
+ TyKind::Tuple(args) => args,
+ _ => panic!("tuple_fields called on non-tuple: {self:?}"),
+ }
+ }
+
+ fn to_opt_closure_kind(self) -> Option<rustc_type_ir::ClosureKind> {
+ match self.kind() {
+ TyKind::Int(int_ty) => match int_ty {
+ IntTy::I8 => Some(ClosureKind::Fn),
+ IntTy::I16 => Some(ClosureKind::FnMut),
+ IntTy::I32 => Some(ClosureKind::FnOnce),
+ _ => unreachable!("cannot convert type `{:?}` to a closure kind", self),
+ },
+
+ // "Bound" types appear in canonical queries when the
+ // closure type is not yet known, and `Placeholder` and `Param`
+ // may be encountered in generic `AsyncFnKindHelper` goals.
+ TyKind::Bound(..) | TyKind::Placeholder(_) | TyKind::Param(_) | TyKind::Infer(_) => {
+ None
+ }
+
+ TyKind::Error(_) => Some(ClosureKind::Fn),
+
+ _ => unreachable!("cannot convert type `{:?}` to a closure kind", self),
+ }
+ }
+
+ fn from_closure_kind(interner: DbInterner<'db>, kind: rustc_type_ir::ClosureKind) -> Self {
+ match kind {
+ ClosureKind::Fn => Ty::new(interner, TyKind::Int(IntTy::I8)),
+ ClosureKind::FnMut => Ty::new(interner, TyKind::Int(IntTy::I16)),
+ ClosureKind::FnOnce => Ty::new(interner, TyKind::Int(IntTy::I32)),
+ }
+ }
+
+ fn from_coroutine_closure_kind(
+ interner: DbInterner<'db>,
+ kind: rustc_type_ir::ClosureKind,
+ ) -> Self {
+ match kind {
+ ClosureKind::Fn | ClosureKind::FnMut => Ty::new(interner, TyKind::Int(IntTy::I16)),
+ ClosureKind::FnOnce => Ty::new(interner, TyKind::Int(IntTy::I32)),
+ }
+ }
+
+ fn discriminant_ty(
+ self,
+ interner: DbInterner<'db>,
+ ) -> <DbInterner<'db> as rustc_type_ir::Interner>::Ty {
+ match self.kind() {
+ TyKind::Adt(adt, _) if adt.is_enum() => adt.repr().discr_type().to_ty(interner),
+ TyKind::Coroutine(_, args) => args.as_coroutine().discr_ty(interner),
+
+ TyKind::Param(_) | TyKind::Alias(..) | TyKind::Infer(InferTy::TyVar(_)) => {
+ /*
+ let assoc_items = tcx.associated_item_def_ids(
+ tcx.require_lang_item(hir::LangItem::DiscriminantKind, None),
+ );
+ TyKind::new_projection_from_args(tcx, assoc_items[0], tcx.mk_args(&[self.into()]))
+ */
+ unimplemented!()
+ }
+
+ TyKind::Pat(ty, _) => ty.discriminant_ty(interner),
+
+ TyKind::Bool
+ | TyKind::Char
+ | TyKind::Int(_)
+ | TyKind::Uint(_)
+ | TyKind::Float(_)
+ | TyKind::Adt(..)
+ | TyKind::Foreign(_)
+ | TyKind::Str
+ | TyKind::Array(..)
+ | TyKind::Slice(_)
+ | TyKind::RawPtr(_, _)
+ | TyKind::Ref(..)
+ | TyKind::FnDef(..)
+ | TyKind::FnPtr(..)
+ | TyKind::Dynamic(..)
+ | TyKind::Closure(..)
+ | TyKind::CoroutineClosure(..)
+ | TyKind::CoroutineWitness(..)
+ | TyKind::Never
+ | TyKind::Tuple(_)
+ | TyKind::Error(_)
+ | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => {
+ Ty::new(interner, TyKind::Uint(UintTy::U8))
+ }
+
+ TyKind::Bound(..)
+ | TyKind::Placeholder(_)
+ | TyKind::Infer(
+ InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_),
+ ) => {
+ panic!(
+ "`dself.iter().map(|v| v.try_fold_with(folder)).collect::<Result<_, _>>()?iscriminant_ty` applied to unexpected type: {self:?}"
+ )
+ }
+ TyKind::UnsafeBinder(..) => unimplemented!(),
+ }
+ }
+
+ fn new_unsafe_binder(
+ interner: DbInterner<'db>,
+ ty: rustc_type_ir::Binder<
+ DbInterner<'db>,
+ <DbInterner<'db> as rustc_type_ir::Interner>::Ty,
+ >,
+ ) -> Self {
+ Ty::new(interner, TyKind::UnsafeBinder(ty.into()))
+ }
+
+ fn has_unsafe_fields(self) -> bool {
+ false
+ }
+}
+
+interned_vec_db!(Tys, Ty);
+
+impl<'db> rustc_type_ir::inherent::Tys<DbInterner<'db>> for Tys<'db> {
+ fn inputs(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::FnInputTys {
+ Tys::new_from_iter(
+ DbInterner::conjure(),
+ self.as_slice().split_last().unwrap().1.iter().cloned(),
+ )
+ }
+
+ fn output(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::Ty {
+ *self.as_slice().split_last().unwrap().0
+ }
+}
+
+pub type PlaceholderTy = Placeholder<BoundTy>;
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct ParamTy {
+ pub index: u32,
+}
+
+impl ParamTy {
+ pub fn to_ty<'db>(self, interner: DbInterner<'db>) -> Ty<'db> {
+ Ty::new_param(interner, self.index, sym::MISSING_NAME.clone())
+ }
+}
+
+impl std::fmt::Debug for ParamTy {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "#{}", self.index)
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct BoundTy {
+ pub var: BoundVar,
+ pub kind: BoundTyKind,
+}
+
+impl std::fmt::Debug for BoundTy {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self.kind {
+ BoundTyKind::Anon => write!(f, "{:?}", self.var),
+ BoundTyKind::Param(def_id) => write!(f, "{def_id:?}"),
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum BoundTyKind {
+ Anon,
+ Param(SolverDefId),
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+pub struct ErrorGuaranteed;
+
+impl<'db> TypeVisitable<DbInterner<'db>> for ErrorGuaranteed {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ visitor.visit_error(*self)
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for ErrorGuaranteed {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(self)
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ self
+ }
+}
+
+impl ParamLike for ParamTy {
+ fn index(self) -> u32 {
+ self.index
+ }
+}
+
+impl<'db> BoundVarLike<DbInterner<'db>> for BoundTy {
+ fn var(self) -> BoundVar {
+ self.var
+ }
+
+ fn assert_eq(self, var: BoundVarKind) {
+ assert_eq!(self.kind, var.expect_ty())
+ }
+}
+
+impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderTy {
+ type Bound = BoundTy;
+
+ fn universe(self) -> rustc_type_ir::UniverseIndex {
+ self.universe
+ }
+
+ fn var(self) -> BoundVar {
+ self.bound.var
+ }
+
+ fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self {
+ Placeholder { universe: ui, bound: self.bound }
+ }
+
+ fn new(ui: rustc_type_ir::UniverseIndex, bound: BoundTy) -> Self {
+ Placeholder { universe: ui, bound }
+ }
+
+ fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self {
+ Placeholder { universe: ui, bound: BoundTy { var, kind: BoundTyKind::Anon } }
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/util.rs b/crates/hir-ty/src/next_solver/util.rs
new file mode 100644
index 0000000..cedc203
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/util.rs
@@ -0,0 +1,1064 @@
+//! Various utilities for the next-trait-solver.
+
+use std::iter;
+use std::ops::{self, ControlFlow};
+
+use base_db::Crate;
+use hir_def::lang_item::LangItem;
+use hir_def::{BlockId, HasModule, ItemContainerId, Lookup};
+use intern::sym;
+use la_arena::Idx;
+use rustc_abi::{Float, HasDataLayout, Integer, IntegerType, Primitive, ReprOptions};
+use rustc_type_ir::data_structures::IndexMap;
+use rustc_type_ir::inherent::{
+ AdtDef, Const as _, GenericArg as _, GenericArgs as _, ParamEnv as _, Region as _, SliceLike,
+ Ty as _,
+};
+use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::solve::SizedTraitKind;
+use rustc_type_ir::{
+ BoundVar, Canonical, DebruijnIndex, GenericArgKind, INNERMOST, Interner, PredicatePolarity,
+ TypeFlags, TypeVisitable, TypeVisitableExt,
+};
+use rustc_type_ir::{
+ ConstKind, CoroutineArgs, FloatTy, IntTy, RegionKind, TypeFolder, TypeSuperFoldable,
+ TypeSuperVisitable, TypeVisitor, UintTy, UniverseIndex, inherent::IntoKind,
+};
+use rustc_type_ir::{InferCtxtLike, TypeFoldable};
+
+use crate::lower_nextsolver::{LifetimeElisionKind, TyLoweringContext};
+use crate::next_solver::infer::InferCtxt;
+use crate::next_solver::{
+ CanonicalVarKind, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion,
+ TypingMode,
+};
+use crate::{
+ db::HirDatabase,
+ from_foreign_def_id,
+ method_resolution::{TraitImpls, TyFingerprint},
+};
+
+use super::fold::{BoundVarReplacer, FnMutDelegate};
+use super::generics::generics;
+use super::{
+ AliasTerm, AliasTy, Binder, BoundRegion, BoundTy, BoundTyKind, BoundVarKind, BoundVarKinds,
+ CanonicalVars, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, GenericArg,
+ GenericArgs, Predicate, PredicateKind, ProjectionPredicate, Region, SolverContext, SolverDefId,
+ Term, TraitPredicate, TraitRef, Ty, TyKind,
+};
+
+#[derive(Clone, Debug)]
+pub struct Discr<'db> {
+ /// Bit representation of the discriminant (e.g., `-128i8` is `0xFF_u128`).
+ pub val: u128,
+ pub ty: Ty<'db>,
+}
+
+impl<'db> Discr<'db> {
+ /// Adds `1` to the value and wraps around if the maximum for the type is reached.
+ pub fn wrap_incr(self, interner: DbInterner<'db>) -> Self {
+ self.checked_add(interner, 1).0
+ }
+ pub fn checked_add(self, interner: DbInterner<'db>, n: u128) -> (Self, bool) {
+ let (size, signed) = self.ty.int_size_and_signed(interner);
+ let (val, oflo) = if signed {
+ let min = size.signed_int_min();
+ let max = size.signed_int_max();
+ let val = size.sign_extend(self.val);
+ assert!(n < (i128::MAX as u128));
+ let n = n as i128;
+ let oflo = val > max - n;
+ let val = if oflo { min + (n - (max - val) - 1) } else { val + n };
+ // zero the upper bits
+ let val = val as u128;
+ let val = size.truncate(val);
+ (val, oflo)
+ } else {
+ let max = size.unsigned_int_max();
+ let val = self.val;
+ let oflo = val > max - n;
+ let val = if oflo { n - (max - val) - 1 } else { val + n };
+ (val, oflo)
+ };
+ (Self { val, ty: self.ty }, oflo)
+ }
+}
+
+pub trait IntegerTypeExt {
+ fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>;
+ fn initial_discriminant<'db>(&self, interner: DbInterner<'db>) -> Discr<'db>;
+ fn disr_incr<'db>(
+ &self,
+ interner: DbInterner<'db>,
+ val: Option<Discr<'db>>,
+ ) -> Option<Discr<'db>>;
+}
+
+impl IntegerTypeExt for IntegerType {
+ fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> {
+ match self {
+ IntegerType::Pointer(true) => Ty::new(interner, TyKind::Int(IntTy::Isize)),
+ IntegerType::Pointer(false) => Ty::new(interner, TyKind::Uint(UintTy::Usize)),
+ IntegerType::Fixed(i, s) => i.to_ty(interner, *s),
+ }
+ }
+
+ fn initial_discriminant<'db>(&self, interner: DbInterner<'db>) -> Discr<'db> {
+ Discr { val: 0, ty: self.to_ty(interner) }
+ }
+
+ fn disr_incr<'db>(
+ &self,
+ interner: DbInterner<'db>,
+ val: Option<Discr<'db>>,
+ ) -> Option<Discr<'db>> {
+ if let Some(val) = val {
+ assert_eq!(self.to_ty(interner), val.ty);
+ let (new, oflo) = val.checked_add(interner, 1);
+ if oflo { None } else { Some(new) }
+ } else {
+ Some(self.initial_discriminant(interner))
+ }
+ }
+}
+
+pub trait IntegerExt {
+ fn to_ty<'db>(&self, interner: DbInterner<'db>, signed: bool) -> Ty<'db>;
+ fn from_int_ty<C: HasDataLayout>(cx: &C, ity: IntTy) -> Integer;
+ fn from_uint_ty<C: HasDataLayout>(cx: &C, ity: UintTy) -> Integer;
+ fn repr_discr<'db>(
+ interner: DbInterner<'db>,
+ ty: Ty<'db>,
+ repr: &ReprOptions,
+ min: i128,
+ max: i128,
+ ) -> (Integer, bool);
+}
+
+impl IntegerExt for Integer {
+ #[inline]
+ fn to_ty<'db>(&self, interner: DbInterner<'db>, signed: bool) -> Ty<'db> {
+ use Integer::*;
+ match (*self, signed) {
+ (I8, false) => Ty::new(interner, TyKind::Uint(UintTy::U8)),
+ (I16, false) => Ty::new(interner, TyKind::Uint(UintTy::U16)),
+ (I32, false) => Ty::new(interner, TyKind::Uint(UintTy::U32)),
+ (I64, false) => Ty::new(interner, TyKind::Uint(UintTy::U64)),
+ (I128, false) => Ty::new(interner, TyKind::Uint(UintTy::U128)),
+ (I8, true) => Ty::new(interner, TyKind::Int(IntTy::I8)),
+ (I16, true) => Ty::new(interner, TyKind::Int(IntTy::I16)),
+ (I32, true) => Ty::new(interner, TyKind::Int(IntTy::I32)),
+ (I64, true) => Ty::new(interner, TyKind::Int(IntTy::I64)),
+ (I128, true) => Ty::new(interner, TyKind::Int(IntTy::I128)),
+ }
+ }
+
+ fn from_int_ty<C: HasDataLayout>(cx: &C, ity: IntTy) -> Integer {
+ use Integer::*;
+ match ity {
+ IntTy::I8 => I8,
+ IntTy::I16 => I16,
+ IntTy::I32 => I32,
+ IntTy::I64 => I64,
+ IntTy::I128 => I128,
+ IntTy::Isize => cx.data_layout().ptr_sized_integer(),
+ }
+ }
+ fn from_uint_ty<C: HasDataLayout>(cx: &C, ity: UintTy) -> Integer {
+ use Integer::*;
+ match ity {
+ UintTy::U8 => I8,
+ UintTy::U16 => I16,
+ UintTy::U32 => I32,
+ UintTy::U64 => I64,
+ UintTy::U128 => I128,
+ UintTy::Usize => cx.data_layout().ptr_sized_integer(),
+ }
+ }
+
+ /// Finds the appropriate Integer type and signedness for the given
+ /// signed discriminant range and `#[repr]` attribute.
+ /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
+ /// that shouldn't affect anything, other than maybe debuginfo.
+ fn repr_discr<'db>(
+ interner: DbInterner<'db>,
+ ty: Ty<'db>,
+ repr: &ReprOptions,
+ min: i128,
+ max: i128,
+ ) -> (Integer, bool) {
+ // Theoretically, negative values could be larger in unsigned representation
+ // than the unsigned representation of the signed minimum. However, if there
+ // are any negative values, the only valid unsigned representation is u128
+ // which can fit all i128 values, so the result remains unaffected.
+ let unsigned_fit = Integer::fit_unsigned(std::cmp::max(min as u128, max as u128));
+ let signed_fit = std::cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
+
+ if let Some(ity) = repr.int {
+ let discr = Integer::from_attr(&interner, ity);
+ let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
+ if discr < fit {
+ panic!(
+ "Integer::repr_discr: `#[repr]` hint too small for \
+ discriminant range of enum `{ty:?}`"
+ )
+ }
+ return (discr, ity.is_signed());
+ }
+
+ let at_least = if repr.c() {
+ // This is usually I32, however it can be different on some platforms,
+ // notably hexagon and arm-none/thumb-none
+ interner.data_layout().c_enum_min_size
+ } else {
+ // repr(Rust) enums try to be as small as possible
+ Integer::I8
+ };
+
+ // If there are no negative values, we can use the unsigned fit.
+ if min >= 0 {
+ (std::cmp::max(unsigned_fit, at_least), false)
+ } else {
+ (std::cmp::max(signed_fit, at_least), true)
+ }
+ }
+}
+
+pub trait FloatExt {
+ fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>;
+ fn from_float_ty(fty: FloatTy) -> Self;
+}
+
+impl FloatExt for Float {
+ #[inline]
+ fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> {
+ use Float::*;
+ match *self {
+ F16 => Ty::new(interner, TyKind::Float(FloatTy::F16)),
+ F32 => Ty::new(interner, TyKind::Float(FloatTy::F32)),
+ F64 => Ty::new(interner, TyKind::Float(FloatTy::F64)),
+ F128 => Ty::new(interner, TyKind::Float(FloatTy::F128)),
+ }
+ }
+
+ fn from_float_ty(fty: FloatTy) -> Self {
+ use Float::*;
+ match fty {
+ FloatTy::F16 => F16,
+ FloatTy::F32 => F32,
+ FloatTy::F64 => F64,
+ FloatTy::F128 => F128,
+ }
+ }
+}
+
+pub trait PrimitiveExt {
+ fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>;
+ fn to_int_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>;
+}
+
+impl PrimitiveExt for Primitive {
+ #[inline]
+ fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> {
+ match *self {
+ Primitive::Int(i, signed) => i.to_ty(interner, signed),
+ Primitive::Float(f) => f.to_ty(interner),
+ Primitive::Pointer(_) => Ty::new(
+ interner,
+ TyKind::RawPtr(
+ Ty::new(interner, TyKind::Tuple(Default::default())),
+ rustc_ast_ir::Mutability::Mut,
+ ),
+ ),
+ }
+ }
+
+ /// Return an *integer* type matching this primitive.
+ /// Useful in particular when dealing with enum discriminants.
+ #[inline]
+ fn to_int_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> {
+ match *self {
+ Primitive::Int(i, signed) => i.to_ty(interner, signed),
+ Primitive::Pointer(_) => {
+ let signed = false;
+ interner.data_layout().ptr_sized_integer().to_ty(interner, signed)
+ }
+ Primitive::Float(_) => panic!("floats do not have an int type"),
+ }
+ }
+}
+
+impl<'db> HasDataLayout for DbInterner<'db> {
+ fn data_layout(&self) -> &rustc_abi::TargetDataLayout {
+ unimplemented!()
+ }
+}
+
+pub trait CoroutineArgsExt<'db> {
+ fn discr_ty(&self, interner: DbInterner<'db>) -> Ty<'db>;
+}
+
+impl<'db> CoroutineArgsExt<'db> for CoroutineArgs<DbInterner<'db>> {
+ /// The type of the state discriminant used in the coroutine type.
+ #[inline]
+ fn discr_ty(&self, interner: DbInterner<'db>) -> Ty<'db> {
+ Ty::new(interner, TyKind::Uint(UintTy::U32))
+ }
+}
+
+/// Finds the max universe present
+pub struct MaxUniverse {
+ max_universe: UniverseIndex,
+}
+
+impl Default for MaxUniverse {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl MaxUniverse {
+ pub fn new() -> Self {
+ MaxUniverse { max_universe: UniverseIndex::ROOT }
+ }
+
+ pub fn max_universe(self) -> UniverseIndex {
+ self.max_universe
+ }
+}
+
+impl<'db> TypeVisitor<DbInterner<'db>> for MaxUniverse {
+ type Result = ();
+
+ fn visit_ty(&mut self, t: Ty<'db>) {
+ if let TyKind::Placeholder(placeholder) = t.kind() {
+ self.max_universe = UniverseIndex::from_u32(
+ self.max_universe.as_u32().max(placeholder.universe.as_u32()),
+ );
+ }
+
+ t.super_visit_with(self)
+ }
+
+ fn visit_const(&mut self, c: Const<'db>) {
+ if let ConstKind::Placeholder(placeholder) = c.kind() {
+ self.max_universe = UniverseIndex::from_u32(
+ self.max_universe.as_u32().max(placeholder.universe.as_u32()),
+ );
+ }
+
+ c.super_visit_with(self)
+ }
+
+ fn visit_region(&mut self, r: Region<'db>) {
+ if let RegionKind::RePlaceholder(placeholder) = r.kind() {
+ self.max_universe = UniverseIndex::from_u32(
+ self.max_universe.as_u32().max(placeholder.universe.as_u32()),
+ );
+ }
+ }
+}
+
+pub struct BottomUpFolder<'db, F, G, H>
+where
+ F: FnMut(Ty<'db>) -> Ty<'db>,
+ G: FnMut(Region<'db>) -> Region<'db>,
+ H: FnMut(Const<'db>) -> Const<'db>,
+{
+ pub interner: DbInterner<'db>,
+ pub ty_op: F,
+ pub lt_op: G,
+ pub ct_op: H,
+}
+
+impl<'db, F, G, H> TypeFolder<DbInterner<'db>> for BottomUpFolder<'db, F, G, H>
+where
+ F: FnMut(Ty<'db>) -> Ty<'db>,
+ G: FnMut(Region<'db>) -> Region<'db>,
+ H: FnMut(Const<'db>) -> Const<'db>,
+{
+ fn cx(&self) -> DbInterner<'db> {
+ self.interner
+ }
+
+ fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
+ let t = ty.super_fold_with(self);
+ (self.ty_op)(t)
+ }
+
+ fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
+ // This one is a little different, because `super_fold_with` is not
+ // implemented on non-recursive `Region`.
+ (self.lt_op)(r)
+ }
+
+ fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
+ let ct = ct.super_fold_with(self);
+ (self.ct_op)(ct)
+ }
+}
+
+pub(crate) fn for_trait_impls(
+ db: &dyn HirDatabase,
+ krate: Crate,
+ block: Option<BlockId>,
+ trait_id: hir_def::TraitId,
+ self_ty_fp: Option<TyFingerprint>,
+ mut f: impl FnMut(&TraitImpls) -> ControlFlow<()>,
+) -> ControlFlow<()> {
+ // Note: Since we're using `impls_for_trait` and `impl_provided_for`,
+ // only impls where the trait can be resolved should ever reach Chalk.
+ // `impl_datum` relies on that and will panic if the trait can't be resolved.
+ let in_deps = db.trait_impls_in_deps(krate);
+ let in_self = db.trait_impls_in_crate(krate);
+ let trait_module = trait_id.module(db);
+ let type_module = match self_ty_fp {
+ Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(db)),
+ Some(TyFingerprint::ForeignType(type_id)) => Some(from_foreign_def_id(type_id).module(db)),
+ Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(db)),
+ _ => None,
+ };
+
+ let mut def_blocks =
+ [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())];
+
+ let block_impls = iter::successors(block, |&block_id| {
+ cov_mark::hit!(block_local_impls);
+ block_id.loc(db).module.containing_block()
+ })
+ .inspect(|&block_id| {
+ // make sure we don't search the same block twice
+ def_blocks.iter_mut().for_each(|block| {
+ if *block == Some(block_id) {
+ *block = None;
+ }
+ });
+ })
+ .filter_map(|block_id| db.trait_impls_in_block(block_id));
+ f(&in_self)?;
+ for it in in_deps.iter().map(ops::Deref::deref) {
+ f(it)?;
+ }
+ for it in block_impls {
+ f(&it)?;
+ }
+ for it in def_blocks.into_iter().flatten().filter_map(|it| db.trait_impls_in_block(it)) {
+ f(&it)?;
+ }
+ ControlFlow::Continue(())
+}
+
+// FIXME(next-trait-solver): uplift
+pub fn sizedness_constraint_for_ty<'db>(
+ interner: DbInterner<'db>,
+ sizedness: SizedTraitKind,
+ ty: Ty<'db>,
+) -> Option<Ty<'db>> {
+ use rustc_type_ir::TyKind::*;
+
+ match ty.kind() {
+ // these are always sized
+ Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..)
+ | FnPtr(..) | Array(..) | Closure(..) | CoroutineClosure(..) | Coroutine(..)
+ | CoroutineWitness(..) | Never => None,
+
+ // these are never sized
+ Str | Slice(..) | Dynamic(_, _, rustc_type_ir::DynKind::Dyn) => match sizedness {
+ // Never `Sized`
+ SizedTraitKind::Sized => Some(ty),
+ // Always `MetaSized`
+ SizedTraitKind::MetaSized => None,
+ },
+
+ // Maybe `Sized` or `MetaSized`
+ Param(..) | Alias(..) | Error(_) => Some(ty),
+
+ // We cannot instantiate the binder, so just return the *original* type back,
+ // but only if the inner type has a sized constraint. Thus we skip the binder,
+ // but don't actually use the result from `sized_constraint_for_ty`.
+ UnsafeBinder(inner_ty) => {
+ sizedness_constraint_for_ty(interner, sizedness, inner_ty.skip_binder()).map(|_| ty)
+ }
+
+ // Never `MetaSized` or `Sized`
+ Foreign(..) => Some(ty),
+
+ // Recursive cases
+ Pat(ty, _) => sizedness_constraint_for_ty(interner, sizedness, ty),
+
+ Tuple(tys) => tys
+ .into_iter()
+ .last()
+ .and_then(|ty| sizedness_constraint_for_ty(interner, sizedness, ty)),
+
+ Adt(adt, args) => {
+ let tail_ty =
+ EarlyBinder::bind(adt.all_field_tys(interner).skip_binder().into_iter().last()?)
+ .instantiate(interner, args);
+ sizedness_constraint_for_ty(interner, sizedness, tail_ty)
+ }
+
+ Placeholder(..) | Bound(..) | Infer(..) => {
+ panic!("unexpected type `{ty:?}` in sizedness_constraint_for_ty")
+ }
+ }
+}
+
+pub fn apply_args_to_binder<'db, T: TypeFoldable<DbInterner<'db>>>(
+ b: Binder<'db, T>,
+ args: GenericArgs<'db>,
+ interner: DbInterner<'db>,
+) -> T {
+ let types = &mut |ty: BoundTy| args.as_slice()[ty.var.index()].expect_ty();
+ let regions = &mut |region: BoundRegion| args.as_slice()[region.var.index()].expect_region();
+ let consts = &mut |const_: BoundVar| args.as_slice()[const_.index()].expect_const();
+ let mut instantiate = BoundVarReplacer::new(interner, FnMutDelegate { types, regions, consts });
+ b.skip_binder().fold_with(&mut instantiate)
+}
+
+pub(crate) fn mini_canonicalize<'db, T: TypeFoldable<DbInterner<'db>>>(
+ mut context: SolverContext<'db>,
+ val: T,
+) -> Canonical<DbInterner<'db>, T> {
+ let mut canon = MiniCanonicalizer {
+ context: &mut context,
+ db: DebruijnIndex::ZERO,
+ vars: IndexMap::default(),
+ };
+ let canon_val = val.fold_with(&mut canon);
+ let vars = canon.vars;
+ Canonical {
+ value: canon_val,
+ max_universe: UniverseIndex::from_u32(1),
+ variables: CanonicalVars::new_from_iter(
+ context.cx(),
+ vars.iter().map(|(k, v)| match (*k).kind() {
+ GenericArgKind::Type(ty) => match ty.kind() {
+ TyKind::Int(..) | TyKind::Uint(..) => {
+ rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int)
+ }
+ TyKind::Float(..) => rustc_type_ir::CanonicalVarKind::Ty(
+ rustc_type_ir::CanonicalTyVarKind::Float,
+ ),
+ _ => rustc_type_ir::CanonicalVarKind::Ty(
+ rustc_type_ir::CanonicalTyVarKind::General(UniverseIndex::ZERO),
+ ),
+ },
+ GenericArgKind::Lifetime(_) => {
+ rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ZERO)
+ }
+ GenericArgKind::Const(_) => {
+ rustc_type_ir::CanonicalVarKind::Const(UniverseIndex::ZERO)
+ }
+ }),
+ ),
+ }
+}
+
+struct MiniCanonicalizer<'a, 'db> {
+ context: &'a mut SolverContext<'db>,
+ db: DebruijnIndex,
+ vars: IndexMap<GenericArg<'db>, usize>,
+}
+
+impl<'db> TypeFolder<DbInterner<'db>> for MiniCanonicalizer<'_, 'db> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.context.cx()
+ }
+
+ fn fold_binder<T: TypeFoldable<DbInterner<'db>>>(
+ &mut self,
+ t: rustc_type_ir::Binder<DbInterner<'db>, T>,
+ ) -> rustc_type_ir::Binder<DbInterner<'db>, T> {
+ self.db.shift_in(1);
+ let res = t.map_bound(|t| t.fold_with(self));
+ self.db.shift_out(1);
+ res
+ }
+
+ fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
+ match t.kind() {
+ rustc_type_ir::TyKind::Bound(db, _) => {
+ if db >= self.db {
+ panic!("Unexpected bound var");
+ }
+ t
+ }
+ rustc_type_ir::TyKind::Infer(infer) => {
+ let t = match infer {
+ rustc_type_ir::InferTy::TyVar(vid) => {
+ self.context.opportunistic_resolve_ty_var(vid)
+ }
+ rustc_type_ir::InferTy::IntVar(vid) => {
+ self.context.opportunistic_resolve_int_var(vid)
+ }
+ rustc_type_ir::InferTy::FloatVar(vid) => {
+ self.context.opportunistic_resolve_float_var(vid)
+ }
+ _ => t,
+ };
+ let len = self.vars.len();
+ let var = *self.vars.entry(t.into()).or_insert(len);
+ Ty::new(
+ self.cx(),
+ TyKind::Bound(
+ self.db,
+ BoundTy { kind: super::BoundTyKind::Anon, var: BoundVar::from_usize(var) },
+ ),
+ )
+ }
+ _ => t.super_fold_with(self),
+ }
+ }
+
+ fn fold_region(
+ &mut self,
+ r: <DbInterner<'db> as rustc_type_ir::Interner>::Region,
+ ) -> <DbInterner<'db> as rustc_type_ir::Interner>::Region {
+ match r.kind() {
+ RegionKind::ReBound(db, _) => {
+ if db >= self.db {
+ panic!("Unexpected bound var");
+ }
+ r
+ }
+ RegionKind::ReVar(vid) => {
+ let len = self.vars.len();
+ let var = *self.vars.entry(r.into()).or_insert(len);
+ Region::new(
+ self.cx(),
+ RegionKind::ReBound(
+ self.db,
+ BoundRegion {
+ kind: super::BoundRegionKind::Anon,
+ var: BoundVar::from_usize(var),
+ },
+ ),
+ )
+ }
+ _ => r,
+ }
+ }
+
+ fn fold_const(
+ &mut self,
+ c: <DbInterner<'db> as rustc_type_ir::Interner>::Const,
+ ) -> <DbInterner<'db> as rustc_type_ir::Interner>::Const {
+ match c.kind() {
+ ConstKind::Bound(db, _) => {
+ if db >= self.db {
+ panic!("Unexpected bound var");
+ }
+ c
+ }
+ ConstKind::Infer(infer) => {
+ let len = self.vars.len();
+ let var = *self.vars.entry(c.into()).or_insert(len);
+ Const::new(self.cx(), ConstKind::Bound(self.db, BoundVar::from_usize(var)))
+ }
+ _ => c.super_fold_with(self),
+ }
+ }
+}
+
+pub fn explicit_item_bounds<'db>(
+ interner: DbInterner<'db>,
+ def_id: SolverDefId,
+) -> EarlyBinder<'db, Clauses<'db>> {
+ let db = interner.db();
+ match def_id {
+ SolverDefId::TypeAliasId(type_alias) => {
+ let trait_ = match type_alias.lookup(db).container {
+ ItemContainerId::TraitId(t) => t,
+ _ => panic!("associated type not in trait"),
+ };
+
+ // Lower bounds -- we could/should maybe move this to a separate query in `lower`
+ let type_alias_data = db.type_alias_signature(type_alias);
+ let generic_params = generics(db, type_alias.into());
+ let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db);
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ &type_alias_data.store,
+ type_alias.into(),
+ LifetimeElisionKind::AnonymousReportError,
+ );
+
+ let trait_args = GenericArgs::identity_for_item(interner, trait_.into());
+ let item_args = GenericArgs::identity_for_item(interner, def_id);
+ let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args);
+
+ let mut bounds = Vec::new();
+ for bound in &type_alias_data.bounds {
+ ctx.lower_type_bound(bound, interner_ty, false).for_each(|pred| {
+ bounds.push(pred);
+ });
+ }
+
+ if !ctx.unsized_types.contains(&interner_ty) {
+ let sized_trait = LangItem::Sized
+ .resolve_trait(ctx.db, interner.krate.expect("Must have interner.krate"));
+ let sized_bound = sized_trait.map(|trait_id| {
+ let trait_ref = TraitRef::new_from_args(
+ interner,
+ trait_id.into(),
+ GenericArgs::new_from_iter(interner, [interner_ty.into()]),
+ );
+ Clause(Predicate::new(
+ interner,
+ Binder::dummy(rustc_type_ir::PredicateKind::Clause(
+ rustc_type_ir::ClauseKind::Trait(TraitPredicate {
+ trait_ref,
+ polarity: rustc_type_ir::PredicatePolarity::Positive,
+ }),
+ )),
+ ))
+ });
+ bounds.extend(sized_bound);
+ bounds.shrink_to_fit();
+ }
+
+ rustc_type_ir::EarlyBinder::bind(Clauses::new_from_iter(interner, bounds))
+ }
+ SolverDefId::InternedOpaqueTyId(id) => {
+ let full_id = db.lookup_intern_impl_trait_id(id);
+ match full_id {
+ crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
+ let datas = db
+ .return_type_impl_traits_ns(func)
+ .expect("impl trait id without impl traits");
+ let datas = (*datas).as_ref().skip_binder();
+ let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())];
+ EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone()))
+ }
+ crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => {
+ let datas = db
+ .type_alias_impl_traits_ns(alias)
+ .expect("impl trait id without impl traits");
+ let datas = (*datas).as_ref().skip_binder();
+ let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())];
+ EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone()))
+ }
+ crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
+ if let Some((future_trait, future_output)) = LangItem::Future
+ .resolve_trait(db, interner.krate.expect("Must have interner.krate"))
+ .and_then(|trait_| {
+ let alias = trait_.trait_items(db).associated_type_by_name(
+ &hir_expand::name::Name::new_symbol_root(sym::Output.clone()),
+ )?;
+ Some((trait_, alias))
+ })
+ {
+ let args = GenericArgs::identity_for_item(interner, def_id);
+ let out = args.as_slice()[0];
+ let mut predicates = vec![];
+
+ let item_ty = Ty::new_alias(
+ interner,
+ rustc_type_ir::AliasTyKind::Opaque,
+ AliasTy::new_from_args(interner, def_id, args),
+ );
+
+ let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
+ polarity: rustc_type_ir::PredicatePolarity::Positive,
+ trait_ref: TraitRef::new_from_args(
+ interner,
+ future_trait.into(),
+ GenericArgs::new_from_iter(interner, [item_ty.into()]),
+ ),
+ }));
+ predicates.push(Clause(Predicate::new(
+ interner,
+ Binder::bind_with_vars(
+ kind,
+ BoundVarKinds::new_from_iter(
+ interner,
+ [BoundVarKind::Ty(BoundTyKind::Anon)],
+ ),
+ ),
+ )));
+ let sized_trait = LangItem::Sized
+ .resolve_trait(db, interner.krate.expect("Must have interner.krate"));
+ if let Some(sized_trait_) = sized_trait {
+ let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
+ polarity: rustc_type_ir::PredicatePolarity::Positive,
+ trait_ref: TraitRef::new_from_args(
+ interner,
+ sized_trait_.into(),
+ GenericArgs::new_from_iter(interner, [item_ty.into()]),
+ ),
+ }));
+ predicates.push(Clause(Predicate::new(
+ interner,
+ Binder::bind_with_vars(
+ kind,
+ BoundVarKinds::new_from_iter(
+ interner,
+ [BoundVarKind::Ty(BoundTyKind::Anon)],
+ ),
+ ),
+ )));
+ }
+ let kind =
+ PredicateKind::Clause(ClauseKind::Projection(ProjectionPredicate {
+ projection_term: AliasTerm::new_from_args(
+ interner,
+ future_output.into(),
+ GenericArgs::new_from_iter(interner, [item_ty.into()]),
+ ),
+ term: match out.kind() {
+ GenericArgKind::Lifetime(lt) => panic!(),
+ GenericArgKind::Type(ty) => Term::Ty(ty),
+ GenericArgKind::Const(const_) => Term::Const(const_),
+ },
+ }));
+ predicates.push(Clause(Predicate::new(
+ interner,
+ Binder::bind_with_vars(
+ kind,
+ BoundVarKinds::new_from_iter(
+ interner,
+ [BoundVarKind::Ty(BoundTyKind::Anon)],
+ ),
+ ),
+ )));
+ EarlyBinder::bind(Clauses::new_from_iter(interner, predicates))
+ } else {
+ // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback.
+ EarlyBinder::bind(Clauses::new_from_iter(interner, []))
+ }
+ }
+ }
+ }
+ _ => panic!("Unexpected GeneridDefId"),
+ }
+}
+
+pub struct ContainsTypeErrors;
+
+impl<'db> TypeVisitor<DbInterner<'db>> for ContainsTypeErrors {
+ type Result = ControlFlow<()>;
+
+ fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
+ match t.kind() {
+ rustc_type_ir::TyKind::Error(_) => ControlFlow::Break(()),
+ _ => t.super_visit_with(self),
+ }
+ }
+}
+
+/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
+pub struct PlaceholderReplacer<'a, 'db> {
+ infcx: &'a InferCtxt<'db>,
+ mapped_regions: FxIndexMap<PlaceholderRegion, BoundRegion>,
+ mapped_types: FxIndexMap<Placeholder<BoundTy>, BoundTy>,
+ mapped_consts: FxIndexMap<PlaceholderConst, BoundVar>,
+ universe_indices: &'a [Option<UniverseIndex>],
+ current_index: DebruijnIndex,
+}
+
+impl<'a, 'db> PlaceholderReplacer<'a, 'db> {
+ pub fn replace_placeholders<T: TypeFoldable<DbInterner<'db>>>(
+ infcx: &'a InferCtxt<'db>,
+ mapped_regions: FxIndexMap<PlaceholderRegion, BoundRegion>,
+ mapped_types: FxIndexMap<Placeholder<BoundTy>, BoundTy>,
+ mapped_consts: FxIndexMap<PlaceholderConst, BoundVar>,
+ universe_indices: &'a [Option<UniverseIndex>],
+ value: T,
+ ) -> T {
+ let mut replacer = PlaceholderReplacer {
+ infcx,
+ mapped_regions,
+ mapped_types,
+ mapped_consts,
+ universe_indices,
+ current_index: INNERMOST,
+ };
+ value.fold_with(&mut replacer)
+ }
+}
+
+impl<'db> TypeFolder<DbInterner<'db>> for PlaceholderReplacer<'_, 'db> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.infcx.interner
+ }
+
+ fn fold_binder<T: TypeFoldable<DbInterner<'db>>>(
+ &mut self,
+ t: Binder<'db, T>,
+ ) -> Binder<'db, T> {
+ if !t.has_placeholders() && !t.has_infer() {
+ return t;
+ }
+ self.current_index.shift_in(1);
+ let t = t.super_fold_with(self);
+ self.current_index.shift_out(1);
+ t
+ }
+
+ fn fold_region(&mut self, r0: Region<'db>) -> Region<'db> {
+ let r1 = match r0.kind() {
+ RegionKind::ReVar(vid) => self
+ .infcx
+ .inner
+ .borrow_mut()
+ .unwrap_region_constraints()
+ .opportunistic_resolve_var(self.infcx.interner, vid),
+ _ => r0,
+ };
+
+ let r2 = match r1.kind() {
+ RegionKind::RePlaceholder(p) => {
+ let replace_var = self.mapped_regions.get(&p);
+ match replace_var {
+ Some(replace_var) => {
+ let index = self
+ .universe_indices
+ .iter()
+ .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+ .unwrap_or_else(|| panic!("Unexpected placeholder universe."));
+ let db = DebruijnIndex::from_usize(
+ self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+ );
+ Region::new_bound(self.cx(), db, *replace_var)
+ }
+ None => r1,
+ }
+ }
+ _ => r1,
+ };
+
+ tracing::debug!(?r0, ?r1, ?r2, "fold_region");
+
+ r2
+ }
+
+ fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
+ let ty = self.infcx.shallow_resolve(ty);
+ match ty.kind() {
+ TyKind::Placeholder(p) => {
+ let replace_var = self.mapped_types.get(&p);
+ match replace_var {
+ Some(replace_var) => {
+ let index = self
+ .universe_indices
+ .iter()
+ .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+ .unwrap_or_else(|| panic!("Unexpected placeholder universe."));
+ let db = DebruijnIndex::from_usize(
+ self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+ );
+ Ty::new_bound(self.infcx.interner, db, *replace_var)
+ }
+ None => {
+ if ty.has_infer() {
+ ty.super_fold_with(self)
+ } else {
+ ty
+ }
+ }
+ }
+ }
+
+ _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self),
+ _ => ty,
+ }
+ }
+
+ fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
+ let ct = self.infcx.shallow_resolve_const(ct);
+ if let ConstKind::Placeholder(p) = ct.kind() {
+ let replace_var = self.mapped_consts.get(&p);
+ match replace_var {
+ Some(replace_var) => {
+ let index = self
+ .universe_indices
+ .iter()
+ .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+ .unwrap_or_else(|| panic!("Unexpected placeholder universe."));
+ let db = DebruijnIndex::from_usize(
+ self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+ );
+ Const::new_bound(self.infcx.interner, db, *replace_var)
+ }
+ None => {
+ if ct.has_infer() {
+ ct.super_fold_with(self)
+ } else {
+ ct
+ }
+ }
+ }
+ } else {
+ ct.super_fold_with(self)
+ }
+ }
+}
+
+pub(crate) fn needs_normalization<'db, T: TypeVisitable<DbInterner<'db>>>(
+ infcx: &InferCtxt<'db>,
+ value: &T,
+) -> bool {
+ let mut flags = TypeFlags::HAS_ALIAS;
+
+ // Opaques are treated as rigid outside of `TypingMode::PostAnalysis`,
+ // so we can ignore those.
+ match infcx.typing_mode() {
+ // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis
+ TypingMode::Coherence
+ | TypingMode::Analysis { .. }
+ | TypingMode::Borrowck { .. }
+ | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(TypeFlags::HAS_TY_OPAQUE),
+ TypingMode::PostAnalysis => {}
+ }
+
+ value.has_type_flags(flags)
+}
+
+pub fn sizedness_fast_path<'db>(
+ tcx: DbInterner<'db>,
+ predicate: Predicate<'db>,
+ param_env: ParamEnv<'db>,
+) -> bool {
+ // Proving `Sized`/`MetaSized`, very often on "obviously sized" types like
+ // `&T`, accounts for about 60% percentage of the predicates we have to prove. No need to
+ // canonicalize and all that for such cases.
+ if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = predicate.kind().skip_binder()
+ && trait_pred.polarity == PredicatePolarity::Positive
+ {
+ let sizedness = match tcx.as_lang_item(trait_pred.def_id()) {
+ Some(TraitSolverLangItem::Sized) => SizedTraitKind::Sized,
+ Some(TraitSolverLangItem::MetaSized) => SizedTraitKind::MetaSized,
+ _ => return false,
+ };
+
+ // FIXME(sized_hierarchy): this temporarily reverts the `sized_hierarchy` feature
+ // while a proper fix for `tests/ui/sized-hierarchy/incomplete-inference-issue-143992.rs`
+ // is pending a proper fix
+ if matches!(sizedness, SizedTraitKind::MetaSized) {
+ return true;
+ }
+
+ if trait_pred.self_ty().has_trivial_sizedness(tcx, sizedness) {
+ tracing::debug!("fast path -- trivial sizedness");
+ return true;
+ }
+
+ if matches!(trait_pred.self_ty().kind(), TyKind::Param(_) | TyKind::Placeholder(_)) {
+ for clause in param_env.caller_bounds().iter() {
+ if let ClauseKind::Trait(clause_pred) = clause.kind().skip_binder()
+ && clause_pred.polarity == PredicatePolarity::Positive
+ && clause_pred.self_ty() == trait_pred.self_ty()
+ && (clause_pred.def_id() == trait_pred.def_id()
+ || (sizedness == SizedTraitKind::MetaSized
+ && tcx.is_lang_item(clause_pred.def_id(), TraitSolverLangItem::Sized)))
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ false
+}
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index 9605a0b..fc31973 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -12,9 +12,6 @@
mod traits;
mod type_alias_impl_traits;
-use std::env;
-use std::sync::LazyLock;
-
use base_db::{Crate, SourceDatabase};
use expect_test::Expect;
use hir_def::{
@@ -35,8 +32,6 @@
ast::{self, AstNode, HasName},
};
use test_fixture::WithFixture;
-use tracing_subscriber::{Registry, layer::SubscriberExt};
-use tracing_tree::HierarchicalLayer;
use triomphe::Arc;
use crate::{
@@ -44,6 +39,7 @@
db::HirDatabase,
display::{DisplayTarget, HirDisplay},
infer::{Adjustment, TypeMismatch},
+ setup_tracing,
test_db::TestDB,
};
@@ -51,23 +47,6 @@
// against snapshots of the expected results using expect. Use
// `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots.
-fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
- static ENABLE: LazyLock<bool> = LazyLock::new(|| env::var("CHALK_DEBUG").is_ok());
- if !*ENABLE {
- return None;
- }
-
- let filter: tracing_subscriber::filter::Targets =
- env::var("CHALK_DEBUG").ok().and_then(|it| it.parse().ok()).unwrap_or_default();
- let layer = HierarchicalLayer::default()
- .with_indent_lines(true)
- .with_ansi(false)
- .with_indent_amount(2)
- .with_writer(std::io::stderr);
- let subscriber = Registry::default().with(filter).with(layer);
- Some(tracing::subscriber::set_default(subscriber))
-}
-
#[track_caller]
fn check_types(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_impl(ra_fixture, false, true, false)
diff --git a/crates/hir-ty/src/tests/closure_captures.rs b/crates/hir-ty/src/tests/closure_captures.rs
index dbc68ee..5893894 100644
--- a/crates/hir-ty/src/tests/closure_captures.rs
+++ b/crates/hir-ty/src/tests/closure_captures.rs
@@ -12,9 +12,10 @@
use crate::mir::MirSpan;
use crate::test_db::TestDB;
-use super::visit_module;
+use super::{setup_tracing, visit_module};
fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
+ let _tracing = setup_tracing();
let (db, file_id) = TestDB::with_single_file(ra_fixture);
let module = db.module_for_file(file_id.file_id(&db));
let def_map = module.def_map(&db);
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index 3894b4b..3b7d4d2 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -96,7 +96,7 @@
fn test() {
let x = if true {
foo(&[1])
- // ^^^^ adjustments: Deref(None), Borrow(Ref('?8, Not)), Pointer(Unsize)
+ // ^^^^ adjustments: Deref(None), Borrow(Ref('?11, Not)), Pointer(Unsize)
} else {
&[1]
};
@@ -148,7 +148,7 @@
fn test(i: i32) {
let x = match i {
2 => foo(&[2]),
- // ^^^^ adjustments: Deref(None), Borrow(Ref('?8, Not)), Pointer(Unsize)
+ // ^^^^ adjustments: Deref(None), Borrow(Ref('?11, Not)), Pointer(Unsize)
1 => &[1],
_ => &[3],
};
@@ -484,6 +484,8 @@
);
}
+// FIXME(next-solver): We could learn more from the `&S` -> `&dyn Foo<i8, _>` coercion if we followed the rustc model
+// where unsized is successful if all unsizing trait goals are certain (and non-unsizing goals are delayed).
#[test]
fn coerce_unsize_trait_object_simple() {
check_types(
@@ -503,8 +505,8 @@
//^ S<i8, i16>
let obj: &dyn Bar<_, i8, i16> = &S;
//^ S<i8, i16>
- let obj: &dyn Foo<i8, _> = &S;
- //^ S<i8, {unknown}>
+ //let obj: &dyn Foo<i8, _> = &S;
+ // S<{unknown}, {unknown}>
}"#,
);
}
@@ -543,9 +545,9 @@
fn test() {
let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] };
- //^^^^^^^^^^^^^^^^^^^^^ expected &'? Foo<[usize]>, got &'? Foo<[i32; 3]>
+ //^^^^^^^^^^^^^^^^^^^^^ type: &'? Foo<[usize; 3]>
let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] });
- //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &'? Bar<[usize]>, got &'? Bar<[i32; 3]>
+ //^^^^^^^^^^^^^^^^^^^^^^^^^^ type: &'? Bar<[usize; 3]>
}
"#,
);
@@ -899,7 +901,7 @@
fn index(&self, index: usize) -> &Self::Output { &() }
}
-impl core::ops::IndexMut for StructMut {
+impl core::ops::IndexMut<usize> for StructMut {
fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut () }
}
fn test() {
diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs
index 3159499..df9061d 100644
--- a/crates/hir-ty/src/tests/incremental.rs
+++ b/crates/hir-ty/src/tests/incremental.rs
@@ -519,6 +519,7 @@
);
}
+// FIXME(next-solver): does this test make sense with fast path?
#[test]
fn add_struct_invalidates_trait_solve() {
let (mut db, file_id) = TestDB::with_single_file(
@@ -559,7 +560,7 @@
let _inference_result = db.infer(def);
}
},
- &[("trait_solve_shim", 2)],
+ &[("trait_solve_shim", 0)],
expect_test::expect![[r#"
[
"source_root_crates_shim",
@@ -606,21 +607,17 @@
"callable_item_signature_shim",
"adt_variance_shim",
"variances_of_shim",
- "trait_solve_shim",
- "trait_datum_shim",
- "generic_predicates_shim",
- "adt_datum_shim",
"trait_impls_in_deps_shim",
"trait_impls_in_crate_shim",
"impl_trait_with_diagnostics_shim",
"impl_self_ty_with_diagnostics_shim",
"type_for_adt_tracked",
- "impl_datum_shim",
- "generic_predicates_shim",
- "program_clauses_for_chalk_env_shim",
+ "impl_trait_with_diagnostics_ns_shim",
+ "impl_self_ty_with_diagnostics_ns_shim",
+ "generic_predicates_ns_shim",
+ "generic_predicates_ns_shim",
"value_ty_shim",
"generic_predicates_shim",
- "trait_solve_shim",
"lang_item",
]
"#]],
@@ -703,10 +700,13 @@
"impl_signature_with_source_map_shim",
"impl_signature_shim",
"callable_item_signature_shim",
- "generic_predicates_shim",
"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_ns_shim",
"generic_predicates_shim",
]
"#]],
diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs
index ea7a113..5d088e4 100644
--- a/crates/hir-ty/src/tests/macros.rs
+++ b/crates/hir-ty/src/tests/macros.rs
@@ -197,17 +197,17 @@
39..442 '{ ...!(); }': ()
73..94 'spam!(...am!())': {unknown}
100..119 'for _ ...!() {}': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter
- 100..119 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+ 100..119 'for _ ...!() {}': {unknown}
100..119 'for _ ...!() {}': !
- 100..119 'for _ ...!() {}': IntoIterator::IntoIter<isize>
- 100..119 'for _ ...!() {}': &'? mut IntoIterator::IntoIter<isize>
- 100..119 'for _ ...!() {}': fn next<IntoIterator::IntoIter<isize>>(&'? mut IntoIterator::IntoIter<isize>) -> Option<<IntoIterator::IntoIter<isize> as Iterator>::Item>
- 100..119 'for _ ...!() {}': Option<IntoIterator::Item<isize>>
+ 100..119 'for _ ...!() {}': {unknown}
+ 100..119 'for _ ...!() {}': &'? mut {unknown}
+ 100..119 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 100..119 'for _ ...!() {}': Option<{unknown}>
100..119 'for _ ...!() {}': ()
100..119 'for _ ...!() {}': ()
100..119 'for _ ...!() {}': ()
100..119 'for _ ...!() {}': ()
- 104..105 '_': IntoIterator::Item<isize>
+ 104..105 '_': {unknown}
117..119 '{}': ()
124..134 '|| spam!()': impl Fn() -> isize
140..156 'while ...!() {}': !
@@ -291,17 +291,17 @@
53..456 '{ ...!(); }': ()
87..108 'spam!(...am!())': {unknown}
114..133 'for _ ...!() {}': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter
- 114..133 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+ 114..133 'for _ ...!() {}': {unknown}
114..133 'for _ ...!() {}': !
- 114..133 'for _ ...!() {}': IntoIterator::IntoIter<isize>
- 114..133 'for _ ...!() {}': &'? mut IntoIterator::IntoIter<isize>
- 114..133 'for _ ...!() {}': fn next<IntoIterator::IntoIter<isize>>(&'? mut IntoIterator::IntoIter<isize>) -> Option<<IntoIterator::IntoIter<isize> as Iterator>::Item>
- 114..133 'for _ ...!() {}': Option<IntoIterator::Item<isize>>
+ 114..133 'for _ ...!() {}': {unknown}
+ 114..133 'for _ ...!() {}': &'? mut {unknown}
+ 114..133 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 114..133 'for _ ...!() {}': Option<{unknown}>
114..133 'for _ ...!() {}': ()
114..133 'for _ ...!() {}': ()
114..133 'for _ ...!() {}': ()
114..133 'for _ ...!() {}': ()
- 118..119 '_': IntoIterator::Item<isize>
+ 118..119 '_': {unknown}
131..133 '{}': ()
138..148 '|| spam!()': impl Fn() -> isize
154..170 'while ...!() {}': !
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index c58ca6c..f09b3ef 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1134,6 +1134,7 @@
fn dyn_trait_super_trait_not_in_scope() {
check_infer(
r#"
+ //- minicore: dispatch_from_dyn
mod m {
pub trait SuperTrait {
fn foo(&self) -> u32 { 0 }
@@ -1309,7 +1310,7 @@
fn dyn_trait_method_priority() {
check_types(
r#"
-//- minicore: from
+//- minicore: from, dispatch_from_dyn
trait Trait {
fn into(&self) -> usize { 0 }
}
@@ -1824,6 +1825,33 @@
}
#[test]
+fn deref_fun_3() {
+ check_types(
+ r#"
+//- minicore: receiver
+
+struct A<T, U>(T, U);
+struct B<T>(T);
+struct C<T>(T);
+
+impl<T> core::ops::Deref for A<B<T>, u32> {
+ type Target = B<T>;
+ fn deref(&self) -> &B<T> { &self.0 }
+}
+
+fn make<T>() -> T { loop {} }
+
+fn test() {
+ let a1 = A(make(), make());
+ let _: usize = (*a1).0;
+ a1;
+ //^^ A<B<usize>, u32>
+}
+"#,
+ );
+}
+
+#[test]
fn deref_into_inference_var() {
check_types(
r#"
@@ -2077,7 +2105,7 @@
}
fn test() {
Box::new(Foo).foo();
- //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?3, Not))
+ //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?5, Not))
}
"#,
);
@@ -2095,7 +2123,7 @@
use core::mem::ManuallyDrop;
fn test() {
ManuallyDrop::new(Foo).foo();
- //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?4, Not))
+ //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?6, Not))
}
"#,
);
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index c4c17a9..25061e1 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -773,7 +773,7 @@
"#,
expect![[r#"
379..383 'self': &'? mut PeerSet<D>
- 401..424 '{ ... }': dyn Future<Output = ()> + '?
+ 401..424 '{ ... }': dyn Future<Output = ()> + 'static
411..418 'loop {}': !
416..418 '{}': ()
575..579 'self': &'? mut Self
@@ -781,6 +781,9 @@
);
}
+// FIXME(next-solver): Though `Repeat: IntoIterator` does not hold here, we
+// should be able to do better at given type hints (with Chalk, we did `IntoIterator::Item<Repeat<...>>`)
+// From what I can tell, the point of this test is to not panic though.
#[test]
fn issue_4966() {
check_infer(
@@ -824,11 +827,11 @@
311..317 'repeat': Repeat<Map<impl Fn(&'? f64) -> f64>>
320..345 'Repeat...nner }': Repeat<Map<impl Fn(&'? f64) -> f64>>
338..343 'inner': Map<impl Fn(&'? f64) -> f64>
- 356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&'? f64) -> f64>>>>
- 362..371 'from_iter': fn from_iter<IntoIterator::Item<Repeat<Map<impl Fn(&'? f64) -> f64>>>, Repeat<Map<impl Fn(&'? f64) -> f64>>>(Repeat<Map<impl Fn(&'? f64) -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<impl Fn(&'? f64) -> f64>>>>
- 362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&'? f64) -> f64>>>>
+ 356..359 'vec': Vec<{unknown}>
+ 362..371 'from_iter': fn from_iter<{unknown}, Repeat<Map<impl Fn(&'? f64) -> f64>>>(Repeat<Map<impl Fn(&'? f64) -> f64>>) -> Vec<{unknown}>
+ 362..379 'from_i...epeat)': Vec<{unknown}>
372..378 'repeat': Repeat<Map<impl Fn(&'? f64) -> f64>>
- 386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&'? f64) -> f64>>>>
+ 386..389 'vec': Vec<{unknown}>
386..399 'vec.foo_bar()': {unknown}
"#]],
);
@@ -1224,6 +1227,8 @@
#[test]
fn for_loop_block_expr_iterable() {
+ // FIXME(next-solver): it would be nice to be able to hint `IntoIterator::IntoIter<()>` instead of just `{unknown}`
+ // (even though `(): IntoIterator` does not hold)
check_infer(
r#"
//- minicore: iterator
@@ -1236,17 +1241,17 @@
expect![[r#"
10..68 '{ ... } }': ()
16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter
- 16..66 'for _ ... }': IntoIterator::IntoIter<()>
+ 16..66 'for _ ... }': {unknown}
16..66 'for _ ... }': !
- 16..66 'for _ ... }': IntoIterator::IntoIter<()>
- 16..66 'for _ ... }': &'? mut IntoIterator::IntoIter<()>
- 16..66 'for _ ... }': fn next<IntoIterator::IntoIter<()>>(&'? mut IntoIterator::IntoIter<()>) -> Option<<IntoIterator::IntoIter<()> as Iterator>::Item>
- 16..66 'for _ ... }': Option<IntoIterator::Item<()>>
+ 16..66 'for _ ... }': {unknown}
+ 16..66 'for _ ... }': &'? mut {unknown}
+ 16..66 'for _ ... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 16..66 'for _ ... }': Option<{unknown}>
16..66 'for _ ... }': ()
16..66 'for _ ... }': ()
16..66 'for _ ... }': ()
16..66 'for _ ... }': ()
- 20..21 '_': IntoIterator::Item<()>
+ 20..21 '_': {unknown}
25..39 '{ let x = 0; }': ()
31..32 'x': i32
35..36 '0': i32
@@ -1283,7 +1288,6 @@
#[test]
fn bug_11242() {
- // FIXME: wrong, should be u32
check_types(
r#"
fn foo<A, B>()
@@ -1292,7 +1296,7 @@
B: IntoIterator<Item = usize>,
{
let _x: <A as IntoIterator>::Item;
- // ^^ {unknown}
+ // ^^ u32
}
pub trait Iterator {
@@ -1495,7 +1499,7 @@
fn regression_11688_3() {
check_types(
r#"
- //- minicore: iterator
+ //- minicore: iterator, dispatch_from_dyn
struct Ar<T, const N: u8>(T);
fn f<const LEN: usize, T, const BASE: u8>(
num_zeros: usize,
@@ -1514,6 +1518,7 @@
fn regression_11688_4() {
check_types(
r#"
+ //- minicore: dispatch_from_dyn
trait Bar<const C: usize> {
fn baz(&self) -> [i32; C];
}
@@ -2035,11 +2040,11 @@
r#"
fn test() {
let x = S::foo::<'static, &()>(&S);
- // ^ Wrap<'?, ()>
+ // ^ Wrap<'static, ()>
let x = S::foo::<&()>(&S);
// ^ Wrap<'?, ()>
let x = S.foo::<'static, &()>();
- // ^ Wrap<'?, ()>
+ // ^ Wrap<'static, ()>
let x = S.foo::<&()>();
// ^ Wrap<'?, ()>
}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index b154e59..a995a45 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -2923,7 +2923,7 @@
// ^^ impl Fn()
let c4 = f1();
- // ^^ impl FnOnce() + ?Sized
+ // ^^ impl FnOnce()
f2(|| { 0 });
// ^^^^^^^^ impl FnOnce() -> i32
@@ -3922,7 +3922,7 @@
expect![[r#"
110..127 '{ ...z(); }': ()
116..122 'T::baz': fn baz<T>() -> <{unknown} as Foo>::Gat<'?>
- 116..124 'T::baz()': Foo::Gat<'?, {unknown}>
+ 116..124 'T::baz()': {unknown}
"#]],
);
}
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 56e31a1..2e4346a 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -1691,7 +1691,7 @@
get2(y);
get(set(S));
get2(set(S));
- get2(S::<str>);
+ get2(S::<usize>);
}"#,
expect![[r#"
49..50 't': T
@@ -1703,7 +1703,7 @@
166..167 't': T
256..257 'x': T
262..263 'y': impl Trait<Type = i64>
- 289..397 '{ ...r>); }': ()
+ 289..399 '{ ...e>); }': ()
295..298 'get': fn get<T>(T) -> <T as Trait>::Type
295..301 'get(x)': u32
299..300 'x': T
@@ -1726,9 +1726,9 @@
367..370 'set': fn set<S<u64>>(S<u64>) -> S<u64>
367..373 'set(S)': S<u64>
371..372 'S': S<u64>
- 380..384 'get2': fn get2<str, S<str>>(S<str>) -> str
- 380..394 'get2(S::<str>)': str
- 385..393 'S::<str>': S<str>
+ 380..384 'get2': fn get2<usize, S<usize>>(S<usize>) -> usize
+ 380..396 'get2(S...size>)': usize
+ 385..395 'S::<usize>': S<usize>
"#]],
);
}
@@ -2740,7 +2740,7 @@
fn dyn_trait_through_chalk() {
check_types(
r#"
-//- minicore: deref
+//- minicore: deref, unsize, dispatch_from_dyn
struct Box<T: ?Sized> {}
impl<T: ?Sized> core::ops::Deref for Box<T> {
type Target = T;
@@ -3228,7 +3228,7 @@
fn infer_dyn_fn_output() {
check_types(
r#"
-//- minicore: fn
+//- minicore: fn, dispatch_from_dyn
fn foo() {
let f: &dyn Fn() -> i32;
f();
@@ -4255,8 +4255,8 @@
127..128 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + '?)
164..195 '{ ...f(); }': ()
170..171 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + '?)
- 170..184 'v.get::<i32>()': &'? i32
- 170..192 'v.get:...eref()': &'? i32
+ 170..184 'v.get::<i32>()': {unknown}
+ 170..192 'v.get:...eref()': &'? {unknown}
"#]],
);
}
@@ -4785,30 +4785,30 @@
fn allowed3(baz: impl Baz<Assoc = Qux<impl Foo>>) {}
"#,
expect![[r#"
- 139..140 'f': impl Fn({unknown}) + ?Sized
+ 139..140 'f': impl Fn({unknown})
161..193 '{ ...oo); }': ()
171..174 'foo': S
177..178 'S': S
- 184..185 'f': impl Fn({unknown}) + ?Sized
+ 184..185 'f': impl Fn({unknown})
184..190 'f(foo)': ()
186..189 'foo': S
- 251..252 'f': impl Fn(&'? {unknown}) + ?Sized
+ 251..252 'f': impl Fn(&'? {unknown})
274..307 '{ ...oo); }': ()
284..287 'foo': S
290..291 'S': S
- 297..298 'f': impl Fn(&'? {unknown}) + ?Sized
+ 297..298 'f': impl Fn(&'? {unknown})
297..304 'f(&foo)': ()
299..303 '&foo': &'? S
300..303 'foo': S
- 325..328 'bar': impl Bar<{unknown}> + ?Sized
+ 325..328 'bar': impl Bar<{unknown}>
350..352 '{}': ()
- 405..408 'bar': impl Bar<&'? {unknown}> + ?Sized
+ 405..408 'bar': impl Bar<&'? {unknown}>
431..433 '{}': ()
- 447..450 'baz': impl Baz<Assoc = impl Foo + ?Sized> + ?Sized
+ 447..450 'baz': impl Baz<Assoc = impl Foo>
480..482 '{}': ()
- 500..503 'baz': impl Baz<Assoc = &'a impl Foo + 'a + ?Sized> + ?Sized
+ 500..503 'baz': impl Baz<Assoc = &'a impl Foo + 'a>
544..546 '{}': ()
- 560..563 'baz': impl Baz<Assoc = Qux<impl Foo + ?Sized>> + ?Sized
+ 560..563 'baz': impl Baz<Assoc = Qux<impl Foo>>
598..600 '{}': ()
"#]],
)
@@ -4930,3 +4930,67 @@
"#]],
);
}
+
+#[test]
+fn new_solver_crash_1() {
+ check_infer(
+ r#"
+pub trait Deserializer<'de> {
+ type Error;
+}
+
+fn deserialize_abs_pathbuf<'de, D>(de: D) -> D::Error
+where
+ D: Deserializer<'de>,
+{
+}
+"#,
+ expect![[r#"
+ 84..86 'de': D
+ 135..138 '{ }': Deserializer::Error<'de, D>
+ "#]],
+ );
+}
+
+#[test]
+fn new_solver_crash_2() {
+ check_infer(
+ r#"
+//- minicore: deref, send, sync
+use core::ops::Deref;
+
+trait Error {}
+
+struct AnyhowError;
+
+impl Deref for AnyhowError {
+ type Target = dyn Error + Send + Sync;
+
+ fn deref(&self) -> &Self::Target { loop {} }
+}
+
+impl AnyhowError {
+ fn downcast<T>(self) {}
+}
+
+
+fn main() {
+ let e = AnyhowError;
+ e.downcast::<()>();
+}
+"#,
+ expect![[r#"
+ 147..151 'self': &'? AnyhowError
+ 170..181 '{ loop {} }': &'? (dyn Error + Send + Sync + 'static)
+ 172..179 'loop {}': !
+ 177..179 '{}': ()
+ 223..227 'self': AnyhowError
+ 229..231 '{}': ()
+ 246..298 '{ ...>(); }': ()
+ 256..257 'e': AnyhowError
+ 260..271 'AnyhowError': AnyhowError
+ 277..278 'e': AnyhowError
+ 277..295 'e.down...<()>()': ()
+ "#]],
+ );
+}
diff --git a/crates/hir-ty/src/tls.rs b/crates/hir-ty/src/tls.rs
index f53409a..fe4cf7a 100644
--- a/crates/hir-ty/src/tls.rs
+++ b/crates/hir-ty/src/tls.rs
@@ -10,6 +10,7 @@
};
use hir_def::{AdtId, ItemContainerId, Lookup, TypeAliasId};
+#[allow(unused)]
pub(crate) use unsafe_tls::{set_current_program, with_current_program};
pub(crate) struct DebugContext<'a>(&'a dyn HirDatabase);
@@ -136,6 +137,7 @@
if PROGRAM.is_set() { PROGRAM.with(|prog| op(Some(prog))) } else { op(None) }
}
+ #[allow(dead_code)]
pub(crate) fn set_current_program<OP, R>(p: &dyn HirDatabase, op: OP) -> R
where
OP: FnOnce() -> R,
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index 08b9d24..51cf5be 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -1,43 +1,38 @@
//! Trait solving using Chalk.
use core::fmt;
-use std::env::var;
use chalk_ir::{DebruijnIndex, GoalData, fold::TypeFoldable};
-use chalk_recursive::Cache;
-use chalk_solve::{Solver, logging_db::LoggingRustIrDatabase, rust_ir};
+use chalk_solve::rust_ir;
use base_db::Crate;
use hir_def::{BlockId, TraitId, lang_item::LangItem};
use hir_expand::name::Name;
use intern::sym;
+use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt};
+use rustc_type_ir::{
+ InferCtxtLike, TypingMode,
+ inherent::{SliceLike, Span as _},
+ solve::Certainty,
+};
use span::Edition;
-use stdx::{never, panic_context};
+use stdx::never;
use triomphe::Arc;
use crate::{
- AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, Interner, ProjectionTy,
- ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, db::HirDatabase,
- infer::unify::InferenceTable, utils::UnevaluatedConstEvaluatorFolder,
+ AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy,
+ ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause,
+ db::HirDatabase,
+ infer::unify::InferenceTable,
+ next_solver::{
+ DbInterner, GenericArg, SolverContext, Span,
+ infer::{DbInternerInferExt, InferCtxt},
+ mapping::{ChalkToNextSolver, convert_canonical_args_for_result},
+ util::mini_canonicalize,
+ },
+ utils::UnevaluatedConstEvaluatorFolder,
};
-/// This controls how much 'time' we give the Chalk solver before giving up.
-const CHALK_SOLVER_FUEL: i32 = 1000;
-
-#[derive(Debug, Copy, Clone)]
-pub(crate) struct ChalkContext<'a> {
- pub(crate) db: &'a dyn HirDatabase,
- pub(crate) krate: Crate,
- pub(crate) block: Option<BlockId>,
-}
-
-fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
- let overflow_depth =
- var("CHALK_OVERFLOW_DEPTH").ok().and_then(|s| s.parse().ok()).unwrap_or(500);
- let max_size = var("CHALK_SOLVER_MAX_SIZE").ok().and_then(|s| s.parse().ok()).unwrap_or(150);
- chalk_recursive::RecursiveSolver::new(overflow_depth, max_size, Some(Cache::new()))
-}
-
/// A set of clauses that we assume to be true. E.g. if we are inside this function:
/// ```rust
/// fn foo<T: Default>(t: T) {}
@@ -103,13 +98,43 @@
table.resolve_completely(ty)
}
+fn identity_subst(
+ binders: chalk_ir::CanonicalVarKinds<Interner>,
+) -> chalk_ir::Canonical<chalk_ir::Substitution<Interner>> {
+ let identity_subst = chalk_ir::Substitution::from_iter(
+ Interner,
+ binders.iter(Interner).enumerate().map(|(index, c)| {
+ let index_db = chalk_ir::BoundVar::new(DebruijnIndex::INNERMOST, index);
+ match &c.kind {
+ chalk_ir::VariableKind::Ty(_) => {
+ chalk_ir::GenericArgData::Ty(TyKind::BoundVar(index_db).intern(Interner))
+ .intern(Interner)
+ }
+ chalk_ir::VariableKind::Lifetime => chalk_ir::GenericArgData::Lifetime(
+ chalk_ir::LifetimeData::BoundVar(index_db).intern(Interner),
+ )
+ .intern(Interner),
+ chalk_ir::VariableKind::Const(ty) => chalk_ir::GenericArgData::Const(
+ chalk_ir::ConstData {
+ ty: ty.clone(),
+ value: chalk_ir::ConstValue::BoundVar(index_db),
+ }
+ .intern(Interner),
+ )
+ .intern(Interner),
+ }
+ }),
+ );
+ chalk_ir::Canonical { binders, value: identity_subst }
+}
+
/// Solve a trait goal using Chalk.
pub(crate) fn trait_solve_query(
db: &dyn HirDatabase,
krate: Crate,
block: Option<BlockId>,
goal: Canonical<InEnvironment<Goal>>,
-) -> Option<Solution> {
+) -> NextTraitSolveResult {
let _p = tracing::info_span!("trait_solve_query", detail = ?match &goal.value.goal.data(Interner) {
GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => db
.trait_signature(it.hir_trait_id())
@@ -128,7 +153,110 @@
&& let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner)
{
// Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
- return Some(Solution::Ambig(Guidance::Unknown));
+ return NextTraitSolveResult::Uncertain(identity_subst(goal.binders.clone()));
+ }
+
+ // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So
+ // we should get rid of it when talking to chalk.
+ let goal = goal
+ .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST)
+ .unwrap();
+
+ // We currently don't deal with universes (I think / hope they're not yet
+ // relevant for our use cases?)
+ next_trait_solve(db, krate, block, goal)
+}
+
+fn solve_nextsolver<'db>(
+ db: &'db dyn HirDatabase,
+ krate: Crate,
+ block: Option<BlockId>,
+ goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>,
+) -> Result<
+ (HasChanged, Certainty, rustc_type_ir::Canonical<DbInterner<'db>, Vec<GenericArg<'db>>>),
+ rustc_type_ir::solve::NoSolution,
+> {
+ // FIXME: should use analysis_in_body, but that needs GenericDefId::Block
+ let context = SolverContext(
+ DbInterner::new_with(db, Some(krate), block)
+ .infer_ctxt()
+ .build(TypingMode::non_body_analysis()),
+ );
+
+ match goal.canonical.value.goal.data(Interner) {
+ // FIXME: args here should be...what? not empty
+ GoalData::All(goals) if goals.is_empty(Interner) => {
+ return Ok((HasChanged::No, Certainty::Yes, mini_canonicalize(context, vec![])));
+ }
+ _ => {}
+ }
+
+ let goal = goal.canonical.to_nextsolver(context.cx());
+ tracing::info!(?goal);
+
+ let (goal, var_values) = context.instantiate_canonical(&goal);
+ tracing::info!(?var_values);
+
+ let res = context.evaluate_root_goal(goal, Span::dummy(), None);
+
+ let vars =
+ var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect();
+ let canonical_var_values = mini_canonicalize(context, vars);
+
+ let res = res.map(|r| (r.has_changed, r.certainty, canonical_var_values));
+
+ tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res);
+
+ res
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum NextTraitSolveResult {
+ Certain(chalk_ir::Canonical<chalk_ir::ConstrainedSubst<Interner>>),
+ Uncertain(chalk_ir::Canonical<chalk_ir::Substitution<Interner>>),
+ NoSolution,
+}
+
+impl NextTraitSolveResult {
+ pub fn no_solution(&self) -> bool {
+ matches!(self, NextTraitSolveResult::NoSolution)
+ }
+
+ pub fn certain(&self) -> bool {
+ matches!(self, NextTraitSolveResult::Certain(..))
+ }
+
+ pub fn uncertain(&self) -> bool {
+ matches!(self, NextTraitSolveResult::Uncertain(..))
+ }
+}
+
+/// Solve a trait goal using Chalk.
+pub fn next_trait_solve(
+ db: &dyn HirDatabase,
+ krate: Crate,
+ block: Option<BlockId>,
+ goal: Canonical<InEnvironment<Goal>>,
+) -> NextTraitSolveResult {
+ let detail = match &goal.value.goal.data(Interner) {
+ GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => {
+ db.trait_signature(it.hir_trait_id()).name.display(db, Edition::LATEST).to_string()
+ }
+ GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_owned(),
+ _ => "??".to_owned(),
+ };
+ let _p = tracing::info_span!("next_trait_solve", ?detail).entered();
+ tracing::info!("next_trait_solve({:?})", goal.value.goal);
+
+ if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq {
+ alias: AliasTy::Projection(projection_ty),
+ ..
+ }))) = &goal.value.goal.data(Interner)
+ && let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner)
+ {
+ // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
+ // FIXME
+ return NextTraitSolveResult::Uncertain(identity_subst(goal.binders.clone()));
}
// Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So
@@ -140,70 +268,44 @@
// We currently don't deal with universes (I think / hope they're not yet
// relevant for our use cases?)
let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 };
- solve(db, krate, block, &u_canonical)
-}
+ tracing::info!(?u_canonical);
-fn solve(
- db: &dyn HirDatabase,
- krate: Crate,
- block: Option<BlockId>,
- goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>,
-) -> Option<chalk_solve::Solution<Interner>> {
- let _p = tracing::info_span!("solve", ?krate, ?block).entered();
- let context = ChalkContext { db, krate, block };
- tracing::debug!("solve goal: {:?}", goal);
- let mut solver = create_chalk_solver();
+ let next_solver_res = solve_nextsolver(db, krate, block, &u_canonical);
- let fuel = std::cell::Cell::new(CHALK_SOLVER_FUEL);
-
- let should_continue = || {
- db.unwind_if_revision_cancelled();
- let remaining = fuel.get();
- fuel.set(remaining - 1);
- if remaining == 0 {
- tracing::debug!("fuel exhausted");
+ match next_solver_res {
+ Err(_) => NextTraitSolveResult::NoSolution,
+ Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain(
+ convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args),
+ ),
+ Ok((_, Certainty::Maybe(_), args)) => {
+ let subst = convert_canonical_args_for_result(
+ DbInterner::new_with(db, Some(krate), block),
+ args,
+ );
+ NextTraitSolveResult::Uncertain(chalk_ir::Canonical {
+ binders: subst.binders,
+ value: subst.value.subst,
+ })
}
- remaining > 0
- };
-
- let mut solve = || {
- let _ctx = if is_chalk_debug() || is_chalk_print() {
- Some(panic_context::enter(format!("solving {goal:?}")))
- } else {
- None
- };
- let solution = if is_chalk_print() {
- let logging_db =
- LoggingRustIrDatabaseLoggingOnDrop(LoggingRustIrDatabase::new(context));
- solver.solve_limited(&logging_db.0, goal, &should_continue)
- } else {
- solver.solve_limited(&context, goal, &should_continue)
- };
-
- tracing::debug!("solve({:?}) => {:?}", goal, solution);
-
- solution
- };
-
- // don't set the TLS for Chalk unless Chalk debugging is active, to make
- // extra sure we only use it for debugging
- if is_chalk_debug() { crate::tls::set_current_program(db, solve) } else { solve() }
-}
-
-struct LoggingRustIrDatabaseLoggingOnDrop<'a>(LoggingRustIrDatabase<Interner, ChalkContext<'a>>);
-
-impl Drop for LoggingRustIrDatabaseLoggingOnDrop<'_> {
- fn drop(&mut self) {
- tracing::info!("chalk program:\n{}", self.0);
}
}
-fn is_chalk_debug() -> bool {
- std::env::var("CHALK_DEBUG").is_ok()
-}
+/// Solve a trait goal using Chalk.
+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>>,
+) -> Result<(HasChanged, Certainty), rustc_type_ir::solve::NoSolution> {
+ tracing::info!(?goal);
-fn is_chalk_print() -> bool {
- std::env::var("CHALK_PRINT").is_ok()
+ let context = <&SolverContext<'db>>::from(infer_ctxt);
+
+ let res = context.evaluate_root_goal(goal, Span::dummy(), None);
+
+ let res = res.map(|r| (r.has_changed, r.certainty));
+
+ tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res);
+
+ res
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index c68ff70..dfa3938 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -22,6 +22,8 @@
triomphe.workspace = true
indexmap.workspace = true
+ra-ap-rustc_type_ir.workspace = true
+
# local deps
base-db.workspace = true
cfg.workspace = true
@@ -36,6 +38,9 @@
[dev-dependencies]
expect-test.workspace = true
+tracing.workspace = true
+tracing-subscriber.workspace = true
+tracing-tree.workspace = true
# local deps
test-utils.workspace = true
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index a323f97..475906a 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -20,6 +20,12 @@
#![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;
mod from_id;
mod has_source;
@@ -54,7 +60,10 @@
},
item_tree::ImportAlias,
layout::{self, ReprOptions, TargetDataLayout},
- nameres::{self, assoc::TraitItems, diagnostics::DefDiagnostic},
+ nameres::{
+ assoc::TraitItems,
+ diagnostics::{DefDiagnostic, DefDiagnosticKind},
+ },
per_ns::PerNs,
resolver::{HasResolver, Resolver},
signatures::{ImplFlags, StaticFlags, TraitFlags, VariantFields},
@@ -76,11 +85,11 @@
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
method_resolution,
mir::{MutBorrowKind, interpret_mir},
+ next_solver::{DbInterner, GenericArgs, SolverDefId, infer::InferCtxt},
primitive::UintTy,
traits::FnTrait,
};
use itertools::Itertools;
-use nameres::diagnostics::DefDiagnosticKind;
use rustc_hash::FxHashSet;
use smallvec::SmallVec;
use span::{AstIdNode, Edition, FileId};
@@ -187,6 +196,10 @@
}
impl Crate {
+ pub fn base(self) -> base_db::Crate {
+ self.id
+ }
+
pub fn origin(self, db: &dyn HirDatabase) -> CrateOrigin {
self.id.data(db).origin.clone()
}
@@ -1247,6 +1260,25 @@
pub(crate) id: LocalFieldId,
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct InstantiatedField<'db> {
+ pub(crate) inner: Field,
+ pub(crate) args: GenericArgs<'db>,
+}
+
+impl<'db> InstantiatedField<'db> {
+ /// Returns the type as in the signature of the struct.
+ pub fn ty(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> {
+ let krate = self.inner.krate(db);
+ let interner = DbInterner::new_with(db, Some(krate.base()), None);
+
+ let var_id = self.inner.parent.into();
+ let field = db.field_types_ns(var_id)[self.inner.id];
+ let ty = field.instantiate(interner, self.args);
+ TypeNs::new(db, var_id, ty)
+ }
+}
+
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub struct TupleField {
pub owner: DefWithBodyId,
@@ -1444,6 +1476,11 @@
pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
db.attrs(self.id.into()).is_unstable()
}
+
+ pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> {
+ let args = infer_ctxt.fresh_args_for_item(self.id.into());
+ InstantiatedStruct { inner: self, args }
+ }
}
impl HasVisibility for Struct {
@@ -1455,6 +1492,35 @@
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct InstantiatedStruct<'db> {
+ pub(crate) inner: Struct,
+ pub(crate) args: GenericArgs<'db>,
+}
+
+impl<'db> InstantiatedStruct<'db> {
+ pub fn fields(self, db: &dyn HirDatabase) -> Vec<InstantiatedField<'db>> {
+ self.inner
+ .id
+ .fields(db)
+ .fields()
+ .iter()
+ .map(|(id, _)| InstantiatedField {
+ inner: Field { parent: self.inner.into(), id },
+ args: self.args,
+ })
+ .collect()
+ }
+
+ pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> {
+ 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());
+ TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args))
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Union {
pub(crate) id: UnionId,
}
@@ -1598,6 +1664,22 @@
}
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct InstantiatedEnum<'db> {
+ pub(crate) inner: Enum,
+ pub(crate) args: GenericArgs<'db>,
+}
+
+impl<'db> InstantiatedEnum<'db> {
+ pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> {
+ 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());
+ TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args))
+ }
+}
+
impl From<&Variant> for DefWithBodyId {
fn from(&v: &Variant) -> Self {
DefWithBodyId::VariantId(v.into())
@@ -1673,6 +1755,38 @@
pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
db.attrs(self.id.into()).is_unstable()
}
+
+ pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedVariant<'db> {
+ let args =
+ infer_ctxt.fresh_args_for_item(self.parent_enum(infer_ctxt.interner.db()).id.into());
+ InstantiatedVariant { inner: self, args }
+ }
+}
+
+// FIXME: Rename to `EnumVariant`
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct InstantiatedVariant<'db> {
+ pub(crate) inner: Variant,
+ pub(crate) args: GenericArgs<'db>,
+}
+
+impl<'db> InstantiatedVariant<'db> {
+ pub fn parent_enum(self, db: &dyn HirDatabase) -> InstantiatedEnum<'db> {
+ InstantiatedEnum { inner: self.inner.id.lookup(db).parent.into(), args: self.args }
+ }
+
+ pub fn fields(self, db: &dyn HirDatabase) -> Vec<InstantiatedField<'db>> {
+ self.inner
+ .id
+ .fields(db)
+ .fields()
+ .iter()
+ .map(|(id, _)| InstantiatedField {
+ inner: Field { parent: self.inner.into(), id },
+ args: self.args,
+ })
+ .collect()
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -5072,7 +5186,7 @@
binders: CanonicalVarKinds::empty(Interner),
};
- db.trait_solve(self.env.krate, self.env.block, goal).is_some()
+ !db.trait_solve(self.env.krate, self.env.block, goal).no_solution()
}
pub fn normalize_trait_assoc_type(
@@ -5827,6 +5941,55 @@
}
}
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub struct TypeNs<'db> {
+ env: Arc<TraitEnvironment>,
+ ty: hir_ty::next_solver::Ty<'db>,
+ _pd: PhantomCovariantLifetime<'db>,
+}
+
+impl<'db> TypeNs<'db> {
+ fn new(
+ db: &'db dyn HirDatabase,
+ lexical_env: impl HasResolver,
+ ty: hir_ty::next_solver::Ty<'db>,
+ ) -> Self {
+ let resolver = lexical_env.resolver(db);
+ let environment = resolver
+ .generic_def()
+ .map_or_else(|| TraitEnvironment::empty(resolver.krate()), |d| db.trait_environment(d));
+ TypeNs { env: environment, ty, _pd: PhantomCovariantLifetime::new() }
+ }
+
+ // 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(
+ infcx.interner,
+ [self.ty].into_iter().chain(args.iter().map(|t| t.ty)).map(|t| t.into()),
+ );
+ let trait_ref = hir_ty::next_solver::TraitRef::new(
+ infcx.interner,
+ SolverDefId::TraitId(trait_.id),
+ args,
+ );
+
+ let pred_kind = rustc_type_ir::Binder::dummy(rustc_type_ir::PredicateKind::Clause(
+ rustc_type_ir::ClauseKind::Trait(rustc_type_ir::TraitPredicate {
+ trait_ref,
+ polarity: rustc_type_ir::PredicatePolarity::Positive,
+ }),
+ ));
+ let predicate = hir_ty::next_solver::Predicate::new(infcx.interner, pred_kind);
+ let goal = hir_ty::next_solver::Goal::new(
+ infcx.interner,
+ hir_ty::next_solver::ParamEnv::empty(),
+ predicate,
+ );
+ 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))
+ }
+}
+
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub struct InlineAsmOperand {
owner: DefWithBodyId,
@@ -6476,3 +6639,6 @@
fn as_name_opt(name: Option<impl AsName>) -> Name {
name.map_or_else(Name::missing, |name| name.as_name())
}
+
+pub use hir_ty::next_solver;
+pub use hir_ty::setup_tracing;
diff --git a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
index af949a0..cb83c67 100644
--- a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
@@ -1,3 +1,4 @@
+use hir::next_solver::{DbInterner, TypingMode};
use ide_db::{RootDatabase, famous_defs::FamousDefs};
use syntax::ast::{self, AstNode, HasName};
@@ -80,17 +81,20 @@
sema: &'_ hir::Semantics<'_, RootDatabase>,
variant: &ast::Variant,
) -> Option<()> {
+ let db = sema.db;
let variant = sema.to_def(variant)?;
- let enum_ = variant.parent_enum(sema.db);
- let krate = enum_.module(sema.db).krate();
-
+ let krate = variant.module(db).krate();
let from_trait = FamousDefs(sema, krate).core_convert_From()?;
+ let interner = DbInterner::new_with(db, Some(krate.base()), None);
+ use hir::next_solver::infer::DbInternerInferExt;
+ let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
- let enum_type = enum_.ty(sema.db);
-
- let wrapped_type = variant.fields(sema.db).first()?.ty(sema.db);
-
- if enum_type.impls_trait(sema.db, from_trait, &[wrapped_type]) { Some(()) } else { None }
+ let variant = variant.instantiate_infer(&infcx);
+ let enum_ = variant.parent_enum(sema.db);
+ let field_ty = variant.fields(sema.db).first()?.ty(sema.db);
+ let enum_ty = enum_.ty(sema.db);
+ tracing::debug!(?enum_, ?field_ty, ?enum_ty);
+ enum_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(())
}
#[cfg(test)]
@@ -119,15 +123,19 @@
);
}
+ // FIXME(next-solver): it would be nice to not be *required* to resolve the
+ // path in order to properly generate assists
#[test]
fn test_generate_from_impl_for_enum_complicated_path() {
check_assist(
generate_from_impl_for_enum,
r#"
//- minicore: from
+mod foo { pub mod bar { pub mod baz { pub struct Boo; } } }
enum A { $0One(foo::bar::baz::Boo) }
"#,
r#"
+mod foo { pub mod bar { pub mod baz { pub struct Boo; } } }
enum A { One(foo::bar::baz::Boo) }
impl From<foo::bar::baz::Boo> for A {
diff --git a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
index 6076d5c..deef4a9 100644
--- a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
+++ b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
@@ -1,4 +1,5 @@
use ast::make;
+use hir::next_solver::{DbInterner, TypingMode};
use hir::{HasCrate, ModuleDef, Semantics};
use ide_db::{
RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast,
@@ -47,6 +48,7 @@
let strukt_name = ctx.find_node_at_offset::<ast::Name>()?;
let adt = ast::Adt::cast(strukt_name.syntax().parent()?)?;
let ast::Adt::Struct(strukt) = adt else {
+ tracing::debug!(?adt);
return None;
};
@@ -57,10 +59,12 @@
let constructors = make_constructors(ctx, module, &types);
if constructors.iter().filter(|expr| expr.is_none()).count() != 1 {
+ tracing::debug!(?constructors);
return None;
}
let main_field_i = constructors.iter().position(Option::is_none)?;
if from_impl_exists(&strukt, main_field_i, &ctx.sema).is_some() {
+ tracing::debug!(?strukt, ?main_field_i);
return None;
}
@@ -200,6 +204,7 @@
})
}
+#[tracing::instrument(ret)]
fn from_impl_exists(
strukt: &ast::Struct,
main_field_i: usize,
@@ -209,9 +214,15 @@
let strukt = sema.to_def(strukt)?;
let krate = strukt.krate(db);
let from_trait = FamousDefs(sema, krate).core_convert_From()?;
- let ty = strukt.fields(db).get(main_field_i)?.ty(db);
+ let interner = DbInterner::new_with(db, Some(krate.base()), None);
+ use hir::next_solver::infer::DbInternerInferExt;
+ let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
- strukt.ty(db).impls_trait(db, from_trait, &[ty]).then_some(())
+ let strukt = strukt.instantiate_infer(&infcx);
+ let field_ty = strukt.fields(db).get(main_field_i)?.ty(db);
+ let struct_ty = strukt.ty(db);
+ tracing::debug!(?strukt, ?field_ty, ?struct_ty);
+ struct_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(())
}
#[cfg(test)]
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 4682c04..5008f97 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -67,7 +67,7 @@
pub mod utils;
use hir::Semantics;
-use ide_db::{EditionedFileId, RootDatabase};
+use ide_db::{EditionedFileId, RootDatabase, base_db::salsa};
use syntax::{Edition, TextRange};
pub(crate) use crate::assist_context::{AssistContext, Assists};
@@ -93,8 +93,11 @@
.unwrap_or_else(|| EditionedFileId::new(db, range.file_id, Edition::CURRENT));
let ctx = AssistContext::new(sema, config, hir::FileRange { file_id, range: range.range });
let mut acc = Assists::new(&ctx, resolve);
- handlers::all().iter().for_each(|handler| {
- handler(&mut acc, &ctx);
+ // the handlers may invoke trait solving related things which accesses salsa structs outside queries
+ salsa::attach(db, || {
+ handlers::all().iter().for_each(|handler| {
+ handler(&mut acc, &ctx);
+ });
});
acc.finish()
}
diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs
index cda2ad4..f4daabf 100644
--- a/crates/ide-assists/src/tests.rs
+++ b/crates/ide-assists/src/tests.rs
@@ -1,11 +1,11 @@
mod generated;
use expect_test::expect;
-use hir::Semantics;
+use hir::{Semantics, setup_tracing};
use ide_db::{
EditionedFileId, FileRange, RootDatabase, SnippetCap,
assists::ExprFillDefaultMode,
- base_db::SourceDatabase,
+ base_db::{SourceDatabase, salsa},
imports::insert_use::{ImportGranularity, InsertUseConfig},
source_change::FileSystemEdit,
};
@@ -305,6 +305,7 @@
expected: ExpectedResult<'_>,
assist_label: Option<&str>,
) {
+ let _tracing = setup_tracing();
let (mut db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
db.enable_proc_attr_macros();
let text_without_caret = db.file_text(file_with_caret_id.file_id(&db)).text(&db).to_string();
@@ -318,7 +319,9 @@
_ => AssistResolveStrategy::All,
};
let mut acc = Assists::new(&ctx, resolve);
- handler(&mut acc, &ctx);
+ salsa::attach(&db, || {
+ handler(&mut acc, &ctx);
+ });
let mut res = acc.finish();
let assist = match assist_label {
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index f751233..129964f 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -1384,14 +1384,15 @@
fn skip_iter() {
check_no_kw(
r#"
- //- minicore: iterator
+ //- minicore: iterator, clone, builtin_impls
fn foo() {
[].$0
}
"#,
expect![[r#"
- me clone() (as Clone) fn(&self) -> Self
- me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
+ me clone() (as Clone) fn(&self) -> Self
+ me fmt(…) (use core::fmt::Debug) fn(&self, &mut Formatter<'_>) -> Result<(), Error>
+ me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
"#]],
);
check_no_kw(
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index a70a113..1a4c97e 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -10,6 +10,7 @@
#[cfg(test)]
mod tests;
+use base_db::salsa;
use ide_db::{
FilePosition, FxHashSet, RootDatabase,
imports::insert_use::{self, ImportScope},
@@ -228,7 +229,7 @@
{
let acc = &mut completions;
- match analysis {
+ salsa::attach(db, || match analysis {
CompletionAnalysis::Name(name_ctx) => completions::complete_name(acc, ctx, name_ctx),
CompletionAnalysis::NameRef(name_ref_ctx) => {
completions::complete_name_ref(acc, ctx, name_ref_ctx)
@@ -256,7 +257,7 @@
);
}
CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (),
- }
+ })
}
Some(completions.into())
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs
index fdc3d9a..4b3b271 100644
--- a/crates/ide-completion/src/tests.rs
+++ b/crates/ide-completion/src/tests.rs
@@ -26,7 +26,7 @@
use base_db::SourceDatabase;
use expect_test::Expect;
-use hir::PrefixKind;
+use hir::{PrefixKind, setup_tracing};
use ide_db::{
FilePosition, RootDatabase, SnippetCap,
imports::insert_use::{ImportGranularity, InsertUseConfig},
@@ -120,6 +120,8 @@
include_keywords: bool,
trigger_character: Option<char>,
) -> Vec<CompletionItem> {
+ let _tracing = setup_tracing();
+
// filter out all but one built-in type completion for smaller test outputs
let items = get_all_items(config, ra_fixture, trigger_character);
items
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index 27c91bc..bb10086 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -1,3 +1,4 @@
+use base_db::salsa;
use expect_test::{Expect, expect};
use crate::{
@@ -19,25 +20,29 @@
let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap();
let mut acc = crate::completions::Completions::default();
- if let CompletionAnalysis::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) =
- &analysis
- {
- crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pat_ctx);
- }
- if let CompletionAnalysis::NameRef(name_ref_ctx) = &analysis {
- match &name_ref_ctx.kind {
- NameRefKind::Path(path) => {
- crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path);
- }
- NameRefKind::DotAccess(dot_access) => {
- crate::completions::flyimport::import_on_the_fly_dot(&mut acc, &ctx, dot_access);
- }
- NameRefKind::Pattern(pattern) => {
- crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pattern);
- }
- _ => (),
+ salsa::attach(ctx.db, || {
+ if let CompletionAnalysis::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) =
+ &analysis
+ {
+ crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pat_ctx);
}
- }
+ if let CompletionAnalysis::NameRef(name_ref_ctx) = &analysis {
+ match &name_ref_ctx.kind {
+ NameRefKind::Path(path) => {
+ crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path);
+ }
+ NameRefKind::DotAccess(dot_access) => {
+ crate::completions::flyimport::import_on_the_fly_dot(
+ &mut acc, &ctx, dot_access,
+ );
+ }
+ NameRefKind::Pattern(pattern) => {
+ crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pattern);
+ }
+ _ => (),
+ }
+ }
+ });
expect.assert_eq(&super::render_completion_list(Vec::from(acc)));
}
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index 1482031..84ddff8 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -677,6 +677,7 @@
expect![[r#"
fn foo() (as Foo) fn() -> Self
ex Bar
+ ex Bar::foo()
ex bar()
"#]],
);
@@ -706,6 +707,7 @@
fn bar() fn()
fn foo() (as Foo) fn() -> Self
ex Bar
+ ex Bar::foo()
ex bar()
"#]],
);
@@ -734,6 +736,7 @@
expect![[r#"
fn foo() (as Foo) fn() -> Self
ex Bar
+ ex Bar::foo()
ex bar()
"#]],
);
diff --git a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
index 1e80d02..6331090 100644
--- a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
+++ b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -157,7 +157,7 @@
fn no_false_positive_dyn_fn() {
check_diagnostics(
r#"
-//- minicore: copy, fn
+//- minicore: copy, fn, dispatch_from_dyn
fn f(x: &mut &mut dyn Fn()) {
x();
}
@@ -166,7 +166,7 @@
field: &'a mut dyn Fn(),
}
-fn f(x: &mut X<'_>) {
+fn g(x: &mut X<'_>) {
(x.field)();
}
"#,
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index a1db926..a4eb3d4 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -92,7 +92,7 @@
use ide_db::{
EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap,
assists::{Assist, AssistId, AssistResolveStrategy, ExprFillDefaultMode},
- base_db::{ReleaseChannel, RootQueryDb as _},
+ base_db::{ReleaseChannel, RootQueryDb as _, salsa},
generated::lints::{CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS, DEFAULT_LINTS, Lint, LintGroup},
imports::insert_use::InsertUseConfig,
label::Label,
@@ -537,10 +537,12 @@
resolve: &AssistResolveStrategy,
file_id: FileId,
) -> Vec<Diagnostic> {
- let mut res = syntax_diagnostics(db, config, file_id);
- let sema = semantic_diagnostics(db, config, resolve, file_id);
- res.extend(sema);
- res
+ salsa::attach(db, || {
+ let mut res = syntax_diagnostics(db, config, file_id);
+ let sema = semantic_diagnostics(db, config, resolve, file_id);
+ res.extend(sema);
+ res
+ })
}
/// Returns whether to keep this diagnostic (or remove it).
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs
index 1819931..c3cc5a0 100644
--- a/crates/ide-diagnostics/src/tests.rs
+++ b/crates/ide-diagnostics/src/tests.rs
@@ -2,6 +2,7 @@
mod overly_long_real_world_cases;
+use hir::setup_tracing;
use ide_db::{
LineIndexDatabase, RootDatabase,
assists::{AssistResolveStrategy, ExprFillDefaultMode},
@@ -198,6 +199,8 @@
config: DiagnosticsConfig,
#[rust_analyzer::rust_fixture] ra_fixture: &str,
) {
+ let _tracing = setup_tracing();
+
let (db, files) = RootDatabase::with_many_files(ra_fixture);
let mut annotations = files
.iter()
diff --git a/crates/ide-ssr/src/resolving.rs b/crates/ide-ssr/src/resolving.rs
index a4e2cfb..1d5f5ad 100644
--- a/crates/ide-ssr/src/resolving.rs
+++ b/crates/ide-ssr/src/resolving.rs
@@ -1,7 +1,7 @@
//! This module is responsible for resolving paths within rules.
use hir::AsAssocItem;
-use ide_db::FxHashMap;
+use ide_db::{FxHashMap, base_db::salsa};
use parsing::Placeholder;
use syntax::{
SmolStr, SyntaxKind, SyntaxNode, SyntaxToken,
@@ -48,16 +48,20 @@
resolution_scope: &ResolutionScope<'db>,
index: usize,
) -> Result<ResolvedRule<'db>, SsrError> {
- let resolver =
- Resolver { resolution_scope, placeholders_by_stand_in: rule.placeholders_by_stand_in };
- let resolved_template = match rule.template {
- Some(template) => Some(resolver.resolve_pattern_tree(template)?),
- None => None,
- };
- Ok(ResolvedRule {
- pattern: resolver.resolve_pattern_tree(rule.pattern)?,
- template: resolved_template,
- index,
+ salsa::attach(resolution_scope.scope.db, || {
+ let resolver = Resolver {
+ resolution_scope,
+ placeholders_by_stand_in: rule.placeholders_by_stand_in,
+ };
+ let resolved_template = match rule.template {
+ Some(template) => Some(resolver.resolve_pattern_tree(template)?),
+ None => None,
+ };
+ Ok(ResolvedRule {
+ pattern: resolver.resolve_pattern_tree(rule.pattern)?,
+ template: resolved_template,
+ index,
+ })
})
}
diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs
index c2f08f8..b004c07 100644
--- a/crates/ide/src/doc_links/tests.rs
+++ b/crates/ide/src/doc_links/tests.rs
@@ -4,6 +4,7 @@
use hir::Semantics;
use ide_db::{
FilePosition, FileRange, RootDatabase,
+ base_db::salsa,
defs::Definition,
documentation::{DocsRangeMap, Documentation, HasDocs},
};
@@ -63,8 +64,10 @@
.flat_map(|(text_range, link, ns)| {
let attr = range.map(text_range);
let is_inner_attr = attr.map(|(_file, attr)| attr.is_inner_attr()).unwrap_or(false);
- let def = resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr)
- .unwrap_or_else(|| panic!("Failed to resolve {link}"));
+ let def = salsa::attach(sema.db, || {
+ resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr)
+ .unwrap_or_else(|| panic!("Failed to resolve {link}"))
+ });
def.try_to_nav(sema.db).unwrap().into_iter().zip(iter::repeat(link))
})
.map(|(nav_target, link)| {
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index f768d4b..633feec 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -10,7 +10,7 @@
};
use ide_db::{
RootDatabase, SymbolKind,
- base_db::{AnchoredPath, SourceDatabase},
+ base_db::{AnchoredPath, SourceDatabase, salsa},
defs::{Definition, IdentClass},
famous_defs::FamousDefs,
helpers::pick_best_token,
@@ -108,7 +108,7 @@
}
Some(
- IdentClass::classify_node(sema, &parent)?
+ salsa::attach(sema.db, || IdentClass::classify_node(sema, &parent))?
.definitions()
.into_iter()
.flat_map(|(def, _)| {
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 02d96a6..4f17262 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -234,6 +234,7 @@
);
}
+ // FIXME(next-solver): it would be nice to be able to also point to `&Foo`
#[test]
fn goto_implementation_all_impls() {
check(
@@ -246,7 +247,6 @@
impl T for Foo {}
//^^^
impl T for &Foo {}
- //^^^^
"#,
);
}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 44c98a4..a48fe43 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -12,6 +12,7 @@
};
use ide_db::{
FileRange, FxIndexSet, Ranker, RootDatabase,
+ base_db::salsa,
defs::{Definition, IdentClass, NameRefClass, OperatorClass},
famous_defs::FamousDefs,
helpers::pick_best_token,
@@ -290,7 +291,7 @@
.into_iter()
.unique_by(|&((def, _), _, _, _)| def)
.map(|((def, subst), macro_arm, hovered_definition, node)| {
- hover_for_definition(
+ salsa::attach(sema.db, || hover_for_definition(
sema,
file_id,
def,
@@ -301,7 +302,7 @@
config,
edition,
display_target,
- )
+ ))
})
.collect::<Vec<_>>(),
)
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 51b5900..1c0272c 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -10,6 +10,7 @@
};
use ide_db::{
RootDatabase,
+ base_db::salsa,
defs::Definition,
documentation::{DocsRangeMap, HasDocs},
famous_defs::FamousDefs,
@@ -44,7 +45,7 @@
Either::Left(expr) => sema.type_of_expr(expr)?,
Either::Right(pat) => sema.type_of_pat(pat)?,
};
- type_info(sema, _config, ty_info, edition, display_target)
+ salsa::attach(sema.db, || type_info(sema, _config, ty_info, edition, display_target))
}
pub(super) fn closure_expr(
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index c548021..6f1bbad 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -6,6 +6,8 @@
HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, fixture,
};
+use hir::setup_tracing;
+
const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
links_in_hover: false,
memory_layout: Some(MemoryLayoutHoverConfig {
@@ -38,6 +40,7 @@
#[track_caller]
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
+ let _tracing = setup_tracing();
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
.hover(
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 8c2a2f6..f6416fe 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -8,7 +8,9 @@
ClosureStyle, DisplayTarget, EditionedFileId, HasVisibility, HirDisplay, HirDisplayError,
HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym,
};
-use ide_db::{FileRange, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder};
+use ide_db::{
+ FileRange, RootDatabase, base_db::salsa, famous_defs::FamousDefs, text_edit::TextEditBuilder,
+};
use ide_db::{FxHashSet, text_edit::TextEdit};
use itertools::Itertools;
use smallvec::{SmallVec, smallvec};
@@ -734,7 +736,7 @@
config: &InlayHintsConfig,
display_target: DisplayTarget,
) -> Result<(), HirDisplayError> {
- let iter_item_type = hint_iterator(sema, famous_defs, ty);
+ let iter_item_type = salsa::attach(sema.db, || hint_iterator(sema, famous_defs, ty));
match iter_item_type {
Some((iter_trait, item, ty)) => {
const LABEL_START: &str = "impl ";
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs
index 4d020ba..39554d7 100644
--- a/crates/ide/src/inlay_hints/adjustment.rs
+++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -411,10 +411,9 @@
(()) == {()};
// ^^&
// ^^^^&
- let closure: dyn Fn = || ();
+ let closure: &dyn Fn = &|| ();
+ //^^^^^^<unsize>&*
closure();
- //^^^^^^^(&
- //^^^^^^^)
Struct[0];
//^^^^^^(&
//^^^^^^)
@@ -507,9 +506,10 @@
(()) == {()};
// ^^.&
// ^^^^.&
- let closure: dyn Fn = || ();
+ let closure: &dyn Fn = &|| ();
+ //^^^^^^(
+ //^^^^^^).*.&.<unsize>
closure();
- //^^^^^^^.&
Struct[0];
//^^^^^^.&
&mut Struct[0];
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index 922e959..7a4af4f 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -909,7 +909,7 @@
foo(plus_one);
let add_mul = bar(|x: u8| { x + 1 });
- // ^^^^^^^ impl FnOnce(u8) -> u8 + ?Sized
+ // ^^^^^^^ impl FnOnce(u8) -> u8
let closure = if let Some(6) = add_mul(2).checked_sub(1) {
// ^^^^^^^ fn(i32) -> i32
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 382573b..2cccb96 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -733,7 +733,7 @@
mod tests {
use expect_test::{Expect, expect};
- use ide_db::FilePosition;
+ use ide_db::{FilePosition, base_db::salsa};
use stdx::format_to;
use test_fixture::ChangeFixture;
@@ -762,7 +762,7 @@
"#
);
let (db, position) = position(&fixture);
- let sig_help = crate::signature_help::signature_help(&db, position);
+ let sig_help = salsa::attach(&db, || crate::signature_help::signature_help(&db, position));
let actual = match sig_help {
Some(sig_help) => {
let mut rendered = String::new();
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 3ca1729..e3dc3ed 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -16,7 +16,7 @@
use either::Either;
use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Name, Semantics};
-use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind};
+use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind, base_db::salsa};
use syntax::{
AstNode, AstToken, NodeOrToken,
SyntaxKind::*,
@@ -434,15 +434,17 @@
|node| unsafe_ops.contains(&InFile::new(descended_element.file_id, node));
let element = match descended_element.value {
NodeOrToken::Node(name_like) => {
- let hl = highlight::name_like(
- sema,
- krate,
- bindings_shadow_count,
- &is_unsafe_node,
- config.syntactic_name_ref_highlighting,
- name_like,
- edition,
- );
+ let hl = salsa::attach(sema.db, || {
+ highlight::name_like(
+ sema,
+ krate,
+ bindings_shadow_count,
+ &is_unsafe_node,
+ config.syntactic_name_ref_highlighting,
+ name_like,
+ edition,
+ )
+ });
if hl.is_some() && !in_macro {
// skip highlighting the contained token of our name-like node
// as that would potentially overwrite our result
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
index 00925bd..d99b29c 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -127,9 +127,9 @@
<span class="keyword">let</span> <span class="variable declaration mutable reference">y</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">mut</span> <span class="variable mutable">x</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration reference">z</span> <span class="operator">=</span> <span class="operator">&</span><span class="variable mutable reference">y</span><span class="semicolon">;</span>
- <span class="keyword">let</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable declaration">z</span><span class="comma">,</span> <span class="variable declaration">y</span> <span class="brace">}</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable reference">z</span><span class="comma">,</span> <span class="variable mutable reference">y</span> <span class="brace">}</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable declaration">z</span><span class="comma">,</span> <span class="variable callable declaration">y</span> <span class="brace">}</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable reference">z</span><span class="comma">,</span> <span class="variable mutable reference">y</span> <span class="brace">}</span><span class="semicolon">;</span>
- <span class="variable">y</span><span class="semicolon">;</span>
+ <span class="variable callable">y</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="unresolved_reference">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="unresolved_reference">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span>
@@ -142,7 +142,7 @@
<span class="variable mutable">copy</span><span class="operator">.</span><span class="method mutable reference">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="variable mutable">copy</span><span class="operator">.</span><span class="method">baz</span><span class="parenthesis">(</span><span class="variable mutable">copy</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="keyword">let</span> <span class="variable callable declaration">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable callable declaration">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param callable declaration">x</span><span class="punctuation">|</span> <span class="value_param callable">x</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable callable declaration">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="method associated consuming">baz</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="comma">,</span><span class="parenthesis">)</span><span class="semicolon">;</span>
@@ -172,13 +172,13 @@
<span class="brace">}</span>
<span class="keyword async">async</span> <span class="keyword">fn</span> <span class="function async declaration">learn_and_sing</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="keyword">let</span> <span class="variable declaration">song</span> <span class="operator">=</span> <span class="unresolved_reference">learn_song</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="operator">.</span><span class="keyword async control">await</span><span class="semicolon">;</span>
- <span class="unresolved_reference">sing_song</span><span class="parenthesis">(</span><span class="variable consuming">song</span><span class="parenthesis">)</span><span class="operator">.</span><span class="keyword async control">await</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable callable declaration">song</span> <span class="operator">=</span> <span class="unresolved_reference">learn_song</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="operator">.</span><span class="keyword async control">await</span><span class="semicolon">;</span>
+ <span class="unresolved_reference">sing_song</span><span class="parenthesis">(</span><span class="variable callable">song</span><span class="parenthesis">)</span><span class="operator">.</span><span class="keyword async control">await</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword async">async</span> <span class="keyword">fn</span> <span class="function async declaration">async_main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="keyword">let</span> <span class="variable declaration">f1</span> <span class="operator">=</span> <span class="function async">learn_and_sing</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="keyword">let</span> <span class="variable declaration">f2</span> <span class="operator">=</span> <span class="unresolved_reference">dance</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable callable declaration">f2</span> <span class="operator">=</span> <span class="unresolved_reference">dance</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="unresolved_reference">futures</span><span class="operator">::</span><span class="unresolved_reference">join</span><span class="macro_bang">!</span><span class="parenthesis">(</span>f1<span class="comma">,</span> f2<span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 4780743..983ed36 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -178,6 +178,8 @@
core,
coroutine_state,
coroutine,
+ coroutine_return,
+ coroutine_yield,
count,
crate_type,
CStr,
@@ -226,6 +228,8 @@
async_fn_once_output,
async_fn_mut,
async_fn,
+ call_ref_future,
+ call_once_future,
fn_ptr_addr,
fn_ptr_trait,
format_alignment,
@@ -416,6 +420,7 @@
rustc_allow_incoherent_impl,
rustc_builtin_macro,
rustc_coherence_is_core,
+ rustc_coinductive,
rustc_const_panic_str,
rustc_deallocator,
rustc_deprecated_safe_2024,
diff --git a/crates/profile/src/stop_watch.rs b/crates/profile/src/stop_watch.rs
index 9f3e636..00c37c0 100644
--- a/crates/profile/src/stop_watch.rs
+++ b/crates/profile/src/stop_watch.rs
@@ -37,10 +37,10 @@
.build()
.map_err(|err| eprintln!("Failed to create perf counter: {err}"))
.ok();
- if let Some(counter) = &mut counter {
- if let Err(err) = counter.enable() {
- eprintln!("Failed to start perf counter: {err}")
- }
+ if let Some(counter) = &mut counter
+ && let Err(err) = counter.enable()
+ {
+ eprintln!("Failed to start perf counter: {err}")
}
counter
} else {
diff --git a/crates/query-group-macro/src/queries.rs b/crates/query-group-macro/src/queries.rs
index c151cca..22a26c4 100644
--- a/crates/query-group-macro/src/queries.rs
+++ b/crates/query-group-macro/src/queries.rs
@@ -48,7 +48,8 @@
quote!(#(#options),*)
})
.into_iter()
- .chain(self.lru.map(|lru| quote!(lru = #lru)));
+ .chain(self.lru.map(|lru| quote!(lru = #lru)))
+ .chain(Some(quote!(unsafe(non_update_return_type))));
let annotation = quote!(#[salsa_macros::tracked( #(#options),* )]);
let pat_and_tys = &self.pat_and_tys;
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 7b719b5..b735b32 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -35,7 +35,7 @@
//! error: fmt
//! fmt: option, result, transmute, coerce_unsized, copy, clone, derive
//! fmt_before_1_89_0: fmt
-//! fn: tuple
+//! fn: sized, tuple
//! from: sized, result
//! future: pin
//! coroutine: pin
@@ -325,6 +325,18 @@
*self
}
}
+
+ impl<T: Clone> Clone for [T; 0] {
+ fn clone(&self) -> Self {
+ []
+ }
+ }
+
+ impl<T: Clone> Clone for [T; 1] {
+ fn clone(&self) -> Self {
+ [self[0].clone()]
+ }
+ }
// endregion:builtin_impls
// region:derive
@@ -1035,6 +1047,7 @@
#[lang = "coroutine"]
pub trait Coroutine<R = ()> {
+ #[lang = "coroutine_yield"]
type Yield;
#[lang = "coroutine_return"]
type Return;