summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/hir-ty
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/Cargo.toml15
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/builder.rs217
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs75
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs109
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs43
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs27
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs29
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs64
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs83
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs352
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs33
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs24
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs78
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs173
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs103
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs409
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs273
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs77
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs91
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs63
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs174
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs185
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/traits.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/utils.rs100
37 files changed, 2201 insertions, 767 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index 5cd444c1a..ed13275ba 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -11,18 +11,19 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
-itertools = "0.10.3"
+itertools = "0.10.5"
arrayvec = "0.7.2"
-smallvec = "1.9.0"
+smallvec = "1.10.0"
ena = "0.14.0"
tracing = "0.1.35"
rustc-hash = "1.1.0"
scoped-tls = "1.0.0"
-chalk-solve = { version = "0.83.0", default-features = false }
-chalk-ir = "0.83.0"
-chalk-recursive = { version = "0.83.0", default-features = false }
+chalk-solve = { version = "0.86.0", default-features = false }
+chalk-ir = "0.86.0"
+chalk-recursive = { version = "0.86.0", default-features = false }
+chalk-derive = "0.86.0"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
-once_cell = "1.12.0"
+once_cell = "1.15.0"
typed-arena = "2.0.1"
stdx = { path = "../stdx", version = "0.0.0" }
@@ -37,7 +38,7 @@ limit = { path = "../limit", version = "0.0.0" }
test-utils = { path = "../test-utils" }
expect-test = "1.4.0"
tracing = "0.1.35"
-tracing-subscriber = { version = "0.3.14", default-features = false, features = [
+tracing-subscriber = { version = "0.3.16", default-features = false, features = [
"env-filter",
"registry",
] }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
index b6f226dbf..78911d8dc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
@@ -1,7 +1,7 @@
//! In certain situations, rust automatically inserts derefs as necessary: for
//! example, field accesses `foo.bar` still work when `foo` is actually a
//! reference to a type with the field `bar`. This is an approximation of the
-//! logic in rustc (which lives in librustc_typeck/check/autoderef.rs).
+//! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs).
use std::sync::Arc;
@@ -104,8 +104,7 @@ pub(crate) fn deref(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
fn builtin_deref(ty: &Ty) -> Option<&Ty> {
match ty.kind(Interner) {
- TyKind::Ref(.., ty) => Some(ty),
- TyKind::Raw(.., ty) => Some(ty),
+ TyKind::Ref(.., ty) | TyKind::Raw(.., ty) => Some(ty),
_ => None,
}
}
@@ -124,13 +123,14 @@ fn deref_by_trait(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
let projection = {
- let b = TyBuilder::assoc_type_projection(db, target);
+ let b = TyBuilder::subst_for_def(db, deref_trait, None);
if b.remaining() != 1 {
// the Target type + Deref trait should only have one generic parameter,
// namely Deref's Self type
return None;
}
- b.push(ty).build()
+ let deref_subst = b.push(ty).build();
+ TyBuilder::assoc_type_projection(db, target, Some(deref_subst)).build()
};
// Check that the type implements Deref at all
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
index 94d7806cb..9ae752556 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
@@ -6,19 +6,19 @@ use chalk_ir::{
cast::{Cast, CastTo, Caster},
fold::TypeFoldable,
interner::HasInterner,
- AdtId, BoundVar, DebruijnIndex, Scalar,
+ AdtId, DebruijnIndex, Scalar,
};
use hir_def::{
- builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, GenericDefId, TraitId,
- TypeAliasId,
+ builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, DefWithBodyId,
+ GenericDefId, TraitId, TypeAliasId,
};
use smallvec::SmallVec;
use crate::{
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
- to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, CallableSig, ConstData,
- ConstValue, GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty,
- TyDefId, TyExt, TyKind, ValueTyDefId,
+ to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
+ GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
+ ValueTyDefId,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -34,17 +34,32 @@ pub struct TyBuilder<D> {
data: D,
vec: SmallVec<[GenericArg; 2]>,
param_kinds: SmallVec<[ParamKind; 2]>,
+ parent_subst: Substitution,
}
impl<A> TyBuilder<A> {
fn with_data<B>(self, data: B) -> TyBuilder<B> {
- TyBuilder { data, param_kinds: self.param_kinds, vec: self.vec }
+ TyBuilder {
+ data,
+ vec: self.vec,
+ param_kinds: self.param_kinds,
+ parent_subst: self.parent_subst,
+ }
}
}
impl<D> TyBuilder<D> {
- fn new(data: D, param_kinds: SmallVec<[ParamKind; 2]>) -> TyBuilder<D> {
- TyBuilder { data, vec: SmallVec::with_capacity(param_kinds.len()), param_kinds }
+ fn new(
+ data: D,
+ param_kinds: SmallVec<[ParamKind; 2]>,
+ parent_subst: Option<Substitution>,
+ ) -> Self {
+ let parent_subst = parent_subst.unwrap_or_else(|| Substitution::empty(Interner));
+ Self { data, vec: SmallVec::with_capacity(param_kinds.len()), param_kinds, parent_subst }
+ }
+
+ fn new_empty(data: D) -> Self {
+ TyBuilder::new(data, SmallVec::new(), None)
}
fn build_internal(self) -> (D, Substitution) {
@@ -52,13 +67,18 @@ impl<D> TyBuilder<D> {
for (a, e) in self.vec.iter().zip(self.param_kinds.iter()) {
self.assert_match_kind(a, e);
}
- let subst = Substitution::from_iter(Interner, self.vec);
+ let subst = Substitution::from_iter(
+ Interner,
+ self.vec.into_iter().chain(self.parent_subst.iter(Interner).cloned()),
+ );
(self.data, subst)
}
pub fn push(mut self, arg: impl CastTo<GenericArg>) -> Self {
+ assert!(self.remaining() > 0);
let arg = arg.cast(Interner);
let expected_kind = &self.param_kinds[self.vec.len()];
+
let arg_kind = match arg.data(Interner) {
chalk_ir::GenericArgData::Ty(_) => ParamKind::Type,
chalk_ir::GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"),
@@ -68,7 +88,9 @@ impl<D> TyBuilder<D> {
}
};
assert_eq!(*expected_kind, arg_kind);
+
self.vec.push(arg);
+
self
}
@@ -79,20 +101,12 @@ impl<D> TyBuilder<D> {
pub fn fill_with_bound_vars(self, debruijn: DebruijnIndex, starting_from: usize) -> Self {
// self.fill is inlined to make borrow checker happy
let mut this = self;
- let other = this.param_kinds.iter().skip(this.vec.len());
+ let other = &this.param_kinds[this.vec.len()..];
let filler = (starting_from..).zip(other).map(|(idx, kind)| match kind {
- ParamKind::Type => {
- GenericArgData::Ty(TyKind::BoundVar(BoundVar::new(debruijn, idx)).intern(Interner))
- .intern(Interner)
+ ParamKind::Type => BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner),
+ ParamKind::Const(ty) => {
+ BoundVar::new(debruijn, idx).to_const(Interner, ty.clone()).cast(Interner)
}
- ParamKind::Const(ty) => GenericArgData::Const(
- ConstData {
- value: ConstValue::BoundVar(BoundVar::new(debruijn, idx)),
- ty: ty.clone(),
- }
- .intern(Interner),
- )
- .intern(Interner),
});
this.vec.extend(filler.take(this.remaining()).casted(Interner));
assert_eq!(this.remaining(), 0);
@@ -102,8 +116,8 @@ impl<D> TyBuilder<D> {
pub fn fill_with_unknown(self) -> Self {
// self.fill is inlined to make borrow checker happy
let mut this = self;
- let filler = this.param_kinds.iter().skip(this.vec.len()).map(|x| match x {
- ParamKind::Type => GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner),
+ let filler = this.param_kinds[this.vec.len()..].iter().map(|x| match x {
+ ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
});
this.vec.extend(filler.casted(Interner));
@@ -113,33 +127,17 @@ impl<D> TyBuilder<D> {
pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable<'_>) -> Self {
self.fill(|x| match x {
- ParamKind::Type => GenericArgData::Ty(table.new_type_var()).intern(Interner),
- ParamKind::Const(ty) => {
- GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner)
- }
+ ParamKind::Type => table.new_type_var().cast(Interner),
+ ParamKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner),
})
}
pub fn fill(mut self, filler: impl FnMut(&ParamKind) -> GenericArg) -> Self {
- self.vec.extend(self.param_kinds.iter().skip(self.vec.len()).map(filler));
+ self.vec.extend(self.param_kinds[self.vec.len()..].iter().map(filler));
assert_eq!(self.remaining(), 0);
self
}
- pub fn use_parent_substs(mut self, parent_substs: &Substitution) -> Self {
- assert!(self.vec.is_empty());
- assert!(parent_substs.len(Interner) <= self.param_kinds.len());
- self.extend(parent_substs.iter(Interner).cloned());
- self
- }
-
- fn extend(&mut self, it: impl Iterator<Item = GenericArg> + Clone) {
- for x in it.clone().zip(self.param_kinds.iter().skip(self.vec.len())) {
- self.assert_match_kind(&x.0, &x.1);
- }
- self.vec.extend(it);
- }
-
fn assert_match_kind(&self, a: &chalk_ir::GenericArg<Interner>, e: &ParamKind) {
match (a.data(Interner), e) {
(chalk_ir::GenericArgData::Ty(_), ParamKind::Type)
@@ -188,21 +186,42 @@ impl TyBuilder<()> {
params.placeholder_subst(db)
}
- pub fn subst_for_def(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> TyBuilder<()> {
- let def = def.into();
- let params = generics(db.upcast(), def);
- TyBuilder::new(
- (),
- params
- .iter()
- .map(|(id, data)| match data {
- TypeOrConstParamData::TypeParamData(_) => ParamKind::Type,
- TypeOrConstParamData::ConstParamData(_) => {
- ParamKind::Const(db.const_param_ty(ConstParamId::from_unchecked(id)))
- }
- })
- .collect(),
- )
+ pub fn subst_for_def(
+ db: &dyn HirDatabase,
+ def: impl Into<GenericDefId>,
+ parent_subst: Option<Substitution>,
+ ) -> TyBuilder<()> {
+ let generics = generics(db.upcast(), def.into());
+ assert!(generics.parent_generics().is_some() == parent_subst.is_some());
+ let params = generics
+ .iter_self()
+ .map(|(id, data)| match data {
+ TypeOrConstParamData::TypeParamData(_) => ParamKind::Type,
+ TypeOrConstParamData::ConstParamData(_) => {
+ ParamKind::Const(db.const_param_ty(ConstParamId::from_unchecked(id)))
+ }
+ })
+ .collect();
+ TyBuilder::new((), params, parent_subst)
+ }
+
+ /// Creates a `TyBuilder` to build `Substitution` for a generator defined in `parent`.
+ ///
+ /// A generator's substitution consists of:
+ /// - resume type of generator
+ /// - yield type of generator ([`Generator::Yield`](std::ops::Generator::Yield))
+ /// - return type of generator ([`Generator::Return`](std::ops::Generator::Return))
+ /// - generic parameters in scope on `parent`
+ /// in this order.
+ ///
+ /// This method prepopulates the builder with placeholder substitution of `parent`, so you
+ /// should only push exactly 3 `GenericArg`s before building.
+ pub fn subst_for_generator(db: &dyn HirDatabase, parent: DefWithBodyId) -> TyBuilder<()> {
+ let parent_subst =
+ parent.as_generic_def_id().map(|p| generics(db.upcast(), p).placeholder_subst(db));
+ // These represent resume type, yield type, and return type of generator.
+ let params = std::iter::repeat(ParamKind::Type).take(3).collect();
+ TyBuilder::new((), params, parent_subst)
}
pub fn build(self) -> Substitution {
@@ -213,7 +232,7 @@ impl TyBuilder<()> {
impl TyBuilder<hir_def::AdtId> {
pub fn adt(db: &dyn HirDatabase, def: hir_def::AdtId) -> TyBuilder<hir_def::AdtId> {
- TyBuilder::subst_for_def(db, def).with_data(def)
+ TyBuilder::subst_for_def(db, def, None).with_data(def)
}
pub fn fill_with_defaults(
@@ -221,16 +240,27 @@ impl TyBuilder<hir_def::AdtId> {
db: &dyn HirDatabase,
mut fallback: impl FnMut() -> Ty,
) -> Self {
+ // Note that we're building ADT, so we never have parent generic parameters.
let defaults = db.generic_defaults(self.data.into());
+ let dummy_ty = TyKind::Error.intern(Interner).cast(Interner);
for default_ty in defaults.iter().skip(self.vec.len()) {
- if let GenericArgData::Ty(x) = default_ty.skip_binders().data(Interner) {
+ // NOTE(skip_binders): we only check if the arg type is error type.
+ if let Some(x) = default_ty.skip_binders().ty(Interner) {
if x.is_unknown() {
self.vec.push(fallback().cast(Interner));
continue;
}
- };
- // each default can depend on the previous parameters
- let subst_so_far = Substitution::from_iter(Interner, self.vec.clone());
+ }
+ // Each default can only depend on the previous parameters.
+ // FIXME: we don't handle const generics here.
+ let subst_so_far = Substitution::from_iter(
+ Interner,
+ self.vec
+ .iter()
+ .cloned()
+ .chain(iter::repeat(dummy_ty.clone()))
+ .take(self.param_kinds.len()),
+ );
self.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner));
}
self
@@ -245,7 +275,7 @@ impl TyBuilder<hir_def::AdtId> {
pub struct Tuple(usize);
impl TyBuilder<Tuple> {
pub fn tuple(size: usize) -> TyBuilder<Tuple> {
- TyBuilder::new(Tuple(size), iter::repeat(ParamKind::Type).take(size).collect())
+ TyBuilder::new(Tuple(size), iter::repeat(ParamKind::Type).take(size).collect(), None)
}
pub fn build(self) -> Ty {
@@ -256,7 +286,7 @@ impl TyBuilder<Tuple> {
impl TyBuilder<TraitId> {
pub fn trait_ref(db: &dyn HirDatabase, def: TraitId) -> TyBuilder<TraitId> {
- TyBuilder::subst_for_def(db, def).with_data(def)
+ TyBuilder::subst_for_def(db, def, None).with_data(def)
}
pub fn build(self) -> TraitRef {
@@ -266,8 +296,12 @@ impl TyBuilder<TraitId> {
}
impl TyBuilder<TypeAliasId> {
- pub fn assoc_type_projection(db: &dyn HirDatabase, def: TypeAliasId) -> TyBuilder<TypeAliasId> {
- TyBuilder::subst_for_def(db, def).with_data(def)
+ pub fn assoc_type_projection(
+ db: &dyn HirDatabase,
+ def: TypeAliasId,
+ parent_subst: Option<Substitution>,
+ ) -> TyBuilder<TypeAliasId> {
+ TyBuilder::subst_for_def(db, def, parent_subst).with_data(def)
}
pub fn build(self) -> ProjectionTy {
@@ -277,19 +311,6 @@ impl TyBuilder<TypeAliasId> {
}
impl<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>> TyBuilder<Binders<T>> {
- fn subst_binders(b: Binders<T>) -> Self {
- let param_kinds = b
- .binders
- .iter(Interner)
- .map(|x| match x {
- chalk_ir::VariableKind::Ty(_) => ParamKind::Type,
- chalk_ir::VariableKind::Lifetime => panic!("Got lifetime parameter"),
- chalk_ir::VariableKind::Const(ty) => ParamKind::Const(ty.clone()),
- })
- .collect();
- TyBuilder::new(b, param_kinds)
- }
-
pub fn build(self) -> T {
let (b, subst) = self.build_internal();
b.substitute(Interner, &subst)
@@ -297,15 +318,41 @@ impl<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>> TyBuilder<Bin
}
impl TyBuilder<Binders<Ty>> {
- pub fn def_ty(db: &dyn HirDatabase, def: TyDefId) -> TyBuilder<Binders<Ty>> {
- TyBuilder::subst_binders(db.ty(def))
+ pub fn def_ty(
+ db: &dyn HirDatabase,
+ def: TyDefId,
+ parent_subst: Option<Substitution>,
+ ) -> TyBuilder<Binders<Ty>> {
+ let poly_ty = db.ty(def);
+ let id: GenericDefId = match def {
+ TyDefId::BuiltinType(_) => {
+ assert!(parent_subst.is_none());
+ return TyBuilder::new_empty(poly_ty);
+ }
+ TyDefId::AdtId(id) => id.into(),
+ TyDefId::TypeAliasId(id) => id.into(),
+ };
+ TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_ty)
}
pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> {
- TyBuilder::subst_binders(db.impl_self_ty(def))
+ TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def))
}
- pub fn value_ty(db: &dyn HirDatabase, def: ValueTyDefId) -> TyBuilder<Binders<Ty>> {
- TyBuilder::subst_binders(db.value_ty(def))
+ pub fn value_ty(
+ db: &dyn HirDatabase,
+ def: ValueTyDefId,
+ parent_subst: Option<Substitution>,
+ ) -> TyBuilder<Binders<Ty>> {
+ let poly_value_ty = db.value_ty(def);
+ let id = match def.to_generic_def_id() {
+ Some(id) => id,
+ None => {
+ // static items
+ assert!(parent_subst.is_none());
+ return TyBuilder::new_empty(poly_value_ty);
+ }
+ };
+ TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_value_ty)
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
index faec99c7d..43c3451ca 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
@@ -11,6 +11,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
use base_db::CrateId;
use hir_def::{
+ expr::Movability,
lang_item::{lang_attr, LangItemTarget},
AssocItemId, GenericDefId, HasModule, ItemContainerId, Lookup, ModuleId, TypeAliasId,
};
@@ -26,9 +27,9 @@ use crate::{
to_assoc_type_id, to_chalk_trait_id,
traits::ChalkContext,
utils::generics,
- AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, Interner, ProjectionTy,
- ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder,
- TyExt, TyKind, WhereClause,
+ wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId,
+ Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef,
+ TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause,
};
pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>;
@@ -372,17 +373,62 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
}
fn generator_datum(
&self,
- _: chalk_ir::GeneratorId<Interner>,
+ id: chalk_ir::GeneratorId<Interner>,
) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorDatum<Interner>> {
- // FIXME
- unimplemented!()
+ let (parent, expr) = self.db.lookup_intern_generator(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 generator in question.
+ let subst = TyBuilder::subst_for_generator(self.db, parent).fill_with_unknown().build();
+
+ let input_output = rust_ir::GeneratorInputOutputDatum {
+ resume_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
+ .intern(Interner),
+ yield_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 1))
+ .intern(Interner),
+ return_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 2))
+ .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::expr::Expr::Closure {
+ closure_kind: hir_def::expr::ClosureKind::Generator(movability),
+ ..
+ } => movability,
+ _ => unreachable!("non generator expression interned as generator"),
+ };
+ let movability = match movability {
+ Movability::Static => rust_ir::Movability::Static,
+ Movability::Movable => rust_ir::Movability::Movable,
+ };
+
+ Arc::new(rust_ir::GeneratorDatum { movability, input_output })
}
fn generator_witness_datum(
&self,
- _: chalk_ir::GeneratorId<Interner>,
+ id: chalk_ir::GeneratorId<Interner>,
) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<Interner>> {
- // FIXME
- unimplemented!()
+ // FIXME: calculate inner types
+ let inner_types =
+ rust_ir::GeneratorWitnessExistential { types: wrap_empty_binders(vec![]) };
+
+ let (parent, _) = self.db.lookup_intern_generator(id.into());
+ // See the comment in `generator_datum()` for unknown types.
+ let subst = TyBuilder::subst_for_generator(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::GeneratorWitnessDatum { inner_types })
}
fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> {
@@ -429,10 +475,15 @@ pub(crate) fn associated_ty_data_query(
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
let ctx = crate::TyLoweringContext::new(db, &resolver)
.with_type_param_mode(crate::lower::ParamLoweringMode::Variable);
- let pro_ty = TyBuilder::assoc_type_projection(db, type_alias)
+
+ let trait_subst = TyBuilder::subst_for_def(db, trait_, None)
+ .fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, generic_params.len_self())
+ .build();
+ let pro_ty = TyBuilder::assoc_type_projection(db, type_alias, Some(trait_subst))
.fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, 0)
.build();
let self_ty = TyKind::Alias(AliasTy::Projection(pro_ty)).intern(Interner);
+
let mut bounds: Vec<_> = type_alias_data
.bounds
.iter()
@@ -772,10 +823,10 @@ pub(super) fn generic_predicate_to_inline_bound(
Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound)))
}
WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
- if projection_ty.self_type_parameter(Interner) != self_ty_shifted_in {
+ let trait_ = projection_ty.trait_(db);
+ if projection_ty.self_type_parameter(db) != self_ty_shifted_in {
return None;
}
- let trait_ = projection_ty.trait_(db);
let args_no_self = projection_ty.substitution.as_slice(Interner)[1..]
.iter()
.map(|ty| ty.clone().cast(Interner))
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
index a9c124b42..e2099d7e5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
@@ -152,7 +152,7 @@ impl TyExt for Ty {
TyKind::FnDef(def, parameters) => {
let callable_def = db.lookup_intern_callable_def((*def).into());
let sig = db.callable_item_signature(callable_def);
- Some(sig.substitute(Interner, &parameters))
+ Some(sig.substitute(Interner, parameters))
}
TyKind::Closure(.., substs) => {
let sig_param = substs.at(Interner, 0).assert_ty_ref(Interner);
@@ -164,6 +164,10 @@ impl TyExt for Ty {
fn dyn_trait(&self) -> Option<TraitId> {
let trait_ref = match self.kind(Interner) {
+ // The principal trait bound should be the first element of the bounds. This is an
+ // invariant ensured by `TyLoweringContext::lower_dyn_trait()`.
+ // FIXME: dyn types may not have principal trait and we don't want to return auto trait
+ // here.
TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
match b.skip_binders() {
WhereClause::Implemented(trait_ref) => Some(trait_ref),
@@ -258,7 +262,7 @@ impl TyExt for Ty {
WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(proj),
ty: _,
- }) => &proj.self_type_parameter(Interner) == self,
+ }) => &proj.self_type_parameter(db) == self,
_ => false,
})
.collect::<Vec<_>>();
@@ -329,6 +333,7 @@ impl TyExt for Ty {
pub trait ProjectionTyExt {
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef;
fn trait_(&self, db: &dyn HirDatabase) -> TraitId;
+ fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty;
}
impl ProjectionTyExt for ProjectionTy {
@@ -345,6 +350,10 @@ impl ProjectionTyExt for ProjectionTy {
_ => panic!("projection ty without parent trait"),
}
}
+
+ fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty {
+ self.trait_ref(db).self_type_parameter(Interner)
+ }
}
pub trait TraitRefExt {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
index 0495a4e64..2c0c6e0b8 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -2,20 +2,22 @@
use std::{
collections::HashMap,
- convert::TryInto,
fmt::{Display, Write},
};
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar};
use hir_def::{
+ builtin_type::BuiltinInt,
expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat, PatId},
path::ModPath,
resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
+ src::HasChildSource,
type_ref::ConstScalar,
- ConstId, DefWithBodyId,
+ ConstId, DefWithBodyId, EnumVariantId, Lookup,
};
-use la_arena::{Arena, Idx};
+use la_arena::{Arena, Idx, RawIdx};
use stdx::never;
+use syntax::ast::HasName;
use crate::{
db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx,
@@ -78,6 +80,7 @@ pub enum ConstEvalError {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ComputedExpr {
Literal(Literal),
+ Enum(String, EnumVariantId, Literal),
Tuple(Box<[ComputedExpr]>),
}
@@ -105,6 +108,7 @@ impl Display for ComputedExpr {
Literal::String(x) => std::fmt::Debug::fmt(x, f),
Literal::ByteString(x) => std::fmt::Debug::fmt(x, f),
},
+ ComputedExpr::Enum(name, _, _) => name.fmt(f),
ComputedExpr::Tuple(t) => {
f.write_char('(')?;
for x in &**t {
@@ -149,13 +153,51 @@ fn is_valid(scalar: &Scalar, value: i128) -> bool {
}
}
+fn get_name(ctx: &mut ConstEvalCtx<'_>, variant: EnumVariantId) -> String {
+ let loc = variant.parent.lookup(ctx.db.upcast());
+ let children = variant.parent.child_source(ctx.db.upcast());
+ let item_tree = loc.id.item_tree(ctx.db.upcast());
+
+ let variant_name = children.value[variant.local_id].name();
+ let enum_name = item_tree[loc.id.value].name.to_string();
+ enum_name + "::" + &variant_name.unwrap().to_string()
+}
+
pub fn eval_const(
expr_id: ExprId,
ctx: &mut ConstEvalCtx<'_>,
) -> Result<ComputedExpr, ConstEvalError> {
+ let u128_to_i128 = |it: u128| -> Result<i128, ConstEvalError> {
+ it.try_into().map_err(|_| ConstEvalError::NotSupported("u128 is too big"))
+ };
+
let expr = &ctx.exprs[expr_id];
match expr {
- Expr::Missing => Err(ConstEvalError::IncompleteExpr),
+ Expr::Missing => match ctx.owner {
+ // evaluate the implicit variant index of an enum variant without expression
+ // FIXME: This should return the type of the enum representation
+ DefWithBodyId::VariantId(variant) => {
+ let prev_idx: u32 = variant.local_id.into_raw().into();
+ let prev_idx = prev_idx.checked_sub(1).map(RawIdx::from).map(Idx::from_raw);
+ let value = match prev_idx {
+ Some(local_id) => {
+ let prev_variant = EnumVariantId { local_id, parent: variant.parent };
+ 1 + match ctx.db.const_eval_variant(prev_variant)? {
+ ComputedExpr::Literal(Literal::Int(v, _)) => v,
+ ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
+ _ => {
+ return Err(ConstEvalError::NotSupported(
+ "Enum can't contain this kind of value",
+ ))
+ }
+ }
+ }
+ _ => 0,
+ };
+ Ok(ComputedExpr::Literal(Literal::Int(value, Some(BuiltinInt::I128))))
+ }
+ _ => Err(ConstEvalError::IncompleteExpr),
+ },
Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())),
&Expr::UnaryOp { expr, op } => {
let ty = &ctx.expr_ty(expr);
@@ -168,9 +210,7 @@ pub fn eval_const(
return Ok(ComputedExpr::Literal(Literal::Bool(!b)))
}
ComputedExpr::Literal(Literal::Int(v, _)) => v,
- ComputedExpr::Literal(Literal::Uint(v, _)) => v
- .try_into()
- .map_err(|_| ConstEvalError::NotSupported("too big u128"))?,
+ ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
_ => return Err(ConstEvalError::NotSupported("this kind of operator")),
};
let r = match ty.kind(Interner) {
@@ -199,9 +239,7 @@ pub fn eval_const(
hir_def::expr::UnaryOp::Neg => {
let v = match ev {
ComputedExpr::Literal(Literal::Int(v, _)) => v,
- ComputedExpr::Literal(Literal::Uint(v, _)) => v
- .try_into()
- .map_err(|_| ConstEvalError::NotSupported("too big u128"))?,
+ ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
_ => return Err(ConstEvalError::NotSupported("this kind of operator")),
};
Ok(ComputedExpr::Literal(Literal::Int(
@@ -220,16 +258,12 @@ pub fn eval_const(
let op = op.ok_or(ConstEvalError::IncompleteExpr)?;
let v1 = match lhs {
ComputedExpr::Literal(Literal::Int(v, _)) => v,
- ComputedExpr::Literal(Literal::Uint(v, _)) => {
- v.try_into().map_err(|_| ConstEvalError::NotSupported("too big u128"))?
- }
+ ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
_ => return Err(ConstEvalError::NotSupported("this kind of operator")),
};
let v2 = match rhs {
ComputedExpr::Literal(Literal::Int(v, _)) => v,
- ComputedExpr::Literal(Literal::Uint(v, _)) => {
- v.try_into().map_err(|_| ConstEvalError::NotSupported("too big u128"))?
- }
+ ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
_ => return Err(ConstEvalError::NotSupported("this kind of operator")),
};
match op {
@@ -340,9 +374,22 @@ pub fn eval_const(
ValueNs::GenericParam(_) => {
Err(ConstEvalError::NotSupported("const generic without substitution"))
}
+ ValueNs::EnumVariantId(id) => match ctx.db.const_eval_variant(id)? {
+ ComputedExpr::Literal(lit) => {
+ Ok(ComputedExpr::Enum(get_name(ctx, id), id, lit))
+ }
+ _ => Err(ConstEvalError::NotSupported(
+ "Enums can't evalute to anything but numbers",
+ )),
+ },
_ => Err(ConstEvalError::NotSupported("path that are not const or local")),
}
}
+ // FIXME: Handle the cast target
+ &Expr::Cast { expr, .. } => match eval_const(expr, ctx)? {
+ ComputedExpr::Enum(_, _, lit) => Ok(ComputedExpr::Literal(lit)),
+ _ => Err(ConstEvalError::NotSupported("Can't cast these types")),
+ },
_ => Err(ConstEvalError::NotSupported("This kind of expression")),
}
}
@@ -413,7 +460,15 @@ pub(crate) fn const_eval_recover(
Err(ConstEvalError::Loop)
}
-pub(crate) fn const_eval_query(
+pub(crate) fn const_eval_variant_recover(
+ _: &dyn HirDatabase,
+ _: &[String],
+ _: &EnumVariantId,
+) -> Result<ComputedExpr, ConstEvalError> {
+ Err(ConstEvalError::Loop)
+}
+
+pub(crate) fn const_eval_variant_query(
db: &dyn HirDatabase,
const_id: ConstId,
) -> Result<ComputedExpr, ConstEvalError> {
@@ -434,6 +489,26 @@ pub(crate) fn const_eval_query(
result
}
+pub(crate) fn const_eval_query_variant(
+ db: &dyn HirDatabase,
+ variant_id: EnumVariantId,
+) -> Result<ComputedExpr, ConstEvalError> {
+ let def = variant_id.into();
+ let body = db.body(def);
+ let infer = &db.infer(def);
+ eval_const(
+ body.body_expr,
+ &mut ConstEvalCtx {
+ db,
+ owner: def,
+ exprs: &body.exprs,
+ pats: &body.pats,
+ local_data: HashMap::default(),
+ infer,
+ },
+ )
+}
+
pub(crate) fn eval_to_const<'a>(
expr: Idx<Expr>,
mode: ParamLoweringMode,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
index 4a052851a..b76506f6e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
@@ -88,6 +88,49 @@ fn consts() {
}
#[test]
+fn enums() {
+ check_number(
+ r#"
+ enum E {
+ F1 = 1,
+ F2 = 2 * E::F1 as u8,
+ F3 = 3 * E::F2 as u8,
+ }
+ const GOAL: i32 = E::F3 as u8;
+ "#,
+ 6,
+ );
+ check_number(
+ r#"
+ enum E { F1 = 1, F2, }
+ const GOAL: i32 = E::F2 as u8;
+ "#,
+ 2,
+ );
+ check_number(
+ r#"
+ enum E { F1, }
+ const GOAL: i32 = E::F1 as u8;
+ "#,
+ 0,
+ );
+ let r = eval_goal(
+ r#"
+ enum E { A = 1, }
+ const GOAL: E = E::A;
+ "#,
+ )
+ .unwrap();
+ match r {
+ ComputedExpr::Enum(name, _, Literal::Uint(val, _)) => {
+ assert_eq!(name, "E::A");
+ assert_eq!(val, 1);
+ }
+ x => panic!("Expected enum but found {:?}", x),
+ }
+}
+
+#[test]
fn const_loop() {
check_fail(
r#"
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
index b385b1caf..932fce835 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -6,8 +6,8 @@ use std::sync::Arc;
use arrayvec::ArrayVec;
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{
- db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, FunctionId,
- GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
+ db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
+ FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
};
use la_arena::ArenaMap;
@@ -43,10 +43,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::lower::const_param_ty_query)]
fn const_param_ty(&self, def: ConstParamId) -> Ty;
- #[salsa::invoke(crate::consteval::const_eval_query)]
+ #[salsa::invoke(crate::consteval::const_eval_variant_query)]
#[salsa::cycle(crate::consteval::const_eval_recover)]
fn const_eval(&self, def: ConstId) -> Result<ComputedExpr, ConstEvalError>;
+ #[salsa::invoke(crate::consteval::const_eval_query_variant)]
+ #[salsa::cycle(crate::consteval::const_eval_variant_recover)]
+ fn const_eval_variant(&self, def: EnumVariantId) -> Result<ComputedExpr, ConstEvalError>;
+
#[salsa::invoke(crate::lower::impl_trait_query)]
fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
@@ -116,6 +120,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
#[salsa::interned]
fn intern_closure(&self, id: (DefWithBodyId, ExprId)) -> InternedClosureId;
+ #[salsa::interned]
+ fn intern_generator(&self, id: (DefWithBodyId, ExprId)) -> InternedGeneratorId;
#[salsa::invoke(chalk_db::associated_ty_data_query)]
fn associated_ty_data(&self, id: chalk_db::AssocTypeId) -> Arc<chalk_db::AssociatedTyDatum>;
@@ -150,6 +156,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
id: chalk_db::AssociatedTyValueId,
) -> Arc<chalk_db::AssociatedTyValue>;
+ #[salsa::invoke(crate::traits::normalize_projection_query)]
+ #[salsa::transparent]
+ fn normalize_projection(
+ &self,
+ projection: crate::ProjectionTy,
+ env: Arc<crate::TraitEnvironment>,
+ ) -> Ty;
+
#[salsa::invoke(trait_solve_wait)]
#[salsa::transparent]
fn trait_solve(
@@ -180,6 +194,9 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
DefWithBodyId::ConstId(it) => {
db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
}
+ DefWithBodyId::VariantId(it) => {
+ db.enum_data(it.parent).variants[it.local_id].name.to_string()
+ }
});
db.infer_query(def)
}
@@ -218,6 +235,10 @@ impl_intern_key!(InternedOpaqueTyId);
pub struct InternedClosureId(salsa::InternId);
impl_intern_key!(InternedClosureId);
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct InternedGeneratorId(salsa::InternId);
+impl_intern_key!(InternedGeneratorId);
+
/// This exists just for Chalk, because Chalk just has a single `FnDefId` where
/// we have different IDs for struct and enum variant constructors.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index 642e03edd..c8df4c796 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -159,12 +159,7 @@ impl ExprValidator {
}
let pattern_arena = Arena::new();
- let cx = MatchCheckCtx {
- module: self.owner.module(db.upcast()),
- body: self.owner,
- db,
- pattern_arena: &pattern_arena,
- };
+ let cx = MatchCheckCtx::new(self.owner.module(db.upcast()), self.owner, db, &pattern_arena);
let mut m_arms = Vec::with_capacity(arms.len());
let mut has_lowering_errors = false;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs
index bbbe539c1..47d60fc41 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs
@@ -52,7 +52,10 @@ use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId};
use smallvec::{smallvec, SmallVec};
use stdx::never;
-use crate::{infer::normalize, AdtId, Interner, Scalar, Ty, TyExt, TyKind};
+use crate::{
+ infer::normalize, inhabitedness::is_enum_variant_uninhabited_from, AdtId, Interner, Scalar, Ty,
+ TyExt, TyKind,
+};
use super::{
is_box,
@@ -557,8 +560,8 @@ impl SplitWildcard {
TyKind::Scalar(Scalar::Bool) => smallvec![make_range(0, 1, Scalar::Bool)],
// TyKind::Array(..) if ... => unhandled(),
TyKind::Array(..) | TyKind::Slice(..) => unhandled(),
- &TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ..) => {
- let enum_data = cx.db.enum_data(enum_id);
+ TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), subst) => {
+ let enum_data = cx.db.enum_data(*enum_id);
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
// additional "unknown" constructor.
@@ -591,14 +594,15 @@ impl SplitWildcard {
let mut ctors: SmallVec<[_; 1]> = enum_data
.variants
.iter()
- .filter(|&(_, _v)| {
+ .map(|(local_id, _)| EnumVariantId { parent: *enum_id, local_id })
+ .filter(|&variant| {
// If `exhaustive_patterns` is enabled, we exclude variants known to be
// uninhabited.
let is_uninhabited = is_exhaustive_pat_feature
- && unimplemented!("after MatchCheckCtx.feature_exhaustive_patterns()");
+ && is_enum_variant_uninhabited_from(variant, subst, cx.module, cx.db);
!is_uninhabited
})
- .map(|(local_id, _)| Variant(EnumVariantId { parent: enum_id, local_id }))
+ .map(Variant)
.collect();
if is_secretly_empty || is_declared_nonexhaustive {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs
index 1221327b9..c4d709a97 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs
@@ -277,7 +277,7 @@ use hir_def::{AdtId, DefWithBodyId, HasModule, ModuleId};
use smallvec::{smallvec, SmallVec};
use typed_arena::Arena;
-use crate::{db::HirDatabase, Ty, TyExt};
+use crate::{db::HirDatabase, inhabitedness::is_ty_uninhabited_from, Ty, TyExt};
use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard};
@@ -289,13 +289,27 @@ pub(crate) struct MatchCheckCtx<'a, 'p> {
pub(crate) db: &'a dyn HirDatabase,
/// Lowered patterns from arms plus generated by the check.
pub(crate) pattern_arena: &'p Arena<DeconstructedPat<'p>>,
+ exhaustive_patterns: bool,
}
impl<'a, 'p> MatchCheckCtx<'a, 'p> {
- pub(super) fn is_uninhabited(&self, _ty: &Ty) -> bool {
- // FIXME(iDawer) implement exhaustive_patterns feature. More info in:
- // Tracking issue for RFC 1872: exhaustive_patterns feature https://github.com/rust-lang/rust/issues/51085
- false
+ pub(crate) fn new(
+ module: ModuleId,
+ body: DefWithBodyId,
+ db: &'a dyn HirDatabase,
+ pattern_arena: &'p Arena<DeconstructedPat<'p>>,
+ ) -> Self {
+ let def_map = db.crate_def_map(module.krate());
+ let exhaustive_patterns = def_map.is_unstable_feature_enabled("exhaustive_patterns");
+ Self { module, body, db, pattern_arena, exhaustive_patterns }
+ }
+
+ pub(super) fn is_uninhabited(&self, ty: &Ty) -> bool {
+ if self.feature_exhaustive_patterns() {
+ is_ty_uninhabited_from(ty, self.module, self.db)
+ } else {
+ false
+ }
}
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
@@ -311,10 +325,9 @@ impl<'a, 'p> MatchCheckCtx<'a, 'p> {
}
}
- // Rust feature described as "Allows exhaustive pattern matching on types that contain uninhabited types."
+ // Rust's unstable feature described as "Allows exhaustive pattern matching on types that contain uninhabited types."
pub(super) fn feature_exhaustive_patterns(&self) -> bool {
- // FIXME see MatchCheckCtx::is_uninhabited
- false
+ self.exhaustive_patterns
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 161b19a73..431ab949b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -18,7 +18,9 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
let is_unsafe = match def {
DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
- DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
+ DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) | DefWithBodyId::VariantId(_) => {
+ false
+ }
};
if is_unsafe {
return res;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index d2f9c2b8b..0221f922f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -20,13 +20,14 @@ use hir_def::{
};
use hir_expand::{hygiene::Hygiene, name::Name};
use itertools::Itertools;
+use smallvec::SmallVec;
use syntax::SmolStr;
use crate::{
db::HirDatabase,
from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx,
mapping::from_chalk,
- primitive, subst_prefix, to_assoc_type_id,
+ primitive, to_assoc_type_id,
utils::{self, generics},
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstValue, DomainGoal,
GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, Mutability,
@@ -221,6 +222,7 @@ pub enum DisplaySourceCodeError {
PathNotFound,
UnknownType,
Closure,
+ Generator,
}
pub enum HirDisplayError {
@@ -289,7 +291,7 @@ impl HirDisplay for ProjectionTy {
let trait_ = f.db.trait_data(self.trait_(f.db));
write!(f, "<")?;
- self.self_type_parameter(Interner).hir_fmt(f)?;
+ self.self_type_parameter(f.db).hir_fmt(f)?;
write!(f, " as {}", trait_.name)?;
if self.substitution.len(Interner) > 1 {
write!(f, "<")?;
@@ -504,8 +506,15 @@ impl HirDisplay for Ty {
let total_len = parent_params + self_param + type_params + const_params;
// We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
if total_len > 0 {
+ // `parameters` are in the order of fn's params (including impl traits),
+ // parent's params (those from enclosing impl or trait, if any).
+ let parameters = parameters.as_slice(Interner);
+ let fn_params_len = self_param + type_params + const_params;
+ let fn_params = parameters.get(..fn_params_len);
+ let parent_params = parameters.get(parameters.len() - parent_params..);
+ let params = parent_params.into_iter().chain(fn_params).flatten();
write!(f, "<")?;
- f.write_joined(&parameters.as_slice(Interner)[..total_len], ", ")?;
+ f.write_joined(params, ", ")?;
write!(f, ">")?;
}
}
@@ -533,6 +542,7 @@ impl HirDisplay for Ty {
f.db.upcast(),
ItemInNs::Types((*def_id).into()),
module_id,
+ false,
) {
write!(f, "{}", path)?;
} else {
@@ -576,9 +586,8 @@ impl HirDisplay for Ty {
Some(x) => x,
None => return true,
};
- let actual_default = default_parameter
- .clone()
- .substitute(Interner, &subst_prefix(parameters, i));
+ let actual_default =
+ default_parameter.clone().substitute(Interner, &parameters);
parameter != &actual_default
}
let mut default_from = 0;
@@ -722,7 +731,7 @@ impl HirDisplay for Ty {
WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(proj),
ty: _,
- }) => &proj.self_type_parameter(Interner) == self,
+ }) => &proj.self_type_parameter(f.db) == self,
_ => false,
})
.collect::<Vec<_>>();
@@ -742,9 +751,19 @@ impl HirDisplay for Ty {
}
TyKind::BoundVar(idx) => idx.hir_fmt(f)?,
TyKind::Dyn(dyn_ty) => {
+ // Reorder bounds to satisfy `write_bounds_like_dyn_trait()`'s expectation.
+ // FIXME: `Iterator::partition_in_place()` or `Vec::drain_filter()` may make it
+ // more efficient when either of them hits stable.
+ let mut bounds: SmallVec<[_; 4]> =
+ dyn_ty.bounds.skip_binders().iter(Interner).cloned().collect();
+ let (auto_traits, others): (SmallVec<[_; 4]>, _) =
+ bounds.drain(1..).partition(|b| b.skip_binders().trait_id().is_some());
+ bounds.extend(others);
+ bounds.extend(auto_traits);
+
write_bounds_like_dyn_trait_with_prefix(
"dyn",
- dyn_ty.bounds.skip_binders().interned(),
+ &bounds,
SizedByDefault::NotSized,
f,
)?;
@@ -782,7 +801,34 @@ impl HirDisplay for Ty {
write!(f, "{{unknown}}")?;
}
TyKind::InferenceVar(..) => write!(f, "_")?,
- TyKind::Generator(..) => write!(f, "{{generator}}")?,
+ TyKind::Generator(_, subst) => {
+ if f.display_target.is_source_code() {
+ return Err(HirDisplayError::DisplaySourceCodeError(
+ DisplaySourceCodeError::Generator,
+ ));
+ }
+
+ let subst = subst.as_slice(Interner);
+ let a: Option<SmallVec<[&Ty; 3]>> = subst
+ .get(subst.len() - 3..)
+ .map(|args| args.iter().map(|arg| arg.ty(Interner)).collect())
+ .flatten();
+
+ if let Some([resume_ty, yield_ty, ret_ty]) = a.as_deref() {
+ write!(f, "|")?;
+ resume_ty.hir_fmt(f)?;
+ write!(f, "|")?;
+
+ write!(f, " yields ")?;
+ yield_ty.hir_fmt(f)?;
+
+ write!(f, " -> ")?;
+ ret_ty.hir_fmt(f)?;
+ } else {
+ // This *should* be unreachable, but fallback just in case.
+ write!(f, "{{generator}}")?;
+ }
+ }
TyKind::GeneratorWitness(..) => write!(f, "{{generator witness}}")?,
}
Ok(())
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 46eeea0e6..0efff651c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -2,7 +2,7 @@
//! the type of each expression and pattern.
//!
//! For type inference, compare the implementations in rustc (the various
-//! check_* methods in librustc_typeck/check/mod.rs are a good entry point) and
+//! check_* methods in rustc_hir_analysis/check/mod.rs are a good entry point) and
//! IntelliJ-Rust (org.rust.lang.core.types.infer). Our entry point for
//! inference here is the `infer` function, which infers the types of all
//! expressions in a given function.
@@ -19,14 +19,15 @@ use std::sync::Arc;
use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
use hir_def::{
body::Body,
+ builtin_type::BuiltinType,
data::{ConstData, StaticData},
expr::{BindingAnnotation, ExprId, PatId},
lang_item::LangItemTarget,
path::{path, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::TypeRef,
- AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup,
- TraitId, TypeAliasId, VariantId,
+ AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
+ ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
};
use hir_expand::name::{name, Name};
use itertools::Either;
@@ -67,6 +68,12 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)),
DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
+ DefWithBodyId::VariantId(v) => {
+ ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() {
+ Either::Left(builtin) => BuiltinType::Int(builtin),
+ Either::Right(builtin) => BuiltinType::Uint(builtin),
+ });
+ }
}
ctx.infer_body();
@@ -182,7 +189,7 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum InferenceDiagnostic {
NoSuchField { expr: ExprId },
- BreakOutsideOfLoop { expr: ExprId },
+ BreakOutsideOfLoop { expr: ExprId, is_break: bool },
MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
}
@@ -332,7 +339,7 @@ pub struct InferenceResult {
/// unresolved or missing subpatterns or subpatterns of mismatched types.
pub type_of_pat: ArenaMap<PatId, Ty>,
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
- /// Interned Unknown to return references to.
+ /// Interned common types to return references to.
standard_types: InternedStandardTypes,
/// Stores the types which were implicitly dereferenced in pattern binding modes.
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
@@ -412,24 +419,53 @@ pub(crate) struct InferenceContext<'a> {
/// closures, but currently this is the only field that will change there,
/// so it doesn't make sense.
return_ty: Ty,
+ /// The resume type and the yield type, respectively, of the generator being inferred.
+ resume_yield_tys: Option<(Ty, Ty)>,
diverges: Diverges,
breakables: Vec<BreakableContext>,
}
#[derive(Clone, Debug)]
struct BreakableContext {
+ /// Whether this context contains at least one break expression.
may_break: bool,
+ /// The coercion target of the context.
coerce: CoerceMany,
+ /// The optional label of the context.
label: Option<name::Name>,
+ kind: BreakableKind,
+}
+
+#[derive(Clone, Debug)]
+enum BreakableKind {
+ Block,
+ Loop,
+ /// A border is something like an async block, closure etc. Anything that prevents
+ /// breaking/continuing through
+ Border,
}
fn find_breakable<'c>(
ctxs: &'c mut [BreakableContext],
label: Option<&name::Name>,
) -> Option<&'c mut BreakableContext> {
+ let mut ctxs = ctxs
+ .iter_mut()
+ .rev()
+ .take_while(|it| matches!(it.kind, BreakableKind::Block | BreakableKind::Loop));
+ match label {
+ Some(_) => ctxs.find(|ctx| ctx.label.as_ref() == label),
+ None => ctxs.find(|ctx| matches!(ctx.kind, BreakableKind::Loop)),
+ }
+}
+
+fn find_continuable<'c>(
+ ctxs: &'c mut [BreakableContext],
+ label: Option<&name::Name>,
+) -> Option<&'c mut BreakableContext> {
match label {
- Some(_) => ctxs.iter_mut().rev().find(|ctx| ctx.label.as_ref() == label),
- None => ctxs.last_mut(),
+ Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)),
+ None => find_breakable(ctxs, label),
}
}
@@ -449,6 +485,7 @@ impl<'a> InferenceContext<'a> {
table: unify::InferenceTable::new(db, trait_env.clone()),
trait_env,
return_ty: TyKind::Error.intern(Interner), // set in collect_fn_signature
+ resume_yield_tys: None,
db,
owner,
body,
@@ -646,10 +683,6 @@ impl<'a> InferenceContext<'a> {
)
}
- fn resolve_obligations_as_possible(&mut self) {
- self.table.resolve_obligations_as_possible();
- }
-
fn push_obligation(&mut self, o: DomainGoal) {
self.table.register_obligation(o.cast(Interner));
}
@@ -669,7 +702,6 @@ impl<'a> InferenceContext<'a> {
}
fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty {
- self.resolve_obligations_as_possible();
self.table.resolve_ty_shallow(ty)
}
@@ -681,6 +713,8 @@ impl<'a> InferenceContext<'a> {
&mut self,
inner_ty: Ty,
assoc_ty: Option<TypeAliasId>,
+ // FIXME(GATs): these are args for the trait ref, args for assoc type itself should be
+ // handled when we support them.
params: &[GenericArg],
) -> Ty {
match assoc_ty {
@@ -734,6 +768,7 @@ impl<'a> InferenceContext<'a> {
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
return (ty, Some(strukt.into()));
}
+ ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None),
_ => return (self.err_ty(), None),
},
Some(ResolveValueResult::Partial(typens, unresolved)) => (typens, Some(unresolved)),
@@ -771,7 +806,18 @@ impl<'a> InferenceContext<'a> {
self.resolve_variant_on_alias(ty, unresolved, path)
}
TypeNs::TypeAliasId(it) => {
- let ty = TyBuilder::def_ty(self.db, it.into())
+ let container = it.lookup(self.db.upcast()).container;
+ let parent_subst = match container {
+ ItemContainerId::TraitId(id) => {
+ let subst = TyBuilder::subst_for_def(self.db, id, None)
+ .fill_with_inference_vars(&mut self.table)
+ .build();
+ Some(subst)
+ }
+ // Type aliases do not exist in impls.
+ _ => None,
+ };
+ let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst)
.fill_with_inference_vars(&mut self.table)
.build();
self.resolve_variant_on_alias(ty, unresolved, path)
@@ -850,6 +896,12 @@ impl<'a> InferenceContext<'a> {
fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
let path = path![core::iter::IntoIterator];
let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
+ self.db.trait_data(trait_).associated_type_by_name(&name![IntoIter])
+ }
+
+ fn resolve_iterator_item(&self) -> Option<TypeAliasId> {
+ let path = path![core::iter::Iterator];
+ let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
self.db.trait_data(trait_).associated_type_by_name(&name![Item])
}
@@ -875,7 +927,10 @@ impl<'a> InferenceContext<'a> {
}
fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
- let trait_ = self.resolve_lang_item(name![future_trait])?.as_trait()?;
+ let trait_ = self
+ .resolver
+ .resolve_known_trait(self.db.upcast(), &path![core::future::IntoFuture])
+ .or_else(|| self.resolve_lang_item(name![future_trait])?.as_trait())?;
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
index 3ead92909..094e460db 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
@@ -12,6 +12,7 @@ use crate::{
use super::{Expectation, InferenceContext};
impl InferenceContext<'_> {
+ // This function handles both closures and generators.
pub(super) fn deduce_closure_type_from_expectations(
&mut self,
closure_expr: ExprId,
@@ -27,6 +28,11 @@ impl InferenceContext<'_> {
// Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here.
let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty);
+ // Generators are not Fn* so return early.
+ if matches!(closure_ty.kind(Interner), TyKind::Generator(..)) {
+ return;
+ }
+
// Deduction based on the expected `dyn Fn` is done separately.
if let TyKind::Dyn(dyn_ty) = expected_ty.kind(Interner) {
if let Some(sig) = self.deduce_sig_from_dyn_ty(dyn_ty) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
index f54440bf5..8df25c83c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
@@ -3,7 +3,7 @@
//! like going from `&Vec<T>` to `&[T]`.
//!
//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
-//! `librustc_typeck/check/coercion.rs`.
+//! `rustc_hir_analysis/check/coercion.rs`.
use std::{iter, sync::Arc};
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index d164e64a8..f56108b26 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -10,25 +10,28 @@ use chalk_ir::{
cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
};
use hir_def::{
- expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, Ordering, Statement, UnaryOp},
+ expr::{
+ ArithOp, Array, BinaryOp, ClosureKind, CmpOp, Expr, ExprId, LabelId, Literal, Statement,
+ UnaryOp,
+ },
generics::TypeOrConstParamData,
path::{GenericArg, GenericArgs},
resolver::resolver_for_expr,
- ConstParamId, FieldId, FunctionId, ItemContainerId, Lookup,
+ ConstParamId, FieldId, ItemContainerId, Lookup,
};
-use hir_expand::name::{name, Name};
+use hir_expand::name::Name;
use stdx::always;
use syntax::ast::RangeOp;
use crate::{
autoderef::{self, Autoderef},
consteval,
- infer::coerce::CoerceMany,
+ infer::{coerce::CoerceMany, find_continuable, BreakableKind},
lower::{
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
},
mapping::{from_chalk, ToChalk},
- method_resolution::{self, VisibleFromModule},
+ method_resolution::{self, lang_names_for_bin_op, VisibleFromModule},
primitive::{self, UintTy},
static_lifetime, to_chalk_trait_id,
utils::{generics, Generics},
@@ -120,32 +123,37 @@ impl<'a> InferenceContext<'a> {
let ty = match label {
Some(_) => {
let break_ty = self.table.new_type_var();
- self.breakables.push(BreakableContext {
- may_break: false,
- coerce: CoerceMany::new(break_ty.clone()),
- label: label.map(|label| self.body[label].name.clone()),
- });
- let ty = self.infer_block(
- tgt_expr,
- statements,
- *tail,
- &Expectation::has_type(break_ty),
+ let (breaks, ty) = self.with_breakable_ctx(
+ BreakableKind::Block,
+ break_ty.clone(),
+ *label,
+ |this| {
+ this.infer_block(
+ tgt_expr,
+ statements,
+ *tail,
+ &Expectation::has_type(break_ty),
+ )
+ },
);
- let ctxt = self.breakables.pop().expect("breakable stack broken");
- if ctxt.may_break {
- ctxt.coerce.complete()
- } else {
- ty
- }
+ breaks.unwrap_or(ty)
}
None => self.infer_block(tgt_expr, statements, *tail, expected),
};
self.resolver = old_resolver;
ty
}
- Expr::Unsafe { body } | Expr::Const { body } => self.infer_expr(*body, expected),
+ Expr::Unsafe { body } => self.infer_expr(*body, expected),
+ Expr::Const { body } => {
+ self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
+ this.infer_expr(*body, expected)
+ })
+ .1
+ }
Expr::TryBlock { body } => {
- let _inner = self.infer_expr(*body, expected);
+ self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
+ let _inner = this.infer_expr(*body, expected);
+ });
// FIXME should be std::result::Result<{inner}, _>
self.err_ty()
}
@@ -154,7 +162,10 @@ impl<'a> InferenceContext<'a> {
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
- let inner_ty = self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty));
+ let (_, inner_ty) =
+ self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
+ this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty))
+ });
self.diverges = prev_diverges;
self.return_ty = prev_ret_ty;
@@ -166,59 +177,51 @@ impl<'a> InferenceContext<'a> {
TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty))
.intern(Interner)
}
- Expr::Loop { body, label } => {
- self.breakables.push(BreakableContext {
- may_break: false,
- coerce: CoerceMany::new(self.table.new_type_var()),
- label: label.map(|label| self.body[label].name.clone()),
- });
- self.infer_expr(*body, &Expectation::has_type(TyBuilder::unit()));
-
- let ctxt = self.breakables.pop().expect("breakable stack broken");
+ &Expr::Loop { body, label } => {
+ let ty = self.table.new_type_var();
+ let (breaks, ()) =
+ self.with_breakable_ctx(BreakableKind::Loop, ty, label, |this| {
+ this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
+ });
- if ctxt.may_break {
- self.diverges = Diverges::Maybe;
- ctxt.coerce.complete()
- } else {
- TyKind::Never.intern(Interner)
+ match breaks {
+ Some(breaks) => {
+ self.diverges = Diverges::Maybe;
+ breaks
+ }
+ None => TyKind::Never.intern(Interner),
}
}
- Expr::While { condition, body, label } => {
- self.breakables.push(BreakableContext {
- may_break: false,
- coerce: CoerceMany::new(self.err_ty()),
- label: label.map(|label| self.body[label].name.clone()),
+ &Expr::While { condition, body, label } => {
+ self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
+ this.infer_expr(
+ condition,
+ &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
+ );
+ this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
});
- self.infer_expr(
- *condition,
- &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
- );
- self.infer_expr(*body, &Expectation::has_type(TyBuilder::unit()));
- let _ctxt = self.breakables.pop().expect("breakable stack broken");
+
// the body may not run, so it diverging doesn't mean we diverge
self.diverges = Diverges::Maybe;
TyBuilder::unit()
}
- Expr::For { iterable, body, pat, label } => {
- let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
-
- self.breakables.push(BreakableContext {
- may_break: false,
- coerce: CoerceMany::new(self.err_ty()),
- label: label.map(|label| self.body[label].name.clone()),
- });
- let pat_ty =
+ &Expr::For { iterable, body, pat, label } => {
+ let iterable_ty = self.infer_expr(iterable, &Expectation::none());
+ let into_iter_ty =
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
+ let pat_ty =
+ self.resolve_associated_type(into_iter_ty, self.resolve_iterator_item());
- self.infer_pat(*pat, &pat_ty, BindingMode::default());
+ self.infer_pat(pat, &pat_ty, BindingMode::default());
+ self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
+ this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
+ });
- self.infer_expr(*body, &Expectation::has_type(TyBuilder::unit()));
- let _ctxt = self.breakables.pop().expect("breakable stack broken");
// the body may not run, so it diverging doesn't mean we diverge
self.diverges = Diverges::Maybe;
TyBuilder::unit()
}
- Expr::Closure { body, args, ret_type, arg_types } => {
+ Expr::Closure { body, args, ret_type, arg_types, closure_kind } => {
assert_eq!(args.len(), arg_types.len());
let mut sig_tys = Vec::new();
@@ -246,20 +249,40 @@ impl<'a> InferenceContext<'a> {
),
})
.intern(Interner);
- let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
- let closure_ty =
- TyKind::Closure(closure_id, Substitution::from1(Interner, sig_ty.clone()))
- .intern(Interner);
+
+ let (ty, resume_yield_tys) = if matches!(closure_kind, ClosureKind::Generator(_)) {
+ // FIXME: report error when there are more than 1 parameter.
+ let resume_ty = match sig_tys.first() {
+ // When `sig_tys.len() == 1` the first type is the return type, not the
+ // first parameter type.
+ Some(ty) if sig_tys.len() > 1 => ty.clone(),
+ _ => self.result.standard_types.unit.clone(),
+ };
+ let yield_ty = self.table.new_type_var();
+
+ let subst = TyBuilder::subst_for_generator(self.db, self.owner)
+ .push(resume_ty.clone())
+ .push(yield_ty.clone())
+ .push(ret_ty.clone())
+ .build();
+
+ let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into();
+ let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner);
+
+ (generator_ty, Some((resume_ty, yield_ty)))
+ } else {
+ let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
+ let closure_ty =
+ TyKind::Closure(closure_id, Substitution::from1(Interner, sig_ty.clone()))
+ .intern(Interner);
+
+ (closure_ty, None)
+ };
// Eagerly try to relate the closure type with the expected
// type, otherwise we often won't have enough information to
// infer the body.
- self.deduce_closure_type_from_expectations(
- tgt_expr,
- &closure_ty,
- &sig_ty,
- expected,
- );
+ self.deduce_closure_type_from_expectations(tgt_expr, &ty, &sig_ty, expected);
// Now go through the argument patterns
for (arg_pat, arg_ty) in args.iter().zip(sig_tys) {
@@ -268,13 +291,18 @@ impl<'a> InferenceContext<'a> {
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
+ let prev_resume_yield_tys =
+ mem::replace(&mut self.resume_yield_tys, resume_yield_tys);
- self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty));
+ self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
+ this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty));
+ });
self.diverges = prev_diverges;
self.return_ty = prev_ret_ty;
+ self.resume_yield_tys = prev_resume_yield_tys;
- closure_ty
+ ty
}
Expr::Call { callee, args, .. } => {
let callee_ty = self.infer_expr(*callee, &Expectation::none());
@@ -372,37 +400,45 @@ impl<'a> InferenceContext<'a> {
let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or_else(|| self.err_ty())
}
- Expr::Continue { .. } => TyKind::Never.intern(Interner),
- Expr::Break { expr, label } => {
- let mut coerce = match find_breakable(&mut self.breakables, label.as_ref()) {
- Some(ctxt) => {
- // avoiding the borrowck
- mem::replace(
- &mut ctxt.coerce,
- CoerceMany::new(self.result.standard_types.unknown.clone()),
- )
- }
- None => CoerceMany::new(self.result.standard_types.unknown.clone()),
+ Expr::Continue { label } => {
+ if let None = find_continuable(&mut self.breakables, label.as_ref()) {
+ self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
+ expr: tgt_expr,
+ is_break: false,
+ });
};
-
+ TyKind::Never.intern(Interner)
+ }
+ Expr::Break { expr, label } => {
let val_ty = if let Some(expr) = *expr {
self.infer_expr(expr, &Expectation::none())
} else {
TyBuilder::unit()
};
- // FIXME: create a synthetic `()` during lowering so we have something to refer to here?
- coerce.coerce(self, *expr, &val_ty);
+ match find_breakable(&mut self.breakables, label.as_ref()) {
+ Some(ctxt) => {
+ // avoiding the borrowck
+ let mut coerce = mem::replace(
+ &mut ctxt.coerce,
+ CoerceMany::new(self.result.standard_types.unknown.clone()),
+ );
- if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
- ctxt.coerce = coerce;
- ctxt.may_break = true;
- } else {
- self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
- expr: tgt_expr,
- });
- };
+ // FIXME: create a synthetic `()` during lowering so we have something to refer to here?
+ coerce.coerce(self, *expr, &val_ty);
+ let ctxt = find_breakable(&mut self.breakables, label.as_ref())
+ .expect("breakable stack changed during coercion");
+ ctxt.coerce = coerce;
+ ctxt.may_break = true;
+ }
+ None => {
+ self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
+ expr: tgt_expr,
+ is_break: true,
+ });
+ }
+ }
TyKind::Never.intern(Interner)
}
Expr::Return { expr } => {
@@ -415,11 +451,18 @@ impl<'a> InferenceContext<'a> {
TyKind::Never.intern(Interner)
}
Expr::Yield { expr } => {
- // FIXME: track yield type for coercion
- if let Some(expr) = expr {
- self.infer_expr(*expr, &Expectation::none());
+ if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
+ if let Some(expr) = expr {
+ self.infer_expr_coerce(*expr, &Expectation::has_type(yield_ty));
+ } else {
+ let unit = self.result.standard_types.unit.clone();
+ let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty);
+ }
+ resume_ty
+ } else {
+ // FIXME: report error (yield expr in non-generator)
+ TyKind::Error.intern(Interner)
}
- TyKind::Never.intern(Interner)
}
Expr::RecordLit { path, fields, spread, .. } => {
let (ty, def_id) = self.resolve_variant(path.as_deref(), false);
@@ -794,9 +837,6 @@ impl<'a> InferenceContext<'a> {
None => self.table.new_float_var(),
},
},
- Expr::MacroStmts { tail, statements } => {
- self.infer_block(tgt_expr, statements, *tail, expected)
- }
Expr::Underscore => {
// Underscore expressions may only appear in assignee expressions,
// which are handled by `infer_assignee_expr()`, so any underscore
@@ -947,9 +987,13 @@ impl<'a> InferenceContext<'a> {
let lhs_ty = self.infer_expr(lhs, &lhs_expectation);
let rhs_ty = self.table.new_type_var();
- let func = self.resolve_binop_method(op);
- let func = match func {
- Some(func) => func,
+ let trait_func = lang_names_for_bin_op(op).and_then(|(name, lang_item)| {
+ let trait_id = self.resolve_lang_item(lang_item)?.as_trait()?;
+ let func = self.db.trait_data(trait_id).method_by_name(&name)?;
+ Some((trait_id, func))
+ });
+ let (trait_, func) = match trait_func {
+ Some(it) => it,
None => {
let rhs_ty = self.builtin_binary_op_rhs_expectation(op, lhs_ty.clone());
let rhs_ty = self.infer_expr_coerce(rhs, &Expectation::from_option(rhs_ty));
@@ -959,7 +1003,9 @@ impl<'a> InferenceContext<'a> {
}
};
- let subst = TyBuilder::subst_for_def(self.db, func)
+ // HACK: We can use this substitution for the function because the function itself doesn't
+ // have its own generic parameters.
+ let subst = TyBuilder::subst_for_def(self.db, trait_, None)
.push(lhs_ty.clone())
.push(rhs_ty.clone())
.build();
@@ -1238,19 +1284,7 @@ impl<'a> InferenceContext<'a> {
assert_eq!(self_params, 0); // method shouldn't have another Self param
let total_len = parent_params + type_params + const_params + impl_trait_params;
let mut substs = Vec::with_capacity(total_len);
- // Parent arguments are unknown
- for (id, param) in def_generics.iter_parent() {
- match param {
- TypeOrConstParamData::TypeParamData(_) => {
- substs.push(GenericArgData::Ty(self.table.new_type_var()).intern(Interner));
- }
- TypeOrConstParamData::ConstParamData(_) => {
- let ty = self.db.const_param_ty(ConstParamId::from_unchecked(id));
- substs
- .push(GenericArgData::Const(self.table.new_const_var(ty)).intern(Interner));
- }
- }
- }
+
// handle provided arguments
if let Some(generic_args) = generic_args {
// if args are provided, it should be all of them, but we can't rely on that
@@ -1259,7 +1293,7 @@ impl<'a> InferenceContext<'a> {
.iter()
.filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
.take(type_params + const_params)
- .zip(def_generics.iter_id().skip(parent_params))
+ .zip(def_generics.iter_id())
{
if let Some(g) = generic_arg_to_chalk(
self.db,
@@ -1283,6 +1317,9 @@ impl<'a> InferenceContext<'a> {
}
}
};
+
+ // Handle everything else as unknown. This also handles generic arguments for the method's
+ // parent (impl or trait), which should come after those for the method.
for (id, data) in def_generics.iter().skip(substs.len()) {
match data {
TypeOrConstParamData::TypeParamData(_) => {
@@ -1320,9 +1357,13 @@ impl<'a> InferenceContext<'a> {
CallableDefId::FunctionId(f) => {
if let ItemContainerId::TraitId(trait_) = f.lookup(self.db.upcast()).container {
// construct a TraitRef
- let substs = crate::subst_prefix(
- &*parameters,
- generics(self.db.upcast(), trait_.into()).len(),
+ let params_len = parameters.len(Interner);
+ let trait_params_len = generics(self.db.upcast(), trait_.into()).len();
+ let substs = Substitution::from_iter(
+ Interner,
+ // The generic parameters for the trait come after those for the
+ // function.
+ &parameters.as_slice(Interner)[params_len - trait_params_len..],
);
self.push_obligation(
TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs }
@@ -1474,54 +1515,19 @@ impl<'a> InferenceContext<'a> {
})
}
- fn resolve_binop_method(&self, op: BinaryOp) -> Option<FunctionId> {
- let (name, lang_item) = match op {
- BinaryOp::LogicOp(_) => return None,
- BinaryOp::ArithOp(aop) => match aop {
- ArithOp::Add => (name!(add), name!(add)),
- ArithOp::Mul => (name!(mul), name!(mul)),
- ArithOp::Sub => (name!(sub), name!(sub)),
- ArithOp::Div => (name!(div), name!(div)),
- ArithOp::Rem => (name!(rem), name!(rem)),
- ArithOp::Shl => (name!(shl), name!(shl)),
- ArithOp::Shr => (name!(shr), name!(shr)),
- ArithOp::BitXor => (name!(bitxor), name!(bitxor)),
- ArithOp::BitOr => (name!(bitor), name!(bitor)),
- ArithOp::BitAnd => (name!(bitand), name!(bitand)),
- },
- BinaryOp::Assignment { op: Some(aop) } => match aop {
- ArithOp::Add => (name!(add_assign), name!(add_assign)),
- ArithOp::Mul => (name!(mul_assign), name!(mul_assign)),
- ArithOp::Sub => (name!(sub_assign), name!(sub_assign)),
- ArithOp::Div => (name!(div_assign), name!(div_assign)),
- ArithOp::Rem => (name!(rem_assign), name!(rem_assign)),
- ArithOp::Shl => (name!(shl_assign), name!(shl_assign)),
- ArithOp::Shr => (name!(shr_assign), name!(shr_assign)),
- ArithOp::BitXor => (name!(bitxor_assign), name!(bitxor_assign)),
- ArithOp::BitOr => (name!(bitor_assign), name!(bitor_assign)),
- ArithOp::BitAnd => (name!(bitand_assign), name!(bitand_assign)),
- },
- BinaryOp::CmpOp(cop) => match cop {
- CmpOp::Eq { negated: false } => (name!(eq), name!(eq)),
- CmpOp::Eq { negated: true } => (name!(ne), name!(eq)),
- CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
- (name!(le), name!(partial_ord))
- }
- CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
- (name!(lt), name!(partial_ord))
- }
- CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
- (name!(ge), name!(partial_ord))
- }
- CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
- (name!(gt), name!(partial_ord))
- }
- },
- BinaryOp::Assignment { op: None } => return None,
- };
-
- let trait_ = self.resolve_lang_item(lang_item)?.as_trait()?;
-
- self.db.trait_data(trait_).method_by_name(&name)
+ fn with_breakable_ctx<T>(
+ &mut self,
+ kind: BreakableKind,
+ ty: Ty,
+ label: Option<LabelId>,
+ cb: impl FnOnce(&mut Self) -> T,
+ ) -> (Option<Ty>, T) {
+ self.breakables.push({
+ let label = label.map(|label| self.body[label].name.clone());
+ BreakableContext { kind, may_break: false, coerce: CoerceMany::new(ty), label }
+ });
+ let res = cb(self);
+ let ctx = self.breakables.pop().expect("breakable stack broken");
+ (ctx.may_break.then(|| ctx.coerce.complete()), res)
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index 5e7320a5d..53259d66d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -14,8 +14,9 @@ use crate::{
consteval::intern_const_scalar,
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
lower::lower_to_chalk_mutability,
- static_lifetime, ConcreteConst, ConstValue, Interner, Substitution, Ty, TyBuilder, TyExt,
- TyKind,
+ primitive::UintTy,
+ static_lifetime, ConcreteConst, ConstValue, Interner, Scalar, Substitution, Ty, TyBuilder,
+ TyExt, TyKind,
};
use super::PatLike;
@@ -294,7 +295,29 @@ impl<'a> InferenceContext<'a> {
let start_ty = self.infer_expr(*start, &Expectation::has_type(expected.clone()));
self.infer_expr(*end, &Expectation::has_type(start_ty))
}
- Pat::Lit(expr) => self.infer_expr(*expr, &Expectation::has_type(expected.clone())),
+ &Pat::Lit(expr) => {
+ // FIXME: using `Option` here is a workaround until we can use if-let chains in stable.
+ let mut pat_ty = None;
+
+ // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`.
+ if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] {
+ if let Some((inner, ..)) = expected.as_reference() {
+ let inner = self.resolve_ty_shallow(inner);
+ if matches!(inner.kind(Interner), TyKind::Slice(_)) {
+ let elem_ty = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);
+ let slice_ty = TyKind::Slice(elem_ty).intern(Interner);
+ let ty = TyKind::Ref(Mutability::Not, static_lifetime(), slice_ty)
+ .intern(Interner);
+ self.write_expr_ty(expr, ty.clone());
+ pat_ty = Some(ty);
+ }
+ }
+ }
+
+ pat_ty.unwrap_or_else(|| {
+ self.infer_expr(expr, &Expectation::has_type(expected.clone()))
+ })
+ }
Pat::Box { inner } => match self.resolve_boxed_box() {
Some(box_adt) => {
let (inner_ty, alloc_ty) = match expected.as_adt() {
@@ -343,7 +366,9 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
// FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented.
Pat::Path(..) => true,
Pat::ConstBlock(..) => true,
- Pat::Lit(expr) => !matches!(body[*expr], Expr::Literal(Literal::String(..))),
+ Pat::Lit(expr) => {
+ !matches!(body[*expr], Expr::Literal(Literal::String(..) | Literal::ByteString(..)))
+ }
Pat::Bind {
mode: BindingAnnotation::Mutable | BindingAnnotation::Unannotated,
subpat: Some(subpat),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index f580e09e9..7a4754cdc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -12,8 +12,8 @@ use crate::{
builder::ParamKind,
consteval,
method_resolution::{self, VisibleFromModule},
- GenericArgData, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
- ValueTyDefId,
+ utils::generics,
+ Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId,
};
use super::{ExprOrPatId, InferenceContext, TraitRef};
@@ -96,17 +96,21 @@ impl<'a> InferenceContext<'a> {
ValueNs::GenericParam(it) => return Some(self.db.const_param_ty(it)),
};
- let parent_substs = self_subst.unwrap_or_else(|| Substitution::empty(Interner));
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
let substs = ctx.substs_from_path(path, typable, true);
- let mut it = substs.as_slice(Interner)[parent_substs.len(Interner)..].iter().cloned();
- let ty = TyBuilder::value_ty(self.db, typable)
- .use_parent_substs(&parent_substs)
+ let substs = substs.as_slice(Interner);
+ let parent_substs = self_subst.or_else(|| {
+ let generics = generics(self.db.upcast(), typable.to_generic_def_id()?);
+ let parent_params_len = generics.parent_generics()?.len();
+ let parent_args = &substs[substs.len() - parent_params_len..];
+ Some(Substitution::from_iter(Interner, parent_args))
+ });
+ let parent_substs_len = parent_substs.as_ref().map_or(0, |s| s.len(Interner));
+ let mut it = substs.iter().take(substs.len() - parent_substs_len).cloned();
+ let ty = TyBuilder::value_ty(self.db, typable, parent_substs)
.fill(|x| {
it.next().unwrap_or_else(|| match x {
- ParamKind::Type => {
- GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
- }
+ ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()),
})
})
@@ -249,7 +253,7 @@ impl<'a> InferenceContext<'a> {
};
let substs = match container {
ItemContainerId::ImplId(impl_id) => {
- let impl_substs = TyBuilder::subst_for_def(self.db, impl_id)
+ let impl_substs = TyBuilder::subst_for_def(self.db, impl_id, None)
.fill_with_inference_vars(&mut self.table)
.build();
let impl_self_ty =
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index e77b55670..b00e3216b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -4,7 +4,7 @@ use std::{fmt, mem, sync::Arc};
use chalk_ir::{
cast::Cast, fold::TypeFoldable, interner::HasInterner, zip::Zip, CanonicalVarKind, FloatTy,
- IntTy, NoSolution, TyVariableKind, UniverseIndex,
+ IntTy, TyVariableKind, UniverseIndex,
};
use chalk_solve::infer::ParameterEnaVariableExt;
use ena::unify::UnifyKey;
@@ -331,7 +331,6 @@ impl<'a> InferenceTable<'a> {
&mut resolve::Resolver { table: self, var_stack, fallback },
DebruijnIndex::INNERMOST,
)
- .expect("fold failed unexpectedly")
}
pub(crate) fn resolve_completely<T>(&mut self, t: T) -> T
@@ -452,13 +451,14 @@ impl<'a> InferenceTable<'a> {
f: impl FnOnce(&mut Self) -> T,
) -> T {
use chalk_ir::fold::TypeFolder;
+
+ #[derive(chalk_derive::FallibleTypeFolder)]
+ #[has_interner(Interner)]
struct VarFudger<'a, 'b> {
table: &'a mut InferenceTable<'b>,
highest_known_var: InferenceVar,
}
impl<'a, 'b> TypeFolder<Interner> for VarFudger<'a, 'b> {
- type Error = NoSolution;
-
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
self
}
@@ -472,24 +472,24 @@ impl<'a> InferenceTable<'a> {
var: chalk_ir::InferenceVar,
kind: TyVariableKind,
_outer_binder: chalk_ir::DebruijnIndex,
- ) -> chalk_ir::Fallible<chalk_ir::Ty<Interner>> {
- Ok(if var < self.highest_known_var {
+ ) -> chalk_ir::Ty<Interner> {
+ if var < self.highest_known_var {
var.to_ty(Interner, kind)
} else {
self.table.new_type_var()
- })
+ }
}
fn fold_inference_lifetime(
&mut self,
var: chalk_ir::InferenceVar,
_outer_binder: chalk_ir::DebruijnIndex,
- ) -> chalk_ir::Fallible<chalk_ir::Lifetime<Interner>> {
- Ok(if var < self.highest_known_var {
+ ) -> chalk_ir::Lifetime<Interner> {
+ if var < self.highest_known_var {
var.to_lifetime(Interner)
} else {
self.table.new_lifetime_var()
- })
+ }
}
fn fold_inference_const(
@@ -497,12 +497,12 @@ impl<'a> InferenceTable<'a> {
ty: chalk_ir::Ty<Interner>,
var: chalk_ir::InferenceVar,
_outer_binder: chalk_ir::DebruijnIndex,
- ) -> chalk_ir::Fallible<chalk_ir::Const<Interner>> {
- Ok(if var < self.highest_known_var {
+ ) -> chalk_ir::Const<Interner> {
+ if var < self.highest_known_var {
var.to_const(Interner, ty)
} else {
self.table.new_const_var(ty)
- })
+ }
}
}
@@ -512,7 +512,6 @@ impl<'a> InferenceTable<'a> {
self.rollback_to(snapshot);
result
.fold_with(&mut VarFudger { table: self, highest_known_var }, DebruijnIndex::INNERMOST)
- .expect("fold_with with VarFudger")
}
/// This checks whether any of the free variables in the `canonicalized`
@@ -598,11 +597,14 @@ impl<'a> InferenceTable<'a> {
.build();
let projection = {
- let b = TyBuilder::assoc_type_projection(self.db, output_assoc_type);
+ let b = TyBuilder::subst_for_def(self.db, fn_once_trait, None);
if b.remaining() != 2 {
return None;
}
- b.push(ty.clone()).push(arg_ty).build()
+ let fn_once_subst = b.push(ty.clone()).push(arg_ty).build();
+
+ TyBuilder::assoc_type_projection(self.db, output_assoc_type, Some(fn_once_subst))
+ .build()
};
let trait_env = self.trait_env.env.clone();
@@ -636,21 +638,24 @@ mod resolve {
use chalk_ir::{
cast::Cast,
fold::{TypeFoldable, TypeFolder},
- Fallible, NoSolution,
};
use hir_def::type_ref::ConstScalar;
- pub(super) struct Resolver<'a, 'b, F> {
+ #[derive(chalk_derive::FallibleTypeFolder)]
+ #[has_interner(Interner)]
+ pub(super) struct Resolver<
+ 'a,
+ 'b,
+ F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
+ > {
pub(super) table: &'a mut InferenceTable<'b>,
pub(super) var_stack: &'a mut Vec<InferenceVar>,
pub(super) fallback: F,
}
- impl<'a, 'b, 'i, F> TypeFolder<Interner> for Resolver<'a, 'b, F>
+ impl<'a, 'b, F> TypeFolder<Interner> for Resolver<'a, 'b, F>
where
- F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg + 'i,
+ F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
{
- type Error = NoSolution;
-
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
self
}
@@ -664,20 +669,19 @@ mod resolve {
var: InferenceVar,
kind: TyVariableKind,
outer_binder: DebruijnIndex,
- ) -> Fallible<Ty> {
+ ) -> Ty {
let var = self.table.var_unification_table.inference_var_root(var);
if self.var_stack.contains(&var) {
// recursive type
let default = self.table.fallback_value(var, kind).cast(Interner);
- return Ok((self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
+ return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
.assert_ty_ref(Interner)
- .clone());
+ .clone();
}
let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
// known_ty may contain other variables that are known by now
self.var_stack.push(var);
- let result =
- known_ty.fold_with(self, outer_binder).expect("fold failed unexpectedly");
+ let result = known_ty.fold_with(self, outer_binder);
self.var_stack.pop();
result.assert_ty_ref(Interner).clone()
} else {
@@ -686,7 +690,7 @@ mod resolve {
.assert_ty_ref(Interner)
.clone()
};
- Ok(result)
+ result
}
fn fold_inference_const(
@@ -694,7 +698,7 @@ mod resolve {
ty: Ty,
var: InferenceVar,
outer_binder: DebruijnIndex,
- ) -> Fallible<Const> {
+ ) -> Const {
let var = self.table.var_unification_table.inference_var_root(var);
let default = ConstData {
ty: ty.clone(),
@@ -704,35 +708,33 @@ mod resolve {
.cast(Interner);
if self.var_stack.contains(&var) {
// recursive
- return Ok((self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
+ return (self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
.assert_const_ref(Interner)
- .clone());
+ .clone();
}
- let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
+ if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
// known_ty may contain other variables that are known by now
self.var_stack.push(var);
- let result =
- known_ty.fold_with(self, outer_binder).expect("fold failed unexpectedly");
+ let result = known_ty.fold_with(self, outer_binder);
self.var_stack.pop();
result.assert_const_ref(Interner).clone()
} else {
(self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
.assert_const_ref(Interner)
.clone()
- };
- Ok(result)
+ }
}
fn fold_inference_lifetime(
&mut self,
_var: InferenceVar,
_outer_binder: DebruijnIndex,
- ) -> Fallible<Lifetime> {
+ ) -> Lifetime {
// fall back all lifetimes to 'static -- currently we don't deal
// with any lifetimes, but we can sometimes get some lifetime
// variables through Chalk's unification, and this at least makes
// sure we don't leak them outside of inference
- Ok(crate::static_lifetime())
+ crate::static_lifetime()
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
new file mode 100644
index 000000000..0c547192a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
@@ -0,0 +1,173 @@
+//! Type inhabitedness logic.
+use std::ops::ControlFlow::{self, Break, Continue};
+
+use chalk_ir::{
+ visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
+ DebruijnIndex,
+};
+use hir_def::{
+ adt::VariantData, attr::Attrs, type_ref::ConstScalar, visibility::Visibility, AdtId,
+ EnumVariantId, HasModule, Lookup, ModuleId, VariantId,
+};
+
+use crate::{
+ db::HirDatabase, Binders, ConcreteConst, Const, ConstValue, Interner, Substitution, Ty, TyKind,
+};
+
+/// Checks whether a type is visibly uninhabited from a particular module.
+pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
+ let mut uninhabited_from = UninhabitedFrom { target_mod, db };
+ let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
+ inhabitedness == BREAK_VISIBLY_UNINHABITED
+}
+
+/// Checks whether a variant is visibly uninhabited from a particular module.
+pub(crate) fn is_enum_variant_uninhabited_from(
+ variant: EnumVariantId,
+ subst: &Substitution,
+ target_mod: ModuleId,
+ db: &dyn HirDatabase,
+) -> bool {
+ let enum_data = db.enum_data(variant.parent);
+ let vars_attrs = db.variants_attrs(variant.parent);
+ let is_local = variant.parent.lookup(db.upcast()).container.krate() == target_mod.krate();
+
+ let mut uninhabited_from = UninhabitedFrom { target_mod, db };
+ let inhabitedness = uninhabited_from.visit_variant(
+ variant.into(),
+ &enum_data.variants[variant.local_id].variant_data,
+ subst,
+ &vars_attrs[variant.local_id],
+ is_local,
+ );
+ inhabitedness == BREAK_VISIBLY_UNINHABITED
+}
+
+struct UninhabitedFrom<'a> {
+ target_mod: ModuleId,
+ db: &'a dyn HirDatabase,
+}
+
+const CONTINUE_OPAQUELY_INHABITED: ControlFlow<VisiblyUninhabited> = Continue(());
+const BREAK_VISIBLY_UNINHABITED: ControlFlow<VisiblyUninhabited> = Break(VisiblyUninhabited);
+#[derive(PartialEq, Eq)]
+struct VisiblyUninhabited;
+
+impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
+ type BreakTy = VisiblyUninhabited;
+
+ fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = VisiblyUninhabited> {
+ self
+ }
+
+ fn visit_ty(
+ &mut self,
+ ty: &Ty,
+ outer_binder: DebruijnIndex,
+ ) -> ControlFlow<VisiblyUninhabited> {
+ match ty.kind(Interner) {
+ TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
+ TyKind::Never => BREAK_VISIBLY_UNINHABITED,
+ TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
+ TyKind::Array(item_ty, len) => match try_usize_const(len) {
+ Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
+ Some(1..) => item_ty.super_visit_with(self, outer_binder),
+ },
+
+ TyKind::Ref(..) | _ => CONTINUE_OPAQUELY_INHABITED,
+ }
+ }
+
+ fn interner(&self) -> Interner {
+ Interner
+ }
+}
+
+impl UninhabitedFrom<'_> {
+ fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow<VisiblyUninhabited> {
+ let attrs = self.db.attrs(adt.into());
+ let adt_non_exhaustive = attrs.by_key("non_exhaustive").exists();
+ let is_local = adt.module(self.db.upcast()).krate() == self.target_mod.krate();
+ if adt_non_exhaustive && !is_local {
+ return CONTINUE_OPAQUELY_INHABITED;
+ }
+
+ // An ADT is uninhabited iff all its variants uninhabited.
+ match adt {
+ // rustc: For now, `union`s are never considered uninhabited.
+ AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED,
+ AdtId::StructId(s) => {
+ let struct_data = self.db.struct_data(s);
+ self.visit_variant(s.into(), &struct_data.variant_data, subst, &attrs, is_local)
+ }
+ AdtId::EnumId(e) => {
+ let vars_attrs = self.db.variants_attrs(e);
+ let enum_data = self.db.enum_data(e);
+
+ for (local_id, enum_var) in enum_data.variants.iter() {
+ let variant_inhabitedness = self.visit_variant(
+ EnumVariantId { parent: e, local_id }.into(),
+ &enum_var.variant_data,
+ subst,
+ &vars_attrs[local_id],
+ is_local,
+ );
+ match variant_inhabitedness {
+ Break(VisiblyUninhabited) => continue,
+ Continue(()) => return CONTINUE_OPAQUELY_INHABITED,
+ }
+ }
+ BREAK_VISIBLY_UNINHABITED
+ }
+ }
+ }
+
+ fn visit_variant(
+ &mut self,
+ variant: VariantId,
+ variant_data: &VariantData,
+ subst: &Substitution,
+ attrs: &Attrs,
+ is_local: bool,
+ ) -> ControlFlow<VisiblyUninhabited> {
+ let non_exhaustive_field_list = attrs.by_key("non_exhaustive").exists();
+ if non_exhaustive_field_list && !is_local {
+ return CONTINUE_OPAQUELY_INHABITED;
+ }
+
+ let is_enum = matches!(variant, VariantId::EnumVariantId(..));
+ let field_tys = self.db.field_types(variant);
+ let field_vis = self.db.field_visibilities(variant);
+
+ for (fid, _) in variant_data.fields().iter() {
+ self.visit_field(field_vis[fid], &field_tys[fid], subst, is_enum)?;
+ }
+ CONTINUE_OPAQUELY_INHABITED
+ }
+
+ fn visit_field(
+ &mut self,
+ vis: Visibility,
+ ty: &Binders<Ty>,
+ subst: &Substitution,
+ is_enum: bool,
+ ) -> ControlFlow<VisiblyUninhabited> {
+ if is_enum || vis.is_visible_from(self.db.upcast(), self.target_mod) {
+ let ty = ty.clone().substitute(Interner, subst);
+ ty.visit_with(self, DebruijnIndex::INNERMOST)
+ } else {
+ CONTINUE_OPAQUELY_INHABITED
+ }
+ }
+}
+
+fn try_usize_const(c: &Const) -> Option<u128> {
+ let data = &c.data(Interner);
+ if data.ty.kind(Interner) != &TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)) {
+ return None;
+ }
+ match data.value {
+ ConstValue::Concrete(ConcreteConst { interned: ConstScalar::UInt(value) }) => Some(value),
+ _ => None,
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index 5a5d610e3..c4b700cbc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -14,6 +14,7 @@ mod chalk_db;
mod chalk_ext;
pub mod consteval;
mod infer;
+mod inhabitedness;
mod interner;
mod lower;
mod mapping;
@@ -195,20 +196,6 @@ pub(crate) fn make_binders<T: HasInterner<Interner = Interner>>(
make_binders_with_count(db, usize::MAX, generics, value)
}
-// FIXME: get rid of this
-pub fn make_canonical<T: HasInterner<Interner = Interner>>(
- value: T,
- kinds: impl IntoIterator<Item = TyVariableKind>,
-) -> Canonical<T> {
- let kinds = kinds.into_iter().map(|tk| {
- chalk_ir::CanonicalVarKind::new(
- chalk_ir::VariableKind::Ty(tk),
- chalk_ir::UniverseIndex::ROOT,
- )
- });
- Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) }
-}
-
// FIXME: get rid of this, just replace it by FnPointer
/// A function signature as seen by type inference: Several parameter types and
/// one return type.
@@ -267,13 +254,13 @@ impl CallableSig {
}
impl TypeFoldable<Interner> for CallableSig {
- fn fold_with<E>(
+ fn try_fold_with<E>(
self,
- folder: &mut dyn chalk_ir::fold::TypeFolder<Interner, Error = E>,
+ folder: &mut dyn chalk_ir::fold::FallibleTypeFolder<Interner, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Self, E> {
let vec = self.params_and_return.to_vec();
- let folded = vec.fold_with(folder, outer_binder)?;
+ let folded = vec.try_fold_with(folder, outer_binder)?;
Ok(CallableSig { params_and_return: folded.into(), is_varargs: self.is_varargs })
}
}
@@ -305,16 +292,19 @@ pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<
for_ty: impl FnMut(BoundVar, DebruijnIndex) -> Ty,
for_const: impl FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
) -> T {
- use chalk_ir::{fold::TypeFolder, Fallible};
- struct FreeVarFolder<F1, F2>(F1, F2);
+ use chalk_ir::fold::TypeFolder;
+
+ #[derive(chalk_derive::FallibleTypeFolder)]
+ #[has_interner(Interner)]
+ struct FreeVarFolder<
+ F1: FnMut(BoundVar, DebruijnIndex) -> Ty,
+ F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
+ >(F1, F2);
impl<
- 'i,
- F1: FnMut(BoundVar, DebruijnIndex) -> Ty + 'i,
- F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const + 'i,
+ F1: FnMut(BoundVar, DebruijnIndex) -> Ty,
+ F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
> TypeFolder<Interner> for FreeVarFolder<F1, F2>
{
- type Error = NoSolution;
-
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
self
}
@@ -323,12 +313,8 @@ pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<
Interner
}
- fn fold_free_var_ty(
- &mut self,
- bound_var: BoundVar,
- outer_binder: DebruijnIndex,
- ) -> Fallible<Ty> {
- Ok(self.0(bound_var, outer_binder))
+ fn fold_free_var_ty(&mut self, bound_var: BoundVar, outer_binder: DebruijnIndex) -> Ty {
+ self.0(bound_var, outer_binder)
}
fn fold_free_var_const(
@@ -336,12 +322,11 @@ pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<
ty: Ty,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
- ) -> Fallible<Const> {
- Ok(self.1(ty, bound_var, outer_binder))
+ ) -> Const {
+ self.1(ty, bound_var, outer_binder)
}
}
t.fold_with(&mut FreeVarFolder(for_ty, for_const), DebruijnIndex::INNERMOST)
- .expect("fold failed unexpectedly")
}
pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>(
@@ -364,16 +349,13 @@ pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + TypeFold
f: impl FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>,
binders: DebruijnIndex,
) -> T {
- use chalk_ir::{
- fold::{TypeFolder, TypeSuperFoldable},
- Fallible,
- };
- struct TyFolder<F>(F);
- impl<'i, F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const> + 'i>
- TypeFolder<Interner> for TyFolder<F>
+ use chalk_ir::fold::{TypeFolder, TypeSuperFoldable};
+ #[derive(chalk_derive::FallibleTypeFolder)]
+ #[has_interner(Interner)]
+ struct TyFolder<F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>>(F);
+ impl<F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>> TypeFolder<Interner>
+ for TyFolder<F>
{
- type Error = NoSolution;
-
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
self
}
@@ -382,16 +364,16 @@ pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + TypeFold
Interner
}
- fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
- let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?;
- Ok(self.0(Either::Left(ty), outer_binder).left().unwrap())
+ fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Ty {
+ let ty = ty.super_fold_with(self.as_dyn(), outer_binder);
+ self.0(Either::Left(ty), outer_binder).left().unwrap()
}
- fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Fallible<Const> {
- Ok(self.0(Either::Right(c), outer_binder).right().unwrap())
+ fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Const {
+ self.0(Either::Right(c), outer_binder).right().unwrap()
}
}
- t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly")
+ t.fold_with(&mut TyFolder(f), binders)
}
/// 'Canonicalizes' the `t` by replacing any errors with new variables. Also
@@ -403,16 +385,16 @@ where
T: HasInterner<Interner = Interner>,
{
use chalk_ir::{
- fold::{TypeFolder, TypeSuperFoldable},
+ fold::{FallibleTypeFolder, TypeSuperFoldable},
Fallible,
};
struct ErrorReplacer {
vars: usize,
}
- impl TypeFolder<Interner> for ErrorReplacer {
+ impl FallibleTypeFolder<Interner> for ErrorReplacer {
type Error = NoSolution;
- fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
+ fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
self
}
@@ -420,18 +402,17 @@ where
Interner
}
- fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
+ fn try_fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
if let TyKind::Error = ty.kind(Interner) {
let index = self.vars;
self.vars += 1;
Ok(TyKind::BoundVar(BoundVar::new(outer_binder, index)).intern(Interner))
} else {
- let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?;
- Ok(ty)
+ ty.try_super_fold_with(self.as_dyn(), outer_binder)
}
}
- fn fold_inference_ty(
+ fn try_fold_inference_ty(
&mut self,
_var: InferenceVar,
_kind: TyVariableKind,
@@ -446,7 +427,7 @@ where
}
}
- fn fold_free_var_ty(
+ fn try_fold_free_var_ty(
&mut self,
_bound_var: BoundVar,
_outer_binder: DebruijnIndex,
@@ -460,7 +441,7 @@ where
}
}
- fn fold_inference_const(
+ fn try_fold_inference_const(
&mut self,
ty: Ty,
_var: InferenceVar,
@@ -473,7 +454,7 @@ where
}
}
- fn fold_free_var_const(
+ fn try_fold_free_var_const(
&mut self,
ty: Ty,
_bound_var: BoundVar,
@@ -486,7 +467,7 @@ where
}
}
- fn fold_inference_lifetime(
+ fn try_fold_inference_lifetime(
&mut self,
_var: InferenceVar,
_outer_binder: DebruijnIndex,
@@ -498,7 +479,7 @@ where
}
}
- fn fold_free_var_lifetime(
+ fn try_fold_free_var_lifetime(
&mut self,
_bound_var: BoundVar,
_outer_binder: DebruijnIndex,
@@ -511,7 +492,7 @@ where
}
}
let mut error_replacer = ErrorReplacer { vars: 0 };
- let value = match t.clone().fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) {
+ let value = match t.clone().try_fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) {
Ok(t) => t,
Err(_) => panic!("Encountered unbound or inference vars in {:?}", t),
};
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 3ed9c941f..223d705b1 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -1,12 +1,12 @@
//! Methods for lowering the HIR to types. There are two main cases here:
//!
//! - Lowering a type reference like `&usize` or `Option<foo::bar::Baz>` to a
-//! type: The entry point for this is `Ty::from_hir`.
-//! - Building the type for an item: This happens through the `type_for_def` query.
+//! type: The entry point for this is `TyLoweringContext::lower_ty`.
+//! - Building the type for an item: This happens through the `ty` query.
//!
//! This usually involves resolving names, collecting generic arguments etc.
use std::{
- cell::{Cell, RefCell},
+ cell::{Cell, RefCell, RefMut},
iter,
sync::Arc,
};
@@ -47,7 +47,7 @@ use crate::{
consteval::{intern_const_scalar, path_to_const, unknown_const, unknown_const_as_generic},
db::HirDatabase,
make_binders,
- mapping::ToChalk,
+ mapping::{from_chalk_trait_id, ToChalk},
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
utils::Generics,
utils::{all_super_trait_refs, associated_type_by_name_including_super_traits, generics},
@@ -238,18 +238,7 @@ impl<'a> TyLoweringContext<'a> {
})
.intern(Interner)
}
- TypeRef::DynTrait(bounds) => {
- let self_ty =
- TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
- let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
- QuantifiedWhereClauses::from_iter(
- Interner,
- bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)),
- )
- });
- let bounds = crate::make_single_type_binders(bounds);
- TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
- }
+ TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds),
TypeRef::ImplTrait(bounds) => {
match self.impl_trait_mode {
ImplTraitLoweringMode::Opaque => {
@@ -317,7 +306,7 @@ impl<'a> TyLoweringContext<'a> {
// FIXME we're probably doing something wrong here
self.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
let (
- parent_params,
+ _parent_params,
self_params,
list_params,
const_params,
@@ -330,7 +319,7 @@ impl<'a> TyLoweringContext<'a> {
};
TyKind::BoundVar(BoundVar::new(
self.in_binders,
- idx as usize + parent_params + self_params + list_params + const_params,
+ idx as usize + self_params + list_params + const_params,
))
.intern(Interner)
}
@@ -341,26 +330,29 @@ impl<'a> TyLoweringContext<'a> {
}
}
TypeRef::Macro(macro_call) => {
- let (expander, recursion_start) = {
- let mut expander = self.expander.borrow_mut();
- if expander.is_some() {
- (Some(expander), false)
- } else {
- *expander = Some(Expander::new(
- self.db.upcast(),
- macro_call.file_id,
- self.resolver.module(),
- ));
- (Some(expander), true)
+ let (mut expander, recursion_start) = {
+ match RefMut::filter_map(self.expander.borrow_mut(), Option::as_mut) {
+ // There already is an expander here, this means we are already recursing
+ Ok(expander) => (expander, false),
+ // No expander was created yet, so we are at the start of the expansion recursion
+ // and therefore have to create an expander.
+ Err(expander) => (
+ RefMut::map(expander, |it| {
+ it.insert(Expander::new(
+ self.db.upcast(),
+ macro_call.file_id,
+ self.resolver.module(),
+ ))
+ }),
+ true,
+ ),
}
};
- let ty = if let Some(mut expander) = expander {
- let expander_mut = expander.as_mut().unwrap();
+ let ty = {
let macro_call = macro_call.to_node(self.db.upcast());
- match expander_mut.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
+ match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
- let ctx =
- LowerCtx::new(self.db.upcast(), expander_mut.current_file_id());
+ let ctx = LowerCtx::new(self.db.upcast(), expander.current_file_id());
let type_ref = TypeRef::from_ast(&ctx, expanded);
drop(expander);
@@ -373,11 +365,14 @@ impl<'a> TyLoweringContext<'a> {
.exit(self.db.upcast(), mark);
Some(ty)
}
- _ => None,
+ _ => {
+ drop(expander);
+ None
+ }
}
- } else {
- None
};
+
+ // drop the expander, resetting it to pre-recursion state
if recursion_start {
*self.expander.borrow_mut() = None;
}
@@ -468,29 +463,10 @@ impl<'a> TyLoweringContext<'a> {
}
}
0 => {
- let self_ty = Some(
- TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
- .intern(Interner),
- );
- let trait_ref = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
- ctx.lower_trait_ref_from_resolved_path(
- trait_,
- resolved_segment,
- self_ty,
- )
- });
- let dyn_ty = DynTy {
- bounds: crate::make_single_type_binders(
- QuantifiedWhereClauses::from_iter(
- Interner,
- Some(crate::wrap_empty_binders(WhereClause::Implemented(
- trait_ref,
- ))),
- ),
- ),
- lifetime: static_lifetime(),
- };
- TyKind::Dyn(dyn_ty).intern(Interner)
+ // Trait object type without dyn; this should be handled in upstream. See
+ // `lower_path()`.
+ stdx::never!("unexpected fully resolved trait path");
+ TyKind::Error.intern(Interner)
}
_ => {
// FIXME report error (ambiguous associated type)
@@ -509,21 +485,45 @@ impl<'a> TyLoweringContext<'a> {
TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
}
ParamLoweringMode::Variable => {
- let idx = generics.param_idx(param_id.into()).expect("matching generics");
+ let idx = match generics.param_idx(param_id.into()) {
+ None => {
+ never!("no matching generics");
+ return (TyKind::Error.intern(Interner), None);
+ }
+ Some(idx) => idx,
+ };
+
TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
}
}
.intern(Interner)
}
TypeNs::SelfType(impl_id) => {
- let generics = generics(self.db.upcast(), impl_id.into());
- let substs = match self.type_param_mode {
- ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db),
+ let def =
+ self.resolver.generic_def().expect("impl should have generic param scope");
+ let generics = generics(self.db.upcast(), def);
+
+ match self.type_param_mode {
+ ParamLoweringMode::Placeholder => {
+ // `def` can be either impl itself or item within, and we need impl itself
+ // now.
+ let generics = generics.parent_generics().unwrap_or(&generics);
+ let subst = generics.placeholder_subst(self.db);
+ self.db.impl_self_ty(impl_id).substitute(Interner, &subst)
+ }
ParamLoweringMode::Variable => {
- generics.bound_vars_subst(self.db, self.in_binders)
+ let starting_from = match def {
+ GenericDefId::ImplId(_) => 0,
+ // `def` is an item within impl. We need to substitute `BoundVar`s but
+ // remember that they are for parent (i.e. impl) generic params so they
+ // come after our own params.
+ _ => generics.len_self(),
+ };
+ TyBuilder::impl_self_ty(self.db, impl_id)
+ .fill_with_bound_vars(self.in_binders, starting_from)
+ .build()
}
- };
- self.db.impl_self_ty(impl_id).substitute(Interner, &substs)
+ }
}
TypeNs::AdtSelfType(adt) => {
let generics = generics(self.db.upcast(), adt.into());
@@ -555,11 +555,20 @@ impl<'a> TyLoweringContext<'a> {
let (ty, res) = self.lower_ty_ext(type_ref);
return self.lower_ty_relative_path(ty, res, path.segments());
}
+
let (resolution, remaining_index) =
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
Some(it) => it,
None => return (TyKind::Error.intern(Interner), None),
};
+
+ if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() {
+ // trait object type without dyn
+ let bound = TypeBound::Path(path.clone(), TraitBoundModifier::None);
+ let ty = self.lower_dyn_trait(&[Interned::new(bound)]);
+ return (ty, None);
+ }
+
let (resolved_segment, remaining_segments) = match remaining_index {
None => (
path.segments().last().expect("resolved path has at least one element"),
@@ -671,40 +680,31 @@ impl<'a> TyLoweringContext<'a> {
fn substs_from_path_segment(
&self,
segment: PathSegment<'_>,
- def_generic: Option<GenericDefId>,
+ def: Option<GenericDefId>,
infer_args: bool,
explicit_self_ty: Option<Ty>,
) -> Substitution {
+ // Remember that the item's own generic args come before its parent's.
let mut substs = Vec::new();
- let def_generics = if let Some(def) = def_generic {
- generics(self.db.upcast(), def)
+ let def = if let Some(d) = def {
+ d
} else {
return Substitution::empty(Interner);
};
+ let def_generics = generics(self.db.upcast(), def);
let (parent_params, self_params, type_params, const_params, impl_trait_params) =
def_generics.provenance_split();
- let total_len =
- parent_params + self_params + type_params + const_params + impl_trait_params;
+ let item_len = self_params + type_params + const_params + impl_trait_params;
+ let total_len = parent_params + item_len;
- let ty_error = GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner);
+ let ty_error = TyKind::Error.intern(Interner).cast(Interner);
let mut def_generic_iter = def_generics.iter_id();
- for _ in 0..parent_params {
- if let Some(eid) = def_generic_iter.next() {
- match eid {
- Either::Left(_) => substs.push(ty_error.clone()),
- Either::Right(x) => {
- substs.push(unknown_const_as_generic(self.db.const_param_ty(x)))
- }
- }
- }
- }
-
let fill_self_params = || {
for x in explicit_self_ty
.into_iter()
- .map(|x| GenericArgData::Ty(x).intern(Interner))
+ .map(|x| x.cast(Interner))
.chain(iter::repeat(ty_error.clone()))
.take(self_params)
{
@@ -765,37 +765,40 @@ impl<'a> TyLoweringContext<'a> {
fill_self_params();
}
+ // These params include those of parent.
+ let remaining_params: SmallVec<[_; 2]> = def_generic_iter
+ .map(|eid| match eid {
+ Either::Left(_) => ty_error.clone(),
+ Either::Right(x) => unknown_const_as_generic(self.db.const_param_ty(x)),
+ })
+ .collect();
+ assert_eq!(remaining_params.len() + substs.len(), total_len);
+
// handle defaults. In expression or pattern path segments without
// explicitly specified type arguments, missing type arguments are inferred
// (i.e. defaults aren't used).
if !infer_args || had_explicit_args {
- if let Some(def_generic) = def_generic {
- let defaults = self.db.generic_defaults(def_generic);
- assert_eq!(total_len, defaults.len());
-
- for default_ty in defaults.iter().skip(substs.len()) {
- // each default can depend on the previous parameters
- let substs_so_far = Substitution::from_iter(Interner, substs.clone());
- if let Some(_id) = def_generic_iter.next() {
- substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
- }
- }
+ let defaults = self.db.generic_defaults(def);
+ assert_eq!(total_len, defaults.len());
+ let parent_from = item_len - substs.len();
+
+ for (idx, default_ty) in defaults[substs.len()..item_len].iter().enumerate() {
+ // each default can depend on the previous parameters
+ let substs_so_far = Substitution::from_iter(
+ Interner,
+ substs.iter().cloned().chain(remaining_params[idx..].iter().cloned()),
+ );
+ substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
}
- }
- // add placeholders for args that were not provided
- // FIXME: emit diagnostics in contexts where this is not allowed
- for eid in def_generic_iter {
- match eid {
- Either::Left(_) => substs.push(ty_error.clone()),
- Either::Right(x) => {
- substs.push(unknown_const_as_generic(self.db.const_param_ty(x)))
- }
- }
+ // Keep parent's params as unknown.
+ let mut remaining_params = remaining_params;
+ substs.extend(remaining_params.drain(parent_from..));
+ } else {
+ substs.extend(remaining_params);
}
- // If this assert fails, it means you pushed into subst but didn't call .next() of def_generic_iter
- assert_eq!(substs.len(), total_len);
+ assert_eq!(substs.len(), total_len);
Substitution::from_iter(Interner, substs)
}
@@ -987,6 +990,86 @@ impl<'a> TyLoweringContext<'a> {
})
}
+ fn lower_dyn_trait(&self, bounds: &[Interned<TypeBound>]) -> Ty {
+ let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
+ // INVARIANT: The principal trait bound, if present, must come first. Others may be in any
+ // order but should be in the same order for the same set but possibly different order of
+ // bounds in the input.
+ // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound.
+ // These invariants are utilized by `TyExt::dyn_trait()` and chalk.
+ let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
+ let mut bounds: Vec<_> = bounds
+ .iter()
+ .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
+ .collect();
+
+ let mut multiple_regular_traits = false;
+ let mut multiple_same_projection = false;
+ bounds.sort_unstable_by(|lhs, rhs| {
+ use std::cmp::Ordering;
+ match (lhs.skip_binders(), rhs.skip_binders()) {
+ (WhereClause::Implemented(lhs), WhereClause::Implemented(rhs)) => {
+ let lhs_id = lhs.trait_id;
+ let lhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(lhs_id)).is_auto;
+ let rhs_id = rhs.trait_id;
+ let rhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(rhs_id)).is_auto;
+
+ if !lhs_is_auto && !rhs_is_auto {
+ multiple_regular_traits = true;
+ }
+ // Note that the ordering here is important; this ensures the invariant
+ // mentioned above.
+ (lhs_is_auto, lhs_id).cmp(&(rhs_is_auto, rhs_id))
+ }
+ (WhereClause::Implemented(_), _) => Ordering::Less,
+ (_, WhereClause::Implemented(_)) => Ordering::Greater,
+ (WhereClause::AliasEq(lhs), WhereClause::AliasEq(rhs)) => {
+ match (&lhs.alias, &rhs.alias) {
+ (AliasTy::Projection(lhs_proj), AliasTy::Projection(rhs_proj)) => {
+ // We only compare the `associated_ty_id`s. We shouldn't have
+ // multiple bounds for an associated type in the correct Rust code,
+ // and if we do, we error out.
+ if lhs_proj.associated_ty_id == rhs_proj.associated_ty_id {
+ multiple_same_projection = true;
+ }
+ lhs_proj.associated_ty_id.cmp(&rhs_proj.associated_ty_id)
+ }
+ // We don't produce `AliasTy::Opaque`s yet.
+ _ => unreachable!(),
+ }
+ }
+ // We don't produce `WhereClause::{TypeOutlives, LifetimeOutlives}` yet.
+ _ => unreachable!(),
+ }
+ });
+
+ if multiple_regular_traits || multiple_same_projection {
+ return None;
+ }
+
+ if bounds.first().and_then(|b| b.trait_id()).is_none() {
+ // When there's no trait bound, that's an error. This happens when the trait refs
+ // are unresolved.
+ return None;
+ }
+
+ // As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
+ // bounds. We shouldn't have repeated elements besides auto traits at this point.
+ bounds.dedup();
+
+ Some(QuantifiedWhereClauses::from_iter(Interner, bounds))
+ });
+
+ if let Some(bounds) = bounds {
+ let bounds = crate::make_single_type_binders(bounds);
+ TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
+ } else {
+ // FIXME: report error
+ // (additional non-auto traits, associated type rebound, or no resolved trait)
+ TyKind::Error.intern(Interner)
+ }
+ }
+
fn lower_impl_trait(
&self,
bounds: &[Interned<TypeBound>],
@@ -1075,11 +1158,28 @@ fn named_associated_type_shorthand_candidates<R>(
};
match res {
- TypeNs::SelfType(impl_id) => search(
+ TypeNs::SelfType(impl_id) => {
// we're _in_ the impl -- the binders get added back later. Correct,
// but it would be nice to make this more explicit
- db.impl_trait(impl_id)?.into_value_and_skipped_binders().0,
- ),
+ let trait_ref = db.impl_trait(impl_id)?.into_value_and_skipped_binders().0;
+
+ let impl_id_as_generic_def: GenericDefId = impl_id.into();
+ if impl_id_as_generic_def != def {
+ // `trait_ref` contains `BoundVar`s bound by impl's `Binders`, but here we need
+ // `BoundVar`s from `def`'s point of view.
+ // FIXME: A `HirDatabase` query may be handy if this process is needed in more
+ // places. It'd be almost identical as `impl_trait_query` where `resolver` would be
+ // of `def` instead of `impl_id`.
+ let starting_idx = generics(db.upcast(), def).len_self();
+ let subst = TyBuilder::subst_for_def(db, impl_id, None)
+ .fill_with_bound_vars(DebruijnIndex::INNERMOST, starting_idx)
+ .build();
+ let trait_ref = subst.apply(trait_ref, Interner);
+ search(trait_ref)
+ } else {
+ search(trait_ref)
+ }
+ }
TypeNs::GenericParam(param_id) => {
let predicates = db.generic_predicates_for_param(def, param_id.into(), assoc_name);
let res = predicates.iter().find_map(|pred| match pred.skip_binders().skip_binders() {
@@ -1096,10 +1196,18 @@ fn named_associated_type_shorthand_candidates<R>(
}
// Handle `Self::Type` referring to own associated type in trait definitions
if let GenericDefId::TraitId(trait_id) = param_id.parent() {
- let generics = generics(db.upcast(), trait_id.into());
- if generics.params.type_or_consts[param_id.local_id()].is_trait_self() {
+ let trait_generics = generics(db.upcast(), trait_id.into());
+ if trait_generics.params.type_or_consts[param_id.local_id()].is_trait_self() {
+ let def_generics = generics(db.upcast(), def);
+ let starting_idx = match def {
+ GenericDefId::TraitId(_) => 0,
+ // `def` is an item within trait. We need to substitute `BoundVar`s but
+ // remember that they are for parent (i.e. trait) generic params so they
+ // come after our own params.
+ _ => def_generics.len_self(),
+ };
let trait_ref = TyBuilder::trait_ref(db, trait_id)
- .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0)
+ .fill_with_bound_vars(DebruijnIndex::INNERMOST, starting_idx)
.build();
return search(trait_ref);
}
@@ -1126,7 +1234,7 @@ pub(crate) fn field_types_query(
let ctx =
TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
for (field_id, field_data) in var_data.fields().iter() {
- res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref)))
+ res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref)));
}
Arc::new(res)
}
@@ -1341,6 +1449,7 @@ pub(crate) fn generic_defaults_query(
let ctx =
TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
let generic_params = generics(db.upcast(), def);
+ let parent_start_idx = generic_params.len_self();
let defaults = generic_params
.iter()
@@ -1353,19 +1462,17 @@ pub(crate) fn generic_defaults_query(
let val = unknown_const_as_generic(
db.const_param_ty(ConstParamId::from_unchecked(id)),
);
- return crate::make_binders_with_count(db, idx, &generic_params, val);
+ return make_binders(db, &generic_params, val);
}
};
let mut ty =
p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
// Each default can only refer to previous parameters.
- // type variable default referring to parameter coming
- // after it. This is forbidden (FIXME: report
- // diagnostic)
- ty = fallback_bound_vars(ty, idx);
- let val = GenericArgData::Ty(ty).intern(Interner);
- crate::make_binders_with_count(db, idx, &generic_params, val)
+ // Type variable default referring to parameter coming
+ // after it is forbidden (FIXME: report diagnostic)
+ ty = fallback_bound_vars(ty, idx, parent_start_idx);
+ crate::make_binders(db, &generic_params, ty.cast(Interner))
})
.collect();
@@ -1382,15 +1489,14 @@ pub(crate) fn generic_defaults_recover(
// we still need one default per parameter
let defaults = generic_params
.iter_id()
- .enumerate()
- .map(|(count, id)| {
+ .map(|id| {
let val = match id {
itertools::Either::Left(_) => {
GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
}
itertools::Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
};
- crate::make_binders_with_count(db, count, &generic_params, val)
+ crate::make_binders(db, &generic_params, val)
})
.collect();
@@ -1569,6 +1675,19 @@ pub enum ValueTyDefId {
}
impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
+impl ValueTyDefId {
+ pub(crate) fn to_generic_def_id(self) -> Option<GenericDefId> {
+ match self {
+ Self::FunctionId(id) => Some(id.into()),
+ Self::StructId(id) => Some(id.into()),
+ Self::UnionId(id) => Some(id.into()),
+ Self::EnumVariantId(var) => Some(var.into()),
+ Self::ConstId(id) => Some(id.into()),
+ Self::StaticId(_) => None,
+ }
+ }
+}
+
/// Build the declared type of an item. This depends on the namespace; e.g. for
/// `struct Foo(usize)`, we have two types: The type of the struct itself, and
/// the constructor function `(usize) -> Foo` which lives in the values
@@ -1752,26 +1871,48 @@ pub(crate) fn const_or_path_to_chalk(
}
}
-/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
-/// num_vars_to_keep) by `TyKind::Unknown`.
+/// Replaces any 'free' `BoundVar`s in `s` by `TyKind::Error` from the perspective of generic
+/// parameter whose index is `param_index`. A `BoundVar` is free when it is or (syntactically)
+/// appears after the generic parameter of `param_index`.
fn fallback_bound_vars<T: TypeFoldable<Interner> + HasInterner<Interner = Interner>>(
s: T,
- num_vars_to_keep: usize,
+ param_index: usize,
+ parent_start: usize,
) -> T {
+ // Keep in mind that parent generic parameters, if any, come *after* those of the item in
+ // question. In the diagrams below, `c*` and `p*` represent generic parameters of the item and
+ // its parent respectively.
+ let is_allowed = |index| {
+ if param_index < parent_start {
+ // The parameter of `param_index` is one from the item in question. Any parent generic
+ // parameters or the item's generic parameters that come before `param_index` is
+ // allowed.
+ // [c1, .., cj, .., ck, p1, .., pl] where cj is `param_index`
+ // ^^^^^^ ^^^^^^^^^^ these are allowed
+ !(param_index..parent_start).contains(&index)
+ } else {
+ // The parameter of `param_index` is one from the parent generics. Only parent generic
+ // parameters that come before `param_index` are allowed.
+ // [c1, .., ck, p1, .., pj, .., pl] where pj is `param_index`
+ // ^^^^^^ these are allowed
+ (parent_start..param_index).contains(&index)
+ }
+ };
+
crate::fold_free_vars(
s,
|bound, binders| {
- if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
- TyKind::Error.intern(Interner)
- } else {
+ if bound.index_if_innermost().map_or(true, is_allowed) {
bound.shifted_in_from(binders).to_ty(Interner)
+ } else {
+ TyKind::Error.intern(Interner)
}
},
|ty, bound, binders| {
- if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
- unknown_const(ty.clone())
- } else {
+ if bound.index_if_innermost().map_or(true, is_allowed) {
bound.shifted_in_from(binders).to_const(Interner, ty)
+ } else {
+ unknown_const(ty.clone())
}
},
)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs
index d765fee0e..f80fb39c1 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs
@@ -103,6 +103,18 @@ impl From<crate::db::InternedClosureId> for chalk_ir::ClosureId<Interner> {
}
}
+impl From<chalk_ir::GeneratorId<Interner>> for crate::db::InternedGeneratorId {
+ fn from(id: chalk_ir::GeneratorId<Interner>) -> Self {
+ Self::from_intern_id(id.0)
+ }
+}
+
+impl From<crate::db::InternedGeneratorId> for chalk_ir::GeneratorId<Interner> {
+ fn from(id: crate::db::InternedGeneratorId) -> Self {
+ chalk_ir::GeneratorId(id.as_intern_id())
+ }
+}
+
pub fn to_foreign_def_id(id: TypeAliasId) -> ForeignDefId {
chalk_ir::ForeignDefId(salsa::InternKey::as_intern_id(&id))
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index 15df7b3dd..3a1a3f4fd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -1,7 +1,7 @@
//! This module is concerned with finding methods that a given type provides.
//! For details about how this works in rustc, see the method lookup page in the
//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
-//! and the corresponding code mostly in librustc_typeck/check/method/probe.rs.
+//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
use std::{iter, ops::ControlFlow, sync::Arc};
use arrayvec::ArrayVec;
@@ -336,7 +336,7 @@ impl InherentImpls {
}
}
-pub fn inherent_impl_crates_query(
+pub(crate) fn inherent_impl_crates_query(
db: &dyn HirDatabase,
krate: CrateId,
fp: TyFingerprint,
@@ -419,6 +419,55 @@ pub fn def_crates(
}
}
+pub fn lang_names_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, Name)> {
+ use hir_expand::name;
+ use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering};
+ Some(match op {
+ BinaryOp::LogicOp(_) => return None,
+ BinaryOp::ArithOp(aop) => match aop {
+ ArithOp::Add => (name!(add), name!(add)),
+ ArithOp::Mul => (name!(mul), name!(mul)),
+ ArithOp::Sub => (name!(sub), name!(sub)),
+ ArithOp::Div => (name!(div), name!(div)),
+ ArithOp::Rem => (name!(rem), name!(rem)),
+ ArithOp::Shl => (name!(shl), name!(shl)),
+ ArithOp::Shr => (name!(shr), name!(shr)),
+ ArithOp::BitXor => (name!(bitxor), name!(bitxor)),
+ ArithOp::BitOr => (name!(bitor), name!(bitor)),
+ ArithOp::BitAnd => (name!(bitand), name!(bitand)),
+ },
+ BinaryOp::Assignment { op: Some(aop) } => match aop {
+ ArithOp::Add => (name!(add_assign), name!(add_assign)),
+ ArithOp::Mul => (name!(mul_assign), name!(mul_assign)),
+ ArithOp::Sub => (name!(sub_assign), name!(sub_assign)),
+ ArithOp::Div => (name!(div_assign), name!(div_assign)),
+ ArithOp::Rem => (name!(rem_assign), name!(rem_assign)),
+ ArithOp::Shl => (name!(shl_assign), name!(shl_assign)),
+ ArithOp::Shr => (name!(shr_assign), name!(shr_assign)),
+ ArithOp::BitXor => (name!(bitxor_assign), name!(bitxor_assign)),
+ ArithOp::BitOr => (name!(bitor_assign), name!(bitor_assign)),
+ ArithOp::BitAnd => (name!(bitand_assign), name!(bitand_assign)),
+ },
+ BinaryOp::CmpOp(cop) => match cop {
+ CmpOp::Eq { negated: false } => (name!(eq), name!(eq)),
+ CmpOp::Eq { negated: true } => (name!(ne), name!(eq)),
+ CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
+ (name!(le), name!(partial_ord))
+ }
+ CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
+ (name!(lt), name!(partial_ord))
+ }
+ CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
+ (name!(ge), name!(partial_ord))
+ }
+ CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
+ (name!(gt), name!(partial_ord))
+ }
+ },
+ BinaryOp::Assignment { op: None } => return None,
+ })
+}
+
/// Look up the method with the given name.
pub(crate) fn lookup_method(
ty: &Canonical<Ty>,
@@ -605,7 +654,7 @@ fn find_matching_impl(
let r = table.run_in_snapshot(|table| {
let impl_data = db.impl_data(impl_);
let substs =
- TyBuilder::subst_for_def(db, impl_).fill_with_inference_vars(table).build();
+ TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build();
let impl_ty = db.impl_self_ty(impl_).substitute(Interner, &substs);
table
@@ -865,22 +914,10 @@ fn iterate_trait_method_candidates(
let db = table.db;
let env = table.trait_env.clone();
let self_is_array = matches!(self_ty.kind(Interner), chalk_ir::TyKind::Array(..));
- // if ty is `dyn Trait`, the trait doesn't need to be in scope
- let inherent_trait =
- self_ty.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
- let env_traits = matches!(self_ty.kind(Interner), TyKind::Placeholder(_))
- // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
- .then(|| {
- env.traits_in_scope_from_clauses(self_ty.clone())
- .flat_map(|t| all_super_traits(db.upcast(), t))
- })
- .into_iter()
- .flatten();
- let traits = inherent_trait.chain(env_traits).chain(traits_in_scope.iter().copied());
let canonical_self_ty = table.canonicalize(self_ty.clone()).value;
- 'traits: for t in traits {
+ 'traits: for &t in traits_in_scope {
let data = db.trait_data(t);
// Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during
@@ -930,6 +967,44 @@ fn iterate_inherent_methods(
) -> ControlFlow<()> {
let db = table.db;
let env = table.trait_env.clone();
+
+ // For trait object types and placeholder types with trait bounds, the methods of the trait and
+ // its super traits are considered inherent methods. This matters because these methods have
+ // higher priority than the other traits' methods, which would be considered in
+ // `iterate_trait_method_candidates()` only after this function.
+ match self_ty.kind(Interner) {
+ TyKind::Placeholder(_) => {
+ let env = table.trait_env.clone();
+ let traits = env
+ .traits_in_scope_from_clauses(self_ty.clone())
+ .flat_map(|t| all_super_traits(db.upcast(), t));
+ iterate_inherent_trait_methods(
+ self_ty,
+ table,
+ name,
+ receiver_ty,
+ receiver_adjustments.clone(),
+ callback,
+ traits,
+ )?;
+ }
+ TyKind::Dyn(_) => {
+ if let Some(principal_trait) = self_ty.dyn_trait() {
+ let traits = all_super_traits(db.upcast(), principal_trait);
+ iterate_inherent_trait_methods(
+ self_ty,
+ table,
+ name,
+ receiver_ty,
+ receiver_adjustments.clone(),
+ callback,
+ traits.into_iter(),
+ )?;
+ }
+ }
+ _ => {}
+ }
+
let def_crates = match def_crates(db, self_ty, env.krate) {
Some(k) => k,
None => return ControlFlow::Continue(()),
@@ -971,6 +1046,28 @@ fn iterate_inherent_methods(
}
return ControlFlow::Continue(());
+ fn iterate_inherent_trait_methods(
+ self_ty: &Ty,
+ table: &mut InferenceTable<'_>,
+ name: Option<&Name>,
+ receiver_ty: Option<&Ty>,
+ receiver_adjustments: Option<ReceiverAdjustments>,
+ callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
+ traits: impl Iterator<Item = TraitId>,
+ ) -> ControlFlow<()> {
+ let db = table.db;
+ for t in traits {
+ let data = db.trait_data(t);
+ for &(_, item) in data.items.iter() {
+ // We don't pass `visible_from_module` as all trait items should be visible.
+ if is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
+ callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
+ }
+ }
+ }
+ ControlFlow::Continue(())
+ }
+
fn impls_for_self_ty(
impls: &InherentImpls,
self_ty: &Ty,
@@ -1015,6 +1112,14 @@ pub fn resolve_indexing_op(
None
}
+macro_rules! check_that {
+ ($cond:expr) => {
+ if !$cond {
+ return false;
+ }
+ };
+}
+
fn is_valid_candidate(
table: &mut InferenceTable<'_>,
name: Option<&Name>,
@@ -1023,54 +1128,10 @@ fn is_valid_candidate(
self_ty: &Ty,
visible_from_module: Option<ModuleId>,
) -> bool {
- macro_rules! check_that {
- ($cond:expr) => {
- if !$cond {
- return false;
- }
- };
- }
-
let db = table.db;
match item {
AssocItemId::FunctionId(m) => {
- let data = db.function_data(m);
-
- check_that!(name.map_or(true, |n| n == &data.name));
- check_that!(visible_from_module.map_or(true, |from_module| {
- let v = db.function_visibility(m).is_visible_from(db.upcast(), from_module);
- if !v {
- cov_mark::hit!(autoderef_candidate_not_visible);
- }
- v
- }));
-
- table.run_in_snapshot(|table| {
- let subst = TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build();
- let expect_self_ty = match m.lookup(db.upcast()).container {
- ItemContainerId::TraitId(_) => {
- subst.at(Interner, 0).assert_ty_ref(Interner).clone()
- }
- ItemContainerId::ImplId(impl_id) => {
- subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
- }
- // We should only get called for associated items (impl/trait)
- ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
- unreachable!()
- }
- };
- check_that!(table.unify(&expect_self_ty, self_ty));
- if let Some(receiver_ty) = receiver_ty {
- check_that!(data.has_self_param());
-
- let sig = db.callable_item_signature(m.into());
- let expected_receiver =
- sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst);
-
- check_that!(table.unify(&receiver_ty, &expected_receiver));
- }
- true
- })
+ is_valid_fn_candidate(table, m, name, receiver_ty, self_ty, visible_from_module)
}
AssocItemId::ConstId(c) => {
let data = db.const_data(c);
@@ -1086,10 +1147,9 @@ fn is_valid_candidate(
}));
if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
let self_ty_matches = table.run_in_snapshot(|table| {
- let subst =
- TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build();
- let expected_self_ty =
- subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner);
+ let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id)
+ .fill_with_inference_vars(table)
+ .build();
table.unify(&expected_self_ty, &self_ty)
});
if !self_ty_matches {
@@ -1103,6 +1163,89 @@ fn is_valid_candidate(
}
}
+fn is_valid_fn_candidate(
+ table: &mut InferenceTable<'_>,
+ fn_id: FunctionId,
+ name: Option<&Name>,
+ receiver_ty: Option<&Ty>,
+ self_ty: &Ty,
+ visible_from_module: Option<ModuleId>,
+) -> bool {
+ let db = table.db;
+ let data = db.function_data(fn_id);
+
+ check_that!(name.map_or(true, |n| n == &data.name));
+ check_that!(visible_from_module.map_or(true, |from_module| {
+ let v = db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module);
+ if !v {
+ cov_mark::hit!(autoderef_candidate_not_visible);
+ }
+ v
+ }));
+
+ table.run_in_snapshot(|table| {
+ let container = fn_id.lookup(db.upcast()).container;
+ let (impl_subst, expect_self_ty) = match container {
+ ItemContainerId::ImplId(it) => {
+ let subst =
+ TyBuilder::subst_for_def(db, it, None).fill_with_inference_vars(table).build();
+ let self_ty = db.impl_self_ty(it).substitute(Interner, &subst);
+ (subst, self_ty)
+ }
+ ItemContainerId::TraitId(it) => {
+ let subst =
+ TyBuilder::subst_for_def(db, it, None).fill_with_inference_vars(table).build();
+ let self_ty = subst.at(Interner, 0).assert_ty_ref(Interner).clone();
+ (subst, self_ty)
+ }
+ _ => unreachable!(),
+ };
+
+ let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone()))
+ .fill_with_inference_vars(table)
+ .build();
+
+ check_that!(table.unify(&expect_self_ty, self_ty));
+
+ if let Some(receiver_ty) = receiver_ty {
+ check_that!(data.has_self_param());
+
+ let sig = db.callable_item_signature(fn_id.into());
+ let expected_receiver =
+ sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
+
+ check_that!(table.unify(&receiver_ty, &expected_receiver));
+ }
+
+ if let ItemContainerId::ImplId(impl_id) = container {
+ // We need to consider the bounds on the impl to distinguish functions of the same name
+ // for a type.
+ let predicates = db.generic_predicates(impl_id.into());
+ predicates
+ .iter()
+ .map(|predicate| {
+ let (p, b) = predicate
+ .clone()
+ .substitute(Interner, &impl_subst)
+ // Skipping the inner binders is ok, as we don't handle quantified where
+ // clauses yet.
+ .into_value_and_skipped_binders();
+ stdx::always!(b.len(Interner) == 0);
+ p
+ })
+ // It's ok to get ambiguity here, as we may not have enough information to prove
+ // obligations. We'll check if the user is calling the selected method properly
+ // later anyway.
+ .all(|p| table.try_obligation(p.cast(Interner)).is_some())
+ } else {
+ // For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in
+ // `iterate_trait_method_candidates()`.
+ // For others, this function shouldn't be called.
+ true
+ }
+ })
+}
+
pub fn implements_trait(
ty: &Canonical<Ty>,
db: &dyn HirDatabase,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
index dc7252f70..118e5311e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
@@ -10,7 +10,7 @@ use base_db::{
};
use hir_def::{db::DefDatabase, ModuleId};
use hir_expand::db::AstDatabase;
-use rustc_hash::{FxHashMap, FxHashSet};
+use stdx::hash::{NoHashHashMap, NoHashHashSet};
use syntax::TextRange;
use test_utils::extract_annotations;
@@ -80,7 +80,7 @@ impl FileLoader for TestDB {
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
FileLoaderDelegate(self).relevant_crates(file_id)
}
}
@@ -102,7 +102,7 @@ impl TestDB {
self.module_for_file_opt(file_id).unwrap()
}
- pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
+ pub(crate) fn extract_annotations(&self) -> NoHashHashMap<FileId, Vec<(TextRange, String)>> {
let mut files = Vec::new();
let crate_graph = self.crate_graph();
for krate in crate_graph.iter() {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index d2f13e435..ebbc54101 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -16,7 +16,7 @@ use base_db::{fixture::WithFixture, FileRange, SourceDatabaseExt};
use expect_test::Expect;
use hir_def::{
body::{Body, BodySourceMap, SyntheticSyntax},
- db::DefDatabase,
+ db::{DefDatabase, InternDatabase},
expr::{ExprId, PatId},
item_scope::ItemScope,
nameres::DefMap,
@@ -135,6 +135,10 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let loc = it.lookup(&db);
loc.source(&db).value.syntax().text_range().start()
}
+ DefWithBodyId::VariantId(it) => {
+ let loc = db.lookup_intern_enum(it.parent);
+ loc.source(&db).value.syntax().text_range().start()
+ }
});
let mut unexpected_type_mismatches = String::new();
for def in defs {
@@ -388,6 +392,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let loc = it.lookup(&db);
loc.source(&db).value.syntax().text_range().start()
}
+ DefWithBodyId::VariantId(it) => {
+ let loc = db.lookup_intern_enum(it.parent);
+ loc.source(&db).value.syntax().text_range().start()
+ }
});
for def in defs {
let (_body, source_map) = db.body_with_source_map(def);
@@ -453,6 +461,18 @@ fn visit_module(
let body = db.body(def);
visit_body(db, &body, cb);
}
+ ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => {
+ db.enum_data(it)
+ .variants
+ .iter()
+ .map(|(id, _)| hir_def::EnumVariantId { parent: it, local_id: id })
+ .for_each(|it| {
+ let def = it.into();
+ cb(def);
+ let body = db.body(def);
+ visit_body(db, &body, cb);
+ });
+ }
ModuleDefId::TraitId(it) => {
let trait_data = db.trait_data(it);
for &(_, item) in trait_data.items.iter() {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
index bf59fadc2..d301595bc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
@@ -295,6 +295,24 @@ fn foo() {
}
#[test]
+fn generator_yield_return_coerce() {
+ check_no_mismatches(
+ r#"
+fn test() {
+ let g = || {
+ yield &1u32;
+ yield &&1u32;
+ if true {
+ return &1u32;
+ }
+ &&1u32
+ };
+}
+ "#,
+ );
+}
+
+#[test]
fn assign_coerce() {
check_no_mismatches(
r"
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs
index 240942e48..8a8ff08cf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs
@@ -56,6 +56,28 @@ fn main() {
}
#[test]
+fn render_dyn_ty_independent_of_order() {
+ check_types_source_code(
+ r#"
+auto trait Send {}
+trait A {
+ type Assoc;
+}
+trait B: A {}
+
+fn test(
+ _: &(dyn A<Assoc = ()> + Send),
+ //^ &(dyn A<Assoc = ()> + Send)
+ _: &(dyn Send + A<Assoc = ()>),
+ //^ &(dyn A<Assoc = ()> + Send)
+ _: &dyn B<Assoc = ()>,
+ //^ &(dyn B<Assoc = ()>)
+) {}
+ "#,
+ );
+}
+
+#[test]
fn render_dyn_for_ty() {
// FIXME
check_types_source_code(
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
index a1ab6060e..b3adafaaf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
@@ -193,8 +193,6 @@ fn expr_macro_def_expanded_in_various_places() {
!0..6 '1isize': isize
!0..6 '1isize': isize
!0..6 '1isize': isize
- !0..6 '1isize': isize
- !0..6 '1isize': isize
39..442 '{ ...!(); }': ()
73..94 'spam!(...am!())': {unknown}
100..119 'for _ ...!() {}': ()
@@ -276,8 +274,6 @@ fn expr_macro_rules_expanded_in_various_places() {
!0..6 '1isize': isize
!0..6 '1isize': isize
!0..6 '1isize': isize
- !0..6 '1isize': isize
- !0..6 '1isize': isize
53..456 '{ ...!(); }': ()
87..108 'spam!(...am!())': {unknown}
114..133 'for _ ...!() {}': ()
@@ -312,7 +308,6 @@ fn expr_macro_expanded_in_stmts() {
}
"#,
expect![[r#"
- !0..8 'leta=();': ()
!3..4 'a': ()
!5..7 '()': ()
57..84 '{ ...); } }': ()
@@ -321,7 +316,7 @@ fn expr_macro_expanded_in_stmts() {
}
#[test]
-fn recurisve_macro_expanded_in_stmts() {
+fn recursive_macro_expanded_in_stmts() {
check_infer(
r#"
macro_rules! ng {
@@ -340,11 +335,6 @@ fn recurisve_macro_expanded_in_stmts() {
}
"#,
expect![[r#"
- !0..7 'leta=3;': ()
- !0..13 'ng!{[leta=3]}': ()
- !0..13 'ng!{[leta=]3}': ()
- !0..13 'ng!{[leta]=3}': ()
- !0..13 'ng!{[let]a=3}': ()
!3..4 'a': i32
!5..6 '3': i32
196..237 '{ ...= a; }': ()
@@ -369,8 +359,6 @@ fn recursive_inner_item_macro_rules() {
"#,
expect![[r#"
!0..1 '1': i32
- !0..7 'mac!($)': ()
- !0..26 'macro_...>{1};}': ()
107..143 '{ ...!(); }': ()
129..130 'a': i32
"#]],
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
index 68463dc06..ac8edb841 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
@@ -1219,6 +1219,40 @@ fn main() {
}
#[test]
+fn dyn_trait_method_priority() {
+ check_types(
+ r#"
+//- minicore: from
+trait Trait {
+ fn into(&self) -> usize { 0 }
+}
+
+fn foo(a: &dyn Trait) {
+ let _ = a.into();
+ //^usize
+}
+ "#,
+ );
+}
+
+#[test]
+fn trait_method_priority_for_placeholder_type() {
+ check_types(
+ r#"
+//- minicore: from
+trait Trait {
+ fn into(&self) -> usize { 0 }
+}
+
+fn foo<T: Trait>(a: &T) {
+ let _ = a.into();
+ //^usize
+}
+ "#,
+ );
+}
+
+#[test]
fn autoderef_visibility_field() {
check(
r#"
@@ -1790,3 +1824,46 @@ impl u16 {
"#,
)
}
+
+#[test]
+fn with_impl_bounds() {
+ check_types(
+ r#"
+trait Trait {}
+struct Foo<T>(T);
+impl Trait for isize {}
+
+impl<T: Trait> Foo<T> {
+ fn foo() -> isize { 0 }
+ fn bar(&self) -> isize { 0 }
+}
+
+impl Foo<()> {
+ fn foo() {}
+ fn bar(&self) {}
+}
+
+fn f() {
+ let _ = Foo::<isize>::foo();
+ //^isize
+ let _ = Foo(0isize).bar();
+ //^isize
+ let _ = Foo::<()>::foo();
+ //^()
+ let _ = Foo(()).bar();
+ //^()
+ let _ = Foo::<usize>::foo();
+ //^{unknown}
+ let _ = Foo(0usize).bar();
+ //^{unknown}
+}
+
+fn g<T: Trait>(a: T) {
+ let _ = Foo::<T>::foo();
+ //^isize
+ let _ = Foo(a).bar();
+ //^isize
+}
+ "#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
index 399553356..74de33117 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
@@ -316,6 +316,51 @@ fn infer_pattern_match_string_literal() {
}
#[test]
+fn infer_pattern_match_byte_string_literal() {
+ check_infer_with_mismatches(
+ r#"
+ //- minicore: index
+ struct S;
+ impl<T, const N: usize> core::ops::Index<S> for [T; N] {
+ type Output = [u8];
+ fn index(&self, index: core::ops::RangeFull) -> &Self::Output {
+ loop {}
+ }
+ }
+ fn test(v: [u8; 3]) {
+ if let b"foo" = &v[S] {}
+ if let b"foo" = &v {}
+ }
+ "#,
+ expect![[r#"
+ 105..109 'self': &[T; N]
+ 111..116 'index': {unknown}
+ 157..180 '{ ... }': &[u8]
+ 167..174 'loop {}': !
+ 172..174 '{}': ()
+ 191..192 'v': [u8; 3]
+ 203..261 '{ ...v {} }': ()
+ 209..233 'if let...[S] {}': ()
+ 212..230 'let b"... &v[S]': bool
+ 216..222 'b"foo"': &[u8]
+ 216..222 'b"foo"': &[u8]
+ 225..230 '&v[S]': &[u8]
+ 226..227 'v': [u8; 3]
+ 226..230 'v[S]': [u8]
+ 228..229 'S': S
+ 231..233 '{}': ()
+ 238..259 'if let... &v {}': ()
+ 241..256 'let b"foo" = &v': bool
+ 245..251 'b"foo"': &[u8; 3]
+ 245..251 'b"foo"': &[u8; 3]
+ 254..256 '&v': &[u8; 3]
+ 255..256 'v': [u8; 3]
+ 257..259 '{}': ()
+ "#]],
+ );
+}
+
+#[test]
fn infer_pattern_match_or() {
check_infer_with_mismatches(
r#"
@@ -444,6 +489,42 @@ fn infer_adt_pattern() {
}
#[test]
+fn tuple_struct_destructured_with_self() {
+ check_infer(
+ r#"
+struct Foo(usize,);
+impl Foo {
+ fn f() {
+ let Self(s,) = &Foo(0,);
+ let Self(s,) = &mut Foo(0,);
+ let Self(s,) = Foo(0,);
+ }
+}
+ "#,
+ expect![[r#"
+ 42..151 '{ ... }': ()
+ 56..64 'Self(s,)': Foo
+ 61..62 's': &usize
+ 67..75 '&Foo(0,)': &Foo
+ 68..71 'Foo': Foo(usize) -> Foo
+ 68..75 'Foo(0,)': Foo
+ 72..73 '0': usize
+ 89..97 'Self(s,)': Foo
+ 94..95 's': &mut usize
+ 100..112 '&mut Foo(0,)': &mut Foo
+ 105..108 'Foo': Foo(usize) -> Foo
+ 105..112 'Foo(0,)': Foo
+ 109..110 '0': usize
+ 126..134 'Self(s,)': Foo
+ 131..132 's': usize
+ 137..140 'Foo': Foo(usize) -> Foo
+ 137..144 'Foo(0,)': Foo
+ 141..142 '0': usize
+ "#]],
+ );
+}
+
+#[test]
fn enum_variant_through_self_in_pattern() {
check_infer(
r#"
@@ -989,3 +1070,13 @@ fn main() {
"#,
);
}
+
+#[test]
+fn cfg_params() {
+ check_types(
+ r#"
+fn my_fn(#[cfg(feature = "feature")] u8: u8, u32: u32) {}
+ //^^^ u32
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index 93a88ab58..a155adcec 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -573,7 +573,6 @@ fn issue_6811() {
}
"#,
expect![[r#"
- !0..16 'let_a=...t_b=1;': ()
!3..5 '_a': i32
!6..7 '1': i32
!11..13 '_b': i32
@@ -1489,7 +1488,6 @@ fn regression_11688_4() {
#[test]
fn gat_crash_1() {
- cov_mark::check!(ignore_gats);
check_no_mismatches(
r#"
trait ATrait {}
@@ -1527,6 +1525,26 @@ unsafe impl Storage for InlineStorage {
}
#[test]
+fn gat_crash_3() {
+ check_no_mismatches(
+ r#"
+trait Collection {
+type Item;
+type Member<T>: Collection<Item = T>;
+fn add(&mut self, value: Self::Item) -> Result<(), Self::Error>;
+}
+struct ConstGen<T, const N: usize> {
+data: [T; N],
+}
+impl<T, const N: usize> Collection for ConstGen<T, N> {
+type Item = T;
+type Member<U> = ConstGen<U, N>;
+}
+ "#,
+ );
+}
+
+#[test]
fn cfgd_out_self_param() {
cov_mark::check!(cfgd_out_self_param);
check_no_mismatches(
@@ -1648,3 +1666,44 @@ fn main() {
"#]],
);
}
+
+#[test]
+fn trailing_empty_macro() {
+ check_no_mismatches(
+ r#"
+macro_rules! m2 {
+ ($($t:tt)*) => {$($t)*};
+}
+
+fn macrostmts() -> u8 {
+ m2! { 0 }
+ m2! {}
+}
+ "#,
+ );
+}
+
+#[test]
+fn dyn_with_unresolved_trait() {
+ check_types(
+ r#"
+fn foo(a: &dyn DoesNotExist) {
+ a.bar();
+ //^&{unknown}
+}
+ "#,
+ );
+}
+
+#[test]
+fn self_assoc_with_const_generics_crash() {
+ check_no_mismatches(
+ r#"
+trait Trait { type Item; }
+impl<T, const N: usize> Trait for [T; N] {
+ type Item = ();
+ fn f<U>(_: Self::Item) {}
+}
+ "#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 5b08f5521..080e2ac1b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -1693,16 +1693,16 @@ fn infer_type_param() {
fn infer_const() {
check_infer(
r#"
- struct Foo;
- impl Foo { const ASSOC_CONST: u32 = 0; }
- const GLOBAL_CONST: u32 = 101;
- fn test() {
- const LOCAL_CONST: u32 = 99;
- let x = LOCAL_CONST;
- let z = GLOBAL_CONST;
- let id = Foo::ASSOC_CONST;
- }
- "#,
+struct Foo;
+impl Foo { const ASSOC_CONST: u32 = 0; }
+const GLOBAL_CONST: u32 = 101;
+fn test() {
+ const LOCAL_CONST: u32 = 99;
+ let x = LOCAL_CONST;
+ let z = GLOBAL_CONST;
+ let id = Foo::ASSOC_CONST;
+}
+"#,
expect![[r#"
48..49 '0': u32
79..82 '101': u32
@@ -1722,17 +1722,17 @@ fn infer_const() {
fn infer_static() {
check_infer(
r#"
- static GLOBAL_STATIC: u32 = 101;
- static mut GLOBAL_STATIC_MUT: u32 = 101;
- fn test() {
- static LOCAL_STATIC: u32 = 99;
- static mut LOCAL_STATIC_MUT: u32 = 99;
- let x = LOCAL_STATIC;
- let y = LOCAL_STATIC_MUT;
- let z = GLOBAL_STATIC;
- let w = GLOBAL_STATIC_MUT;
- }
- "#,
+static GLOBAL_STATIC: u32 = 101;
+static mut GLOBAL_STATIC_MUT: u32 = 101;
+fn test() {
+ static LOCAL_STATIC: u32 = 99;
+ static mut LOCAL_STATIC_MUT: u32 = 99;
+ let x = LOCAL_STATIC;
+ let y = LOCAL_STATIC_MUT;
+ let z = GLOBAL_STATIC;
+ let w = GLOBAL_STATIC_MUT;
+}
+"#,
expect![[r#"
28..31 '101': u32
69..72 '101': u32
@@ -1752,6 +1752,41 @@ fn infer_static() {
}
#[test]
+fn infer_enum_variant() {
+ check_infer(
+ r#"
+enum Foo {
+ A = 15,
+ B = Foo::A as isize + 1
+}
+"#,
+ expect![[r#"
+ 19..21 '15': isize
+ 31..37 'Foo::A': Foo
+ 31..46 'Foo::A as isize': isize
+ 31..50 'Foo::A...ze + 1': isize
+ 49..50 '1': isize
+ "#]],
+ );
+ check_infer(
+ r#"
+#[repr(u32)]
+enum Foo {
+ A = 15,
+ B = Foo::A as u32 + 1
+}
+"#,
+ expect![[r#"
+ 32..34 '15': u32
+ 44..50 'Foo::A': Foo
+ 44..57 'Foo::A as u32': u32
+ 44..61 'Foo::A...32 + 1': u32
+ 60..61 '1': u32
+ "#]],
+ );
+}
+
+#[test]
fn shadowing_primitive() {
check_types(
r#"
@@ -1918,6 +1953,88 @@ fn closure_return_inferred() {
}
#[test]
+fn generator_types_inferred() {
+ check_infer(
+ r#"
+//- minicore: generator, deref
+use core::ops::{Generator, GeneratorState};
+use core::pin::Pin;
+
+fn f(v: i64) {}
+fn test() {
+ let mut g = |r| {
+ let a = yield 0;
+ let a = yield 1;
+ let a = yield 2;
+ "return value"
+ };
+
+ match Pin::new(&mut g).resume(0usize) {
+ GeneratorState::Yielded(y) => { f(y); }
+ GeneratorState::Complete(r) => {}
+ }
+}
+ "#,
+ expect![[r#"
+ 70..71 'v': i64
+ 78..80 '{}': ()
+ 91..362 '{ ... } }': ()
+ 101..106 'mut g': |usize| yields i64 -> &str
+ 109..218 '|r| { ... }': |usize| yields i64 -> &str
+ 110..111 'r': usize
+ 113..218 '{ ... }': &str
+ 127..128 'a': usize
+ 131..138 'yield 0': usize
+ 137..138 '0': i64
+ 152..153 'a': usize
+ 156..163 'yield 1': usize
+ 162..163 '1': i64
+ 177..178 'a': usize
+ 181..188 'yield 2': usize
+ 187..188 '2': i64
+ 198..212 '"return value"': &str
+ 225..360 'match ... }': ()
+ 231..239 'Pin::new': fn new<&mut |usize| yields i64 -> &str>(&mut |usize| yields i64 -> &str) -> Pin<&mut |usize| yields i64 -> &str>
+ 231..247 'Pin::n...mut g)': Pin<&mut |usize| yields i64 -> &str>
+ 231..262 'Pin::n...usize)': GeneratorState<i64, &str>
+ 240..246 '&mut g': &mut |usize| yields i64 -> &str
+ 245..246 'g': |usize| yields i64 -> &str
+ 255..261 '0usize': usize
+ 273..299 'Genera...ded(y)': GeneratorState<i64, &str>
+ 297..298 'y': i64
+ 303..312 '{ f(y); }': ()
+ 305..306 'f': fn f(i64)
+ 305..309 'f(y)': ()
+ 307..308 'y': i64
+ 321..348 'Genera...ete(r)': GeneratorState<i64, &str>
+ 346..347 'r': &str
+ 352..354 '{}': ()
+ "#]],
+ );
+}
+
+#[test]
+fn generator_resume_yield_return_unit() {
+ check_no_mismatches(
+ r#"
+//- minicore: generator, deref
+use core::ops::{Generator, GeneratorState};
+use core::pin::Pin;
+fn test() {
+ let mut g = || {
+ let () = yield;
+ };
+
+ match Pin::new(&mut g).resume(()) {
+ GeneratorState::Yielded(()) => {}
+ GeneratorState::Complete(()) => {}
+ }
+}
+ "#,
+ );
+}
+
+#[test]
fn fn_pointer_return() {
check_infer(
r#"
@@ -2549,7 +2666,6 @@ impl B for Astruct {}
expect![[r#"
569..573 'self': Box<[T], A>
602..634 '{ ... }': Vec<T, A>
- 612..628 'unimpl...ted!()': Vec<T, A>
648..761 '{ ...t]); }': ()
658..661 'vec': Vec<i32, Global>
664..679 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
@@ -3070,3 +3186,17 @@ fn main() {
"#,
);
}
+
+#[test]
+fn nested_break() {
+ check_no_mismatches(
+ r#"
+fn func() {
+ let int = loop {
+ break 0;
+ break (break 0);
+ };
+}
+ "#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index 75802a5eb..555b6972f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -138,6 +138,31 @@ fn not_send() -> Box<dyn Future<Output = ()> + 'static> {
}
#[test]
+fn into_future_trait() {
+ check_types(
+ r#"
+//- minicore: future
+struct Futurable;
+impl core::future::IntoFuture for Futurable {
+ type Output = u64;
+ type IntoFuture = IntFuture;
+}
+
+struct IntFuture;
+impl core::future::Future for IntFuture {
+ type Output = u64;
+}
+
+fn test() {
+ let r = Futurable;
+ let v = r.await;
+ v;
+} //^ u64
+"#,
+ );
+}
+
+#[test]
fn infer_try() {
check_types(
r#"
@@ -254,6 +279,10 @@ fn test() {
pub mod iter {
pub trait IntoIterator {
type Item;
+ type IntoIter: Iterator<Item = Self::Item>;
+ }
+ pub trait Iterator {
+ type Item;
}
}
pub mod prelude {
@@ -272,7 +301,13 @@ pub mod collections {
}
impl<T> IntoIterator for Vec<T> {
- type Item=T;
+ type Item = T;
+ type IntoIter = IntoIter<T>;
+ }
+
+ struct IntoIter<T> {}
+ impl<T> Iterator for IntoIter<T> {
+ type Item = T;
}
}
"#,
@@ -1476,6 +1511,34 @@ fn test(x: Trait, y: &Trait) -> u64 {
165..172 'z.foo()': u64
"#]],
);
+
+ check_infer_with_mismatches(
+ r#"
+//- minicore: fn, coerce_unsized
+struct S;
+impl S {
+ fn foo(&self) {}
+}
+fn f(_: &Fn(S)) {}
+fn main() {
+ f(&|number| number.foo());
+}
+ "#,
+ expect![[r#"
+ 31..35 'self': &S
+ 37..39 '{}': ()
+ 47..48 '_': &dyn Fn(S)
+ 58..60 '{}': ()
+ 71..105 '{ ...()); }': ()
+ 77..78 'f': fn f(&dyn Fn(S))
+ 77..102 'f(&|nu...foo())': ()
+ 79..101 '&|numb....foo()': &|S| -> ()
+ 80..101 '|numbe....foo()': |S| -> ()
+ 81..87 'number': S
+ 89..95 'number': S
+ 89..101 'number.foo()': ()
+ "#]],
+ )
}
#[test]
@@ -3780,3 +3843,123 @@ fn test() {
"#,
)
}
+
+#[test]
+fn dyn_multiple_auto_traits_in_different_order() {
+ check_no_mismatches(
+ r#"
+auto trait Send {}
+auto trait Sync {}
+
+fn f(t: &(dyn Sync + Send)) {}
+fn g(t: &(dyn Send + Sync)) {
+ f(t);
+}
+ "#,
+ );
+
+ check_no_mismatches(
+ r#"
+auto trait Send {}
+auto trait Sync {}
+trait T {}
+
+fn f(t: &(dyn T + Send + Sync)) {}
+fn g(t: &(dyn Sync + T + Send)) {
+ f(t);
+}
+ "#,
+ );
+
+ check_infer_with_mismatches(
+ r#"
+auto trait Send {}
+auto trait Sync {}
+trait T1 {}
+trait T2 {}
+
+fn f(t: &(dyn T1 + T2 + Send + Sync)) {}
+fn g(t: &(dyn Sync + T2 + T1 + Send)) {
+ f(t);
+}
+ "#,
+ expect![[r#"
+ 68..69 't': &{unknown}
+ 101..103 '{}': ()
+ 109..110 't': &{unknown}
+ 142..155 '{ f(t); }': ()
+ 148..149 'f': fn f(&{unknown})
+ 148..152 'f(t)': ()
+ 150..151 't': &{unknown}
+ "#]],
+ );
+
+ check_no_mismatches(
+ r#"
+auto trait Send {}
+auto trait Sync {}
+trait T {
+ type Proj: Send + Sync;
+}
+
+fn f(t: &(dyn T<Proj = ()> + Send + Sync)) {}
+fn g(t: &(dyn Sync + T<Proj = ()> + Send)) {
+ f(t);
+}
+ "#,
+ );
+}
+
+#[test]
+fn dyn_multiple_projection_bounds() {
+ check_no_mismatches(
+ r#"
+trait Trait {
+ type T;
+ type U;
+}
+
+fn f(t: &dyn Trait<T = (), U = ()>) {}
+fn g(t: &dyn Trait<U = (), T = ()>) {
+ f(t);
+}
+ "#,
+ );
+
+ check_types(
+ r#"
+trait Trait {
+ type T;
+}
+
+fn f(t: &dyn Trait<T = (), T = ()>) {}
+ //^&{unknown}
+ "#,
+ );
+}
+
+#[test]
+fn dyn_duplicate_auto_trait() {
+ check_no_mismatches(
+ r#"
+auto trait Send {}
+
+fn f(t: &(dyn Send + Send)) {}
+fn g(t: &(dyn Send)) {
+ f(t);
+}
+ "#,
+ );
+
+ check_no_mismatches(
+ r#"
+auto trait Send {}
+trait T {}
+
+fn f(t: &(dyn T + Send + Send)) {}
+fn g(t: &(dyn T + Send)) {
+ f(t);
+}
+ "#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
index 77afeb321..c425f35ac 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
@@ -1,6 +1,6 @@
//! Trait solving using Chalk.
-use std::env::var;
+use std::{env::var, sync::Arc};
use chalk_ir::GoalData;
use chalk_recursive::Cache;
@@ -12,8 +12,9 @@ use stdx::panic_context;
use syntax::SmolStr;
use crate::{
- db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment,
- Interner, Solution, TraitRefExt, Ty, TyKind, WhereClause,
+ db::HirDatabase, infer::unify::InferenceTable, AliasEq, AliasTy, Canonical, DomainGoal, Goal,
+ Guidance, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, Solution, TraitRefExt, Ty,
+ TyKind, WhereClause,
};
/// This controls how much 'time' we give the Chalk solver before giving up.
@@ -64,6 +65,16 @@ impl TraitEnvironment {
}
}
+pub(crate) fn normalize_projection_query(
+ db: &dyn HirDatabase,
+ projection: ProjectionTy,
+ env: Arc<TraitEnvironment>,
+) -> Ty {
+ let mut table = InferenceTable::new(db, env);
+ let ty = table.normalize_projection_ty(projection);
+ table.resolve_completely(ty)
+}
+
/// Solve a trait goal using Chalk.
pub(crate) fn trait_solve_query(
db: &dyn HirDatabase,
@@ -84,7 +95,7 @@ pub(crate) fn trait_solve_query(
..
}))) = &goal.value.goal.data(Interner)
{
- if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(Interner).kind(Interner) {
+ if 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));
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
index 83319755d..e54bcb421 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
@@ -4,7 +4,7 @@
use std::iter;
use base_db::CrateId;
-use chalk_ir::{fold::Shift, BoundVar, DebruijnIndex};
+use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex};
use hir_def::{
db::DefDatabase,
generics::{
@@ -24,8 +24,7 @@ use smallvec::{smallvec, SmallVec};
use syntax::SmolStr;
use crate::{
- db::HirDatabase, ChalkTraitId, ConstData, ConstValue, GenericArgData, Interner, Substitution,
- TraitRef, TraitRefExt, TyKind, WhereClause,
+ db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, WhereClause,
};
pub(crate) fn fn_traits(db: &dyn DefDatabase, krate: CrateId) -> impl Iterator<Item = TraitId> {
@@ -174,22 +173,6 @@ pub(super) fn associated_type_by_name_including_super_traits(
pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def)));
- if parent_generics.is_some() && matches!(def, GenericDefId::TypeAliasId(_)) {
- let params = db.generic_params(def);
- let has_consts =
- params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)));
- return if has_consts {
- // XXX: treat const generic associated types as not existing to avoid crashes (#11769)
- //
- // Chalk expects the inner associated type's parameters to come
- // *before*, not after the trait's generics as we've always done it.
- // Adapting to this requires a larger refactoring
- cov_mark::hit!(ignore_gats);
- Generics { def, params: Interned::new(Default::default()), parent_generics }
- } else {
- Generics { def, params, parent_generics }
- };
- }
Generics { def, params: db.generic_params(def), parent_generics }
}
@@ -212,23 +195,30 @@ impl Generics {
})
}
- /// Iterator over types and const params of parent, then self.
+ /// Iterator over types and const params of self, then parent.
pub(crate) fn iter<'a>(
&'a self,
) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
let to_toc_id = |it: &'a Generics| {
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p)
};
- self.parent_generics()
- .into_iter()
- .flat_map(move |it| it.params.iter().map(to_toc_id(it)))
- .chain(self.params.iter().map(to_toc_id(self)))
+ self.params.iter().map(to_toc_id(self)).chain(self.iter_parent())
+ }
+
+ /// Iterate over types and const params without parent params.
+ pub(crate) fn iter_self<'a>(
+ &'a self,
+ ) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
+ let to_toc_id = |it: &'a Generics| {
+ move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p)
+ };
+ self.params.iter().map(to_toc_id(self))
}
/// Iterator over types and const params of parent.
pub(crate) fn iter_parent<'a>(
&'a self,
- ) -> impl Iterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
+ ) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
self.parent_generics().into_iter().flat_map(|it| {
let to_toc_id =
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p);
@@ -236,12 +226,18 @@ impl Generics {
})
}
+ /// Returns total number of generic parameters in scope, including those from parent.
pub(crate) fn len(&self) -> usize {
let parent = self.parent_generics().map_or(0, Generics::len);
let child = self.params.type_or_consts.len();
parent + child
}
+ /// Returns numbers of generic parameters excluding those from parent.
+ pub(crate) fn len_self(&self) -> usize {
+ self.params.type_or_consts.len()
+ }
+
/// (parent total, self param, type param list, const param list, impl trait)
pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize) {
let ty_iter = || self.params.iter().filter_map(|x| x.1.type_param());
@@ -264,21 +260,19 @@ impl Generics {
fn find_param(&self, param: TypeOrConstParamId) -> Option<(usize, &TypeOrConstParamData)> {
if param.parent == self.def {
- let (idx, (_local_id, data)) = self
- .params
- .iter()
- .enumerate()
- .find(|(_, (idx, _))| *idx == param.local_id)
- .unwrap();
- let parent_len = self.parent_generics().map_or(0, Generics::len);
- Some((parent_len + idx, data))
+ let (idx, (_local_id, data)) =
+ self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?;
+ Some((idx, data))
} else {
- self.parent_generics().and_then(|g| g.find_param(param))
+ self.parent_generics()
+ .and_then(|g| g.find_param(param))
+ // Remember that parent parameters come after parameters for self.
+ .map(|(idx, data)| (self.len_self() + idx, data))
}
}
- fn parent_generics(&self) -> Option<&Generics> {
- self.parent_generics.as_ref().map(|it| &**it)
+ pub(crate) fn parent_generics(&self) -> Option<&Generics> {
+ self.parent_generics.as_deref()
}
/// Returns a Substitution that replaces each parameter by a bound variable.
@@ -290,18 +284,10 @@ impl Generics {
Substitution::from_iter(
Interner,
self.iter_id().enumerate().map(|(idx, id)| match id {
- Either::Left(_) => GenericArgData::Ty(
- TyKind::BoundVar(BoundVar::new(debruijn, idx)).intern(Interner),
- )
- .intern(Interner),
- Either::Right(id) => GenericArgData::Const(
- ConstData {
- value: ConstValue::BoundVar(BoundVar::new(debruijn, idx)),
- ty: db.const_param_ty(id),
- }
- .intern(Interner),
- )
- .intern(Interner),
+ Either::Left(_) => BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner),
+ Either::Right(id) => BoundVar::new(debruijn, idx)
+ .to_const(Interner, db.const_param_ty(id))
+ .cast(Interner),
}),
)
}
@@ -311,18 +297,12 @@ impl Generics {
Substitution::from_iter(
Interner,
self.iter_id().map(|id| match id {
- Either::Left(id) => GenericArgData::Ty(
- TyKind::Placeholder(crate::to_placeholder_idx(db, id.into())).intern(Interner),
- )
- .intern(Interner),
- Either::Right(id) => GenericArgData::Const(
- ConstData {
- value: ConstValue::Placeholder(crate::to_placeholder_idx(db, id.into())),
- ty: db.const_param_ty(id),
- }
- .intern(Interner),
- )
- .intern(Interner),
+ Either::Left(id) => {
+ crate::to_placeholder_idx(db, id.into()).to_ty(Interner).cast(Interner)
+ }
+ Either::Right(id) => crate::to_placeholder_idx(db, id.into())
+ .to_const(Interner, db.const_param_ty(id))
+ .cast(Interner),
}),
)
}