Implement next trait solver
This commit is contained in:
parent
840d8130a1
commit
eb2bbbb913
109 changed files with 19148 additions and 1317 deletions
|
|
@ -259,9 +259,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
|||
|
||||
[[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 @@ dependencies = [
|
|||
|
||||
[[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 @@ dependencies = [
|
|||
|
||||
[[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 @@ dependencies = [
|
|||
|
||||
[[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",
|
||||
|
|
@ -445,6 +445,17 @@ dependencies = [
|
|||
"powerfmt",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
@ -696,6 +707,7 @@ dependencies = [
|
|||
"indexmap",
|
||||
"intern",
|
||||
"itertools 0.14.0",
|
||||
"ra-ap-rustc_type_ir",
|
||||
"rustc-hash 2.1.1",
|
||||
"smallvec",
|
||||
"span",
|
||||
|
|
@ -705,6 +717,8 @@ dependencies = [
|
|||
"test-fixture",
|
||||
"test-utils",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-tree",
|
||||
"triomphe",
|
||||
"tt",
|
||||
]
|
||||
|
|
@ -801,8 +815,11 @@ dependencies = [
|
|||
"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",
|
||||
|
|
@ -1856,6 +1873,12 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
@ -1908,6 +1931,19 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
@ -1931,6 +1967,37 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
|
|||
|
|
@ -94,6 +94,9 @@ ra-ap-rustc_parse_format = { version = "0.121", default-features = false }
|
|||
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 @@ arrayvec = "0.7.6"
|
|||
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"
|
||||
|
|
|
|||
|
|
@ -383,12 +383,17 @@ language_item_table! {
|
|||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@ impl ImplSignature {
|
|||
|
||||
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 @@ bitflags::bitflags! {
|
|||
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 @@ impl TraitSignature {
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,14 @@ salsa-macros.workspace = true
|
|||
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 @@ span.workspace = true
|
|||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.5.1"
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing-tree.workspace = true
|
||||
project-model.workspace = true
|
||||
|
||||
# local deps
|
||||
|
|
|
|||
|
|
@ -225,7 +225,9 @@ pub(crate) fn deref_by_trait(
|
|||
// 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Inte
|
|||
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 @@ impl chalk_ir::UnificationDatabase<Interner> for &dyn HirDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
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 well_known_trait_from_lang_item(item: LangItem) -> Option<WellKnownTrait> {
|
|||
})
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -14,10 +14,9 @@ use hir_def::{
|
|||
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 @@ impl TyExt for Ty {
|
|||
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 {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use test_utils::skip_slow_tests;
|
|||
|
||||
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 pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
|
|||
}
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ use smallvec::SmallVec;
|
|||
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 @@ use crate::{
|
|||
lower::{Diagnostics, GenericDefaults, GenericPredicates},
|
||||
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
|
||||
mir::{BorrowckResult, MirBody, MirLowerError},
|
||||
traits::NextTraitSolveResult,
|
||||
};
|
||||
|
||||
#[query_group::query_group]
|
||||
|
|
@ -295,24 +296,162 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
|
|||
) -> 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]
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc<TraitEnvironment>) -> bool {
|
|||
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(
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ use rustc_hash::FxHashSet;
|
|||
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 @@ fn receiver_is_dispatchable(
|
|||
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 @@ fn receiver_is_dispatchable(
|
|||
.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 @@ fn receiver_is_dispatchable(
|
|||
|
||||
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> {
|
||||
|
|
|
|||
|
|
@ -56,18 +56,21 @@ fn check_dyn_compatibility<'a>(
|
|||
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;");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,6 +165,21 @@ impl Generics {
|
|||
(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 @@ pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> O
|
|||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -88,52 +88,54 @@ pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
|
|||
|
||||
/// 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 @@ pub(crate) fn infer_cycle_result(_: &dyn HirDatabase, _: DefWithBodyId) -> Arc<I
|
|||
///
|
||||
/// 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 @@ impl<'db> InferenceContext<'db> {
|
|||
// 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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 @@ use hir_def::{
|
|||
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 syntax::utils::is_raw_identifier;
|
|||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -13,14 +13,15 @@ use stdx::always;
|
|||
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 @@ impl InferenceTable<'_> {
|
|||
// 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 @@ impl InferenceTable<'_> {
|
|||
},
|
||||
);
|
||||
}
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -278,6 +278,7 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, is_read), ret)]
|
||||
fn infer_expr_inner(
|
||||
&mut self,
|
||||
tgt_expr: ExprId,
|
||||
|
|
@ -286,7 +287,9 @@ impl InferenceContext<'_> {
|
|||
) -> 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 @@ impl InferenceContext<'_> {
|
|||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,11 +19,13 @@ use triomphe::Arc;
|
|||
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 @@ impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
|
|||
// 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 @@ impl<'a> InferenceTable<'a> {
|
|||
/// 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 @@ impl<'a> InferenceTable<'a> {
|
|||
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 @@ impl<'a> InferenceTable<'a> {
|
|||
}
|
||||
|
||||
/// 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 @@ impl<'a> InferenceTable<'a> {
|
|||
};
|
||||
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 @@ impl<'a> InferenceTable<'a> {
|
|||
/// 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 @@ impl<'a> InferenceTable<'a> {
|
|||
/// 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 @@ impl<'a> InferenceTable<'a> {
|
|||
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 @@ impl<'a> InferenceTable<'a> {
|
|||
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 @@ impl<'a> InferenceTable<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
#[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());
|
||||
}
|
||||
Some(_) => {
|
||||
// FIXME use this when trying to resolve everything at the end
|
||||
}
|
||||
None => {
|
||||
// FIXME obligation cannot be fulfilled => diagnostic
|
||||
// ...so, should think about how to get some actually get some guidance here
|
||||
NextTraitSolveResult::Uncertain(v) => {
|
||||
canonicalized.apply_solution(self, v.clone());
|
||||
}
|
||||
NextTraitSolveResult::NoSolution => {}
|
||||
}
|
||||
|
||||
solution
|
||||
}
|
||||
|
||||
|
|
@ -896,7 +1017,10 @@ impl<'a> InferenceTable<'a> {
|
|||
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 @@ impl<'a> InferenceTable<'a> {
|
|||
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 @@ impl<'a> InferenceTable<'a> {
|
|||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 smallvec::SmallVec;
|
|||
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 @@ impl<T: fmt::Debug> fmt::Debug for InternedWrapper<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[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 @@ impl chalk_ir::interner::Interner for Interner {
|
|||
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> {
|
||||
|
|
|
|||
|
|
@ -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 @@ impl<'a> LayoutCx<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ fn layout_of_simd_ty(
|
|||
// * #[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 @@ fn layout_of_simd_ty(
|
|||
|
||||
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 @@ pub fn layout_of_ty_query(
|
|||
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 @@ pub fn layout_of_ty_query(
|
|||
.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 @@ pub fn layout_of_ty_query(
|
|||
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::Error(_) => return Err(LayoutError::HasErrorType),
|
||||
TyKind::Placeholder(_) | TyKind::Bound(..) | TyKind::Infer(..) | TyKind::Param(..) => {
|
||||
return Err(LayoutError::HasPlaceholder);
|
||||
}
|
||||
TyKind::Alias(_)
|
||||
| TyKind::Placeholder(_)
|
||||
| TyKind::BoundVar(_)
|
||||
| TyKind::InferenceVar(_, _) => 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 struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ use std::{cmp, ops::Bound};
|
|||
|
||||
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,16 +15,25 @@ use triomphe::Arc;
|
|||
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 {
|
||||
|
|
@ -35,7 +44,7 @@ pub fn layout_of_adt_query(
|
|||
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 @@ pub fn layout_of_adt_query(
|
|||
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 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>,
|
|||
(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
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use crate::{
|
|||
Interner, Substitution,
|
||||
db::HirDatabase,
|
||||
layout::{Layout, LayoutError},
|
||||
setup_tracing,
|
||||
test_db::TestDB,
|
||||
};
|
||||
|
||||
|
|
@ -29,6 +30,7 @@ fn eval_goal(
|
|||
#[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 @@ fn eval_expr(
|
|||
#[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}}};}}",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,24 @@ extern crate rustc_pattern_analysis;
|
|||
#[cfg(not(feature = "in-rust-tree"))]
|
||||
extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
|
||||
|
||||
#[cfg(feature = "in-rust-tree")]
|
||||
extern crate rustc_ast_ir;
|
||||
|
||||
#[cfg(not(feature = "in-rust-tree"))]
|
||||
extern crate ra_ap_rustc_ast_ir as rustc_ast_ir;
|
||||
|
||||
#[cfg(feature = "in-rust-tree")]
|
||||
extern crate rustc_type_ir;
|
||||
|
||||
#[cfg(not(feature = "in-rust-tree"))]
|
||||
extern crate ra_ap_rustc_type_ir as rustc_type_ir;
|
||||
|
||||
#[cfg(feature = "in-rust-tree")]
|
||||
extern crate rustc_next_trait_solver;
|
||||
|
||||
#[cfg(not(feature = "in-rust-tree"))]
|
||||
extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver;
|
||||
|
||||
mod builder;
|
||||
mod chalk_db;
|
||||
mod chalk_ext;
|
||||
|
|
@ -29,13 +47,16 @@ mod infer;
|
|||
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 @@ mod variance;
|
|||
use std::hash::Hash;
|
||||
|
||||
use chalk_ir::{
|
||||
NoSolution,
|
||||
NoSolution, VariableKinds,
|
||||
fold::{Shift, TypeFoldable},
|
||||
interner::HasInterner,
|
||||
};
|
||||
|
|
@ -121,9 +142,9 @@ pub type ClosureId = chalk_ir::ClosureId<Interner>;
|
|||
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 GenericArgData = chalk_ir::GenericArgData<Interner>;
|
|||
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 @@ where
|
|||
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 make_binders<T: HasInterner<Interner = Interner>>(
|
|||
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 @@ pub fn callable_sig_from_fn_trait(
|
|||
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 @@ pub fn callable_sig_from_fn_trait(
|
|||
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 @@ impl TypeVisitor<Interner> for PlaceholderCollector<'_> {
|
|||
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) enum DeclOrigin {
|
|||
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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,9 +46,9 @@ use stdx::{impl_from, never};
|
|||
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 @@ impl ImplTraitLoweringState {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct PathDiagnosticCallbackData(TypeRefId);
|
||||
pub(crate) struct PathDiagnosticCallbackData(pub(crate) TypeRefId);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum LifetimeElisionKind {
|
||||
|
|
@ -889,7 +889,7 @@ fn named_associated_type_shorthand_candidates<R>(
|
|||
|
||||
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 @@ pub(crate) fn trait_environment_query(
|
|||
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 @@ pub(crate) fn trait_environment_query(
|
|||
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 @@ pub(crate) fn trait_environment_query(
|
|||
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 @@ pub(crate) fn generic_predicates_without_parent_with_diagnostics_query(
|
|||
}
|
||||
|
||||
/// 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,
|
||||
|
|
|
|||
1609
src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs
Normal file
1609
src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs
Normal file
File diff suppressed because it is too large
Load diff
1459
src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs
Normal file
1459
src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -16,22 +16,24 @@ use hir_def::{
|
|||
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 @@ pub enum TyFingerprint {
|
|||
Slice,
|
||||
Array,
|
||||
Never,
|
||||
Ref(Mutability),
|
||||
RawPtr(Mutability),
|
||||
Scalar(Scalar),
|
||||
// These can have user-defined impls:
|
||||
|
|
@ -88,7 +91,7 @@ impl TyFingerprint {
|
|||
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 @@ impl TyFingerprint {
|
|||
};
|
||||
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 @@ fn find_matching_impl(
|
|||
|
||||
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 @@ fn iterate_trait_method_candidates(
|
|||
};
|
||||
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 @@ pub(crate) fn resolve_indexing_op(
|
|||
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 @@ fn is_valid_impl_fn_candidate(
|
|||
);
|
||||
|
||||
match solution {
|
||||
Some(Solution::Unique(canonical_subst)) => {
|
||||
NextTraitSolveResult::Certain(canonical_subst) => {
|
||||
canonicalized.apply_solution(
|
||||
table,
|
||||
Canonical {
|
||||
|
|
@ -1701,16 +1795,13 @@ fn is_valid_impl_fn_candidate(
|
|||
},
|
||||
);
|
||||
}
|
||||
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 @@ pub fn implements_trait(
|
|||
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 @@ pub fn implements_trait_unique(
|
|||
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
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ use syntax::{TextRange, TextSize};
|
|||
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 @@ fn check_pass_and_stdio(
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -2163,7 +2163,9 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<Mi
|
|||
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))
|
||||
}
|
||||
|
|
|
|||
46
src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs
Normal file
46
src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs
Normal file
|
|
@ -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>>;
|
||||
68
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs
Normal file
68
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs
Normal file
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
404
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs
Normal file
404
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs
Normal file
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
129
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs
Normal file
129
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs
Normal file
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
229
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs
Normal file
229
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs
Normal file
|
|
@ -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>),
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
1066
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs
Normal file
1066
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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>;
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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]
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2173
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs
Normal file
2173
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
1368
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs
Normal file
1368
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs
Normal file
File diff suppressed because it is too large
Load diff
167
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs
Normal file
167
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs
Normal file
|
|
@ -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),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
//! Projection code for next-solver.
|
||||
|
||||
pub(crate) mod solve_normalize;
|
||||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
332
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs
Normal file
332
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs
Normal file
|
|
@ -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);
|
||||
289
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs
Normal file
289
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs
Normal file
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
943
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs
Normal file
943
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs
Normal file
|
|
@ -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 } }
|
||||
}
|
||||
}
|
||||
1064
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs
Normal file
1064
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -12,9 +12,6 @@ mod simple;
|
|||
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 @@ use syntax::{
|
|||
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 @@ use crate::{
|
|||
db::HirDatabase,
|
||||
display::{DisplayTarget, HirDisplay},
|
||||
infer::{Adjustment, TypeMismatch},
|
||||
setup_tracing,
|
||||
test_db::TestDB,
|
||||
};
|
||||
|
||||
|
|
@ -51,23 +47,6 @@ use crate::{
|
|||
// 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)
|
||||
|
|
|
|||
|
|
@ -12,9 +12,10 @@ use crate::display::{DisplayTarget, HirDisplay};
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ fn foo<T>(x: &[T]) -> &[T] { x }
|
|||
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 foo<T>(x: &[T]) -> &[T] { x }
|
|||
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 @@ fn test() {
|
|||
);
|
||||
}
|
||||
|
||||
// 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 @@ fn test() {
|
|||
//^ 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 @@ struct Bar<T>(Foo<T>);
|
|||
|
||||
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 @@ impl core::ops::Index<usize> for StructMut {
|
|||
|
||||
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() {
|
||||
|
|
|
|||
|
|
@ -519,6 +519,7 @@ impl SomeStruct {
|
|||
);
|
||||
}
|
||||
|
||||
// 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 @@ fn main() {
|
|||
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 @@ fn main() {
|
|||
"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 @@ fn main() {
|
|||
"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",
|
||||
]
|
||||
"#]],
|
||||
|
|
|
|||
|
|
@ -197,17 +197,17 @@ fn expr_macro_def_expanded_in_various_places() {
|
|||
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 @@ fn expr_macro_rules_expanded_in_various_places() {
|
|||
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 ...!() {}': !
|
||||
|
|
|
|||
|
|
@ -1134,6 +1134,7 @@ fn test() { (S {}).method(); }
|
|||
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 main() {
|
|||
fn dyn_trait_method_priority() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: from
|
||||
//- minicore: from, dispatch_from_dyn
|
||||
trait Trait {
|
||||
fn into(&self) -> usize { 0 }
|
||||
}
|
||||
|
|
@ -1823,6 +1824,33 @@ fn test() {
|
|||
);
|
||||
}
|
||||
|
||||
#[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(
|
||||
|
|
@ -2077,7 +2105,7 @@ impl Foo {
|
|||
}
|
||||
fn test() {
|
||||
Box::new(Foo).foo();
|
||||
//^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?3, Not))
|
||||
//^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?5, Not))
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
@ -2095,7 +2123,7 @@ impl Foo {
|
|||
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))
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -773,7 +773,7 @@ fn issue_4800() {
|
|||
"#,
|
||||
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 @@ fn issue_4800() {
|
|||
);
|
||||
}
|
||||
|
||||
// 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 @@ fn issue_4966() {
|
|||
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 @@ fn mamba(a: U32!(), p: u32) -> u32 {
|
|||
|
||||
#[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 @@ fn test() {
|
|||
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 @@ fn test() {
|
|||
|
||||
#[test]
|
||||
fn bug_11242() {
|
||||
// FIXME: wrong, should be u32
|
||||
check_types(
|
||||
r#"
|
||||
fn foo<A, B>()
|
||||
|
|
@ -1292,7 +1296,7 @@ where
|
|||
B: IntoIterator<Item = usize>,
|
||||
{
|
||||
let _x: <A as IntoIterator>::Item;
|
||||
// ^^ {unknown}
|
||||
// ^^ u32
|
||||
}
|
||||
|
||||
pub trait Iterator {
|
||||
|
|
@ -1495,7 +1499,7 @@ fn regression_11688_2() {
|
|||
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_3() {
|
|||
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 @@ fn issue_17734() {
|
|||
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<'?, ()>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2923,7 +2923,7 @@ fn test {
|
|||
// ^^ impl Fn()
|
||||
|
||||
let c4 = f1();
|
||||
// ^^ impl FnOnce() + ?Sized
|
||||
// ^^ impl FnOnce()
|
||||
|
||||
f2(|| { 0 });
|
||||
// ^^^^^^^^ impl FnOnce() -> i32
|
||||
|
|
@ -3922,7 +3922,7 @@ fn foo<T: Bar>() {
|
|||
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}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1691,7 +1691,7 @@ fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) {
|
|||
get2(y);
|
||||
get(set(S));
|
||||
get2(set(S));
|
||||
get2(S::<str>);
|
||||
get2(S::<usize>);
|
||||
}"#,
|
||||
expect![[r#"
|
||||
49..50 't': T
|
||||
|
|
@ -1703,7 +1703,7 @@ fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) {
|
|||
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 @@ fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) {
|
|||
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 @@ impl<F: core::ops::Deref<Target = impl Bar>> Foo<F> {
|
|||
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 foo() {
|
|||
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 @@ fn f<'a>(v: &dyn Trait<Assoc<i32> = &'a i32>) {
|
|||
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 allowed2<'a>(baz: impl Baz<Assoc = &'a (impl Foo + 'a)>) {}
|
|||
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 @@ fn main() {
|
|||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[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...<()>()': ()
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use crate::{
|
|||
};
|
||||
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 @@ mod unsafe_tls {
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -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 @@ pub(crate) fn normalize_projection_query(
|
|||
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 @@ pub(crate) fn trait_solve_query(
|
|||
&& 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 @@ pub(crate) fn trait_solve_query(
|
|||
// 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)]
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ tracing.workspace = true
|
|||
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 @@ span.workspace = true
|
|||
|
||||
[dev-dependencies]
|
||||
expect-test.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing-tree.workspace = true
|
||||
|
||||
# local deps
|
||||
test-utils.workspace = true
|
||||
|
|
|
|||
|
|
@ -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 @@ use hir_def::{
|
|||
},
|
||||
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 @@ use hir_ty::{
|
|||
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 @@ pub struct CrateDependency {
|
|||
}
|
||||
|
||||
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 struct Field {
|
|||
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 @@ impl Struct {
|
|||
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 {
|
||||
|
|
@ -1454,6 +1491,35 @@ impl HasVisibility for Struct {
|
|||
}
|
||||
}
|
||||
|
||||
#[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 @@ impl HasVisibility for Enum {
|
|||
}
|
||||
}
|
||||
|
||||
#[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 @@ impl Variant {
|
|||
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 @@ impl<'db> Type<'db> {
|
|||
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 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
#[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 @@ pub fn resolve_absolute_path<'a, I: Iterator<Item = Symbol> + Clone + 'a>(
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -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 @@ fn existing_from_impl(
|
|||
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 @@ impl From<u32> for A {
|
|||
);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
|
|||
|
|
@ -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 @@ pub(crate) fn generate_single_field_struct_from(
|
|||
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 @@ pub(crate) fn generate_single_field_struct_from(
|
|||
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 @@ fn get_fields(strukt: &ast::Struct) -> Option<(Option<Vec<ast::Name>>, Vec<ast::
|
|||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(ret)]
|
||||
fn from_impl_exists(
|
||||
strukt: &ast::Struct,
|
||||
main_field_i: usize,
|
||||
|
|
@ -209,9 +214,15 @@ fn from_impl_exists(
|
|||
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)]
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ mod tests;
|
|||
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 @@ pub fn assists(
|
|||
.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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@ fn check_with_config(
|
|||
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 @@ fn check_with_config(
|
|||
_ => 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 {
|
||||
|
|
|
|||
|
|
@ -1384,14 +1384,15 @@ fn baz() {
|
|||
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(
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ mod snippet;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use base_db::salsa;
|
||||
use ide_db::{
|
||||
FilePosition, FxHashSet, RootDatabase,
|
||||
imports::insert_use::{self, ImportScope},
|
||||
|
|
@ -228,7 +229,7 @@ pub fn completions(
|
|||
{
|
||||
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 @@ pub fn completions(
|
|||
);
|
||||
}
|
||||
CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Some(completions.into())
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ mod visibility;
|
|||
|
||||
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 @@ fn completion_list_with_config_raw(
|
|||
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
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use base_db::salsa;
|
||||
use expect_test::{Expect, expect};
|
||||
|
||||
use crate::{
|
||||
|
|
@ -19,25 +20,29 @@ fn check_with_config(
|
|||
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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -677,6 +677,7 @@ fn bar() -> Bar {
|
|||
expect![[r#"
|
||||
fn foo() (as Foo) fn() -> Self
|
||||
ex Bar
|
||||
ex Bar::foo()
|
||||
ex bar()
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -706,6 +707,7 @@ fn bar() -> Bar {
|
|||
fn bar() fn()
|
||||
fn foo() (as Foo) fn() -> Self
|
||||
ex Bar
|
||||
ex Bar::foo()
|
||||
ex bar()
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -734,6 +736,7 @@ fn bar() -> Bar {
|
|||
expect![[r#"
|
||||
fn foo() (as Foo) fn() -> Self
|
||||
ex Bar
|
||||
ex Bar::foo()
|
||||
ex bar()
|
||||
"#]],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ fn h(x: &Y<Unknown>) -> Y<Unknown> {
|
|||
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 @@ struct X<'a> {
|
|||
field: &'a mut dyn Fn(),
|
||||
}
|
||||
|
||||
fn f(x: &mut X<'_>) {
|
||||
fn g(x: &mut X<'_>) {
|
||||
(x.field)();
|
||||
}
|
||||
"#,
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ use hir::{
|
|||
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 @@ pub fn full_diagnostics(
|
|||
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).
|
||||
|
|
|
|||
|
|
@ -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 @@ pub(crate) fn check_diagnostics_with_config(
|
|||
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()
|
||||
|
|
|
|||
|
|
@ -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 @@ impl<'db> ResolvedRule<'db> {
|
|||
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,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use expect_test::{Expect, expect};
|
|||
use hir::Semantics;
|
||||
use ide_db::{
|
||||
FilePosition, FileRange, RootDatabase,
|
||||
base_db::salsa,
|
||||
defs::Definition,
|
||||
documentation::{DocsRangeMap, Documentation, HasDocs},
|
||||
};
|
||||
|
|
@ -63,8 +64,10 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
|||
.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)| {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use hir::{
|
|||
};
|
||||
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 @@ pub(crate) fn goto_definition(
|
|||
}
|
||||
|
||||
Some(
|
||||
IdentClass::classify_node(sema, &parent)?
|
||||
salsa::attach(sema.db, || IdentClass::classify_node(sema, &parent))?
|
||||
.definitions()
|
||||
.into_iter()
|
||||
.flat_map(|(def, _)| {
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ impl crate::T for crate::Foo {}
|
|||
);
|
||||
}
|
||||
|
||||
// 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 Foo {}
|
|||
impl T for Foo {}
|
||||
//^^^
|
||||
impl T for &Foo {}
|
||||
//^^^^
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use hir::{
|
|||
};
|
||||
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 @@ fn hover_offset(
|
|||
.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 @@ fn hover_offset(
|
|||
config,
|
||||
edition,
|
||||
display_target,
|
||||
)
|
||||
))
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use hir::{
|
|||
};
|
||||
use ide_db::{
|
||||
RootDatabase,
|
||||
base_db::salsa,
|
||||
defs::Definition,
|
||||
documentation::{DocsRangeMap, HasDocs},
|
||||
famous_defs::FamousDefs,
|
||||
|
|
@ -44,7 +45,7 @@ pub(super) fn type_info_of(
|
|||
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(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ use crate::{
|
|||
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 @@ fn check_hover_no_result(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
|||
|
||||
#[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(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ use hir::{
|
|||
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 @@ fn label_of_ty(
|
|||
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 ";
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue