diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/hir-ty')
30 files changed, 1229 insertions, 520 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 7f143f396..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.84.0", default-features = false } -chalk-ir = "0.84.0" -chalk-recursive = { version = "0.84.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 344036dd8..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; @@ -123,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 4a5533c64..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, ¶meters)) + Some(sig.substitute(Interner, parameters)) } TyKind::Closure(.., substs) => { let sig_param = substs.at(Interner, 0).assert_ty_ref(Interner); @@ -166,6 +166,8 @@ impl TyExt for Ty { 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), @@ -260,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<_>>(); @@ -331,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 { @@ -347,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 6ecb6e6fd..2c0c6e0b8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -7,14 +7,17 @@ use std::{ 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, @@ -77,6 +80,7 @@ pub enum ConstEvalError { #[derive(Debug, Clone, PartialEq, Eq)] pub enum ComputedExpr { Literal(Literal), + Enum(String, EnumVariantId, Literal), Tuple(Box<[ComputedExpr]>), } @@ -104,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 { @@ -148,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); @@ -167,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) { @@ -198,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( @@ -219,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 { @@ -339,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")), } } @@ -412,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> { @@ -433,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/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(¶meters.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, ¶meters); 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 10ffde87e..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(); @@ -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,6 +419,8 @@ 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>, } @@ -476,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, @@ -673,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)); } @@ -696,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) } @@ -708,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 { @@ -799,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) @@ -878,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]) } 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 2d04a864a..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,7 +10,10 @@ use chalk_ir::{ cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind, }; use hir_def::{ - expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, LabelId, Literal, Statement, UnaryOp}, + expr::{ + ArithOp, Array, BinaryOp, ClosureKind, CmpOp, Expr, ExprId, LabelId, Literal, Statement, + UnaryOp, + }, generics::TypeOrConstParamData, path::{GenericArg, GenericArgs}, resolver::resolver_for_expr, @@ -204,8 +207,10 @@ impl<'a> InferenceContext<'a> { } &Expr::For { iterable, body, pat, label } => { let iterable_ty = self.infer_expr(iterable, &Expectation::none()); - let pat_ty = + 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.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| { @@ -216,7 +221,7 @@ impl<'a> InferenceContext<'a> { 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(); @@ -244,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) { @@ -266,6 +291,8 @@ 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.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| { this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); @@ -273,8 +300,9 @@ impl<'a> InferenceContext<'a> { 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()); @@ -423,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); @@ -952,11 +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 = lang_names_for_bin_op(op).and_then(|(name, lang_item)| { - self.db.trait_data(self.resolve_lang_item(lang_item)?.as_trait()?).method_by_name(&name) + 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 func = match func { - Some(func) => 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)); @@ -966,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(); @@ -1245,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 @@ -1266,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, @@ -1290,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(_) => { @@ -1327,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. + ¶meters.as_slice(Interner)[params_len - trait_params_len..], ); self.push_obligation( TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs } 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/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index a82a331d4..c4b700cbc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -196,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. @@ -268,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 }) } } @@ -306,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 } @@ -324,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( @@ -337,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>>( @@ -365,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 } @@ -383,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 @@ -404,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 } @@ -421,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, @@ -447,7 +427,7 @@ where } } - fn fold_free_var_ty( + fn try_fold_free_var_ty( &mut self, _bound_var: BoundVar, _outer_binder: DebruijnIndex, @@ -461,7 +441,7 @@ where } } - fn fold_inference_const( + fn try_fold_inference_const( &mut self, ty: Ty, _var: InferenceVar, @@ -474,7 +454,7 @@ where } } - fn fold_free_var_const( + fn try_fold_free_var_const( &mut self, ty: Ty, _bound_var: BoundVar, @@ -487,7 +467,7 @@ where } } - fn fold_inference_lifetime( + fn try_fold_inference_lifetime( &mut self, _var: InferenceVar, _outer_binder: DebruijnIndex, @@ -499,7 +479,7 @@ where } } - fn fold_free_var_lifetime( + fn try_fold_free_var_lifetime( &mut self, _bound_var: BoundVar, _outer_binder: DebruijnIndex, @@ -512,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 532544fee..223d705b1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -306,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, @@ -319,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) } @@ -499,14 +499,31 @@ impl<'a> TyLoweringContext<'a> { .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()); @@ -663,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) { @@ -757,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) } @@ -981,10 +992,11 @@ 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 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. - // This invariant is used by `TyExt::dyn_trait()` and chalk. + // 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() @@ -1035,6 +1047,12 @@ impl<'a> TyLoweringContext<'a> { 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(); @@ -1046,7 +1064,8 @@ impl<'a> TyLoweringContext<'a> { 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 or associated type rebound) + // FIXME: report error + // (additional non-auto traits, associated type rebound, or no resolved trait) TyKind::Error.intern(Interner) } } @@ -1139,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() { @@ -1160,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); } @@ -1405,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() @@ -1417,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(); @@ -1446,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(); @@ -1633,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 @@ -1816,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 9a63d5013..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; @@ -654,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 @@ -914,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 @@ -979,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(()), @@ -1020,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, @@ -1099,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 { @@ -1138,31 +1185,26 @@ fn is_valid_fn_candidate( table.run_in_snapshot(|table| { let container = fn_id.lookup(db.upcast()).container; - let impl_subst = match container { + let (impl_subst, expect_self_ty) = match container { ItemContainerId::ImplId(it) => { - TyBuilder::subst_for_def(db, it).fill_with_inference_vars(table).build() + 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) => { - TyBuilder::subst_for_def(db, it).fill_with_inference_vars(table).build() + 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) - .use_parent_substs(&impl_subst) + let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone())) .fill_with_inference_vars(table) .build(); - let expect_self_ty = match container { - ItemContainerId::TraitId(_) => fn_subst.at(Interner, 0).assert_ty_ref(Interner).clone(), - ItemContainerId::ImplId(impl_id) => { - fn_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 { 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/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 81588a7c4..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#" 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 eb04bf877..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 @@ -1070,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 23e51a9c1..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 @@ -1488,7 +1488,6 @@ fn regression_11688_4() { #[test] fn gat_crash_1() { - cov_mark::check!(ignore_gats); check_no_mismatches( r#" trait ATrait {} @@ -1527,30 +1526,22 @@ unsafe impl Storage for InlineStorage { #[test] fn gat_crash_3() { - // FIXME: This test currently crashes rust analyzer in a debug build but not in a - // release build (i.e. for the user). With the assumption that tests will always be run - // in debug mode, we catch the unwind and expect that it panicked. See the - // [`crate::utils::generics`] function for more information. - cov_mark::check!(ignore_gats); - std::panic::catch_unwind(|| { - check_no_mismatches( - r#" + check_no_mismatches( + r#" trait Collection { - type Item; - type Member<T>: Collection<Item = T>; - fn add(&mut self, value: Self::Item) -> Result<(), Self::Error>; +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], +data: [T; N], } impl<T, const N: usize> Collection for ConstGen<T, N> { - type Item = T; - type Member<U> = ConstGen<U, N>; +type Item = T; +type Member<U> = ConstGen<U, N>; } - "#, - ); - }) - .expect_err("must panic"); + "#, + ); } #[test] @@ -1691,3 +1682,28 @@ fn macrostmts() -> u8 { "#, ); } + +#[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 4ea103e5d..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#" 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 21a863197..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 @@ -279,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 { @@ -297,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; } } "#, 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 d6638db02..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,31 +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 parent_params = &parent_generics.as_ref().unwrap().params; - let has_consts = - params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_))); - let parent_has_consts = - parent_params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_))); - return if has_consts || parent_has_consts { - // XXX: treat const generic associated types as not existing to avoid crashes - // (#11769) - // - // Note: Also crashes when the parent has const generics (also even if the GAT - // doesn't use them), see `tests::regression::gat_crash_3` for an example. - // Avoids that by disabling GATs when the parent (i.e. `impl` block) has - // const generics (#12193). - // - // 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 } } @@ -221,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); @@ -245,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()); @@ -275,15 +262,17 @@ impl Generics { if param.parent == self.def { let (idx, (_local_id, data)) = self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?; - let parent_len = self.parent_generics().map_or(0, Generics::len); - Some((parent_len + idx, data)) + 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. @@ -295,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), }), ) } @@ -316,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), }), ) } |