diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 958882d43e..9afe07932e 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -79,7 +79,7 @@ enum Scope { ExprScope(ExprScope), } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum TypeNs { SelfType(ImplId), GenericParam(TypeParamId), diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 08ba80cdff..23206e93bc 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use std::{convert::identity, ops::Index}; -use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags}; +use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags}; use either::Either; use hir_def::{ body::Body, @@ -37,10 +37,10 @@ use rustc_hash::{FxHashMap, FxHashSet}; use stdx::{always, never}; use crate::{ - db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany, - lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, AliasEq, AliasTy, Const, - DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, - Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, + db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode, + static_lifetime, to_assoc_type_id, AliasEq, AliasTy, DomainGoal, GenericArg, Goal, ImplTraitId, + InEnvironment, Interner, ProjectionTy, RpitId, Substitution, TraitRef, Ty, TyBuilder, TyExt, + TyKind, }; // This lint has a false positive here. See the link below for details. @@ -744,43 +744,13 @@ impl<'a> InferenceContext<'a> { self.result.standard_types.unknown.clone() } - /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it. - fn insert_const_vars_shallow(&mut self, c: Const) -> Const { - let data = c.data(Interner); - match &data.value { - ConstValue::Concrete(cc) => match cc.interned { - crate::ConstScalar::Unknown => self.table.new_const_var(data.ty.clone()), - _ => c, - }, - _ => c, - } - } - /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { - match ty.kind(Interner) { - TyKind::Error => self.table.new_type_var(), - TyKind::InferenceVar(..) => { - let ty_resolved = self.resolve_ty_shallow(&ty); - if ty_resolved.is_unknown() { - self.table.new_type_var() - } else { - ty - } - } - _ => ty, - } + self.table.insert_type_vars_shallow(ty) } fn insert_type_vars(&mut self, ty: Ty) -> Ty { - fold_tys_and_consts( - ty, - |x, _| match x { - Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)), - Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)), - }, - DebruijnIndex::INNERMOST, - ) + self.table.insert_type_vars(ty) } fn push_obligation(&mut self, o: DomainGoal) { @@ -909,8 +879,6 @@ impl<'a> InferenceContext<'a> { None => return (self.err_ty(), None), }; let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); - // FIXME: this should resolve assoc items as well, see this example: - // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 let (resolution, unresolved) = if value_ns { match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) { Some(ResolveValueResult::ValueNs(value)) => match value { @@ -964,8 +932,68 @@ impl<'a> InferenceContext<'a> { TypeNs::SelfType(impl_id) => { let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); let substs = generics.placeholder_subst(self.db); - let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs); - self.resolve_variant_on_alias(ty, unresolved, mod_path) + let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs); + + let Some(mut remaining_idx) = unresolved else { + return self.resolve_variant_on_alias(ty, None, mod_path); + }; + + let mut remaining_segments = path.segments().skip(remaining_idx); + + // We need to try resolving unresolved segments one by one because each may resolve + // to a projection, which `TyLoweringContext` cannot handle on its own. + while !remaining_segments.is_empty() { + let resolved_segment = path.segments().get(remaining_idx - 1).unwrap(); + let current_segment = remaining_segments.take(1); + + // If we can resolve to an enum variant, it takes priority over associated type + // of the same name. + if let Some((AdtId::EnumId(id), _)) = ty.as_adt() { + let enum_data = self.db.enum_data(id); + let name = current_segment.first().unwrap().name; + if let Some(local_id) = enum_data.variant(name) { + let variant = EnumVariantId { parent: id, local_id }; + return if remaining_segments.len() == 1 { + (ty, Some(variant.into())) + } else { + // We still have unresolved paths, but enum variants never have + // associated types! + (self.err_ty(), None) + }; + } + } + + // `lower_partly_resolved_path()` returns `None` as type namespace unless + // `remaining_segments` is empty, which is never the case here. We don't know + // which namespace the new `ty` is in until normalized anyway. + (ty, _) = ctx.lower_partly_resolved_path( + resolution, + resolved_segment, + current_segment, + false, + ); + + ty = self.table.insert_type_vars(ty); + ty = self.table.normalize_associated_types_in(ty); + ty = self.table.resolve_ty_shallow(&ty); + if ty.is_unknown() { + return (self.err_ty(), None); + } + + // FIXME(inherent_associated_types): update `resolution` based on `ty` here. + remaining_idx += 1; + remaining_segments = remaining_segments.skip(1); + } + + let variant = ty.as_adt().and_then(|(id, _)| match id { + AdtId::StructId(s) => Some(VariantId::StructId(s)), + AdtId::UnionId(u) => Some(VariantId::UnionId(u)), + AdtId::EnumId(_) => { + // FIXME Error E0071, expected struct, variant or union type, found enum `Foo` + None + } + }); + (ty, variant) } TypeNs::TypeAliasId(it) => { let container = it.lookup(self.db.upcast()).container; diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 266e410187..368c3f6524 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -30,11 +30,18 @@ impl<'a> InferenceContext<'a> { fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { - let Some(last) = path.segments().last() else { return None }; - let ty = self.make_ty(type_ref); - let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); + let last = path.segments().last()?; + + // Don't use `self.make_ty()` here as we need `orig_ns`. let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); - let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty); + let (ty, orig_ns) = ctx.lower_ty_ext(type_ref); + let ty = self.table.insert_type_vars(ty); + let ty = self.table.normalize_associated_types_in(ty); + + let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); + let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty); + let ty = self.table.insert_type_vars(ty); + let ty = self.table.normalize_associated_types_in(ty); self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? } else { // FIXME: report error, unresolved first path segment @@ -169,7 +176,7 @@ impl<'a> InferenceContext<'a> { ) -> Option<(ValueNs, Substitution)> { let trait_ = trait_ref.hir_trait_id(); let item = - self.db.trait_data(trait_).items.iter().map(|(_name, id)| (*id)).find_map(|item| { + self.db.trait_data(trait_).items.iter().map(|(_name, id)| *id).find_map(|item| { match item { AssocItemId::FunctionId(func) => { if segment.name == &self.db.function_data(func).name { @@ -288,7 +295,7 @@ impl<'a> InferenceContext<'a> { name: &Name, id: ExprOrPatId, ) -> Option<(ValueNs, Substitution)> { - let ty = self.resolve_ty_shallow(ty); + let ty = self.resolve_ty_shallow(&ty); let (enum_id, subst) = match ty.as_adt() { Some((AdtId::EnumId(e), subst)) => (e, subst), _ => return None, diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index f0e0714e1d..c73acb4b12 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -7,16 +7,18 @@ use chalk_ir::{ IntTy, TyVariableKind, UniverseIndex, }; use chalk_solve::infer::ParameterEnaVariableExt; +use either::Either; use ena::unify::UnifyKey; use hir_expand::name; use stdx::never; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - db::HirDatabase, fold_tys, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq, - AliasTy, BoundVar, Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, - InEnvironment, InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, - Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, + db::HirDatabase, fold_tys, fold_tys_and_consts, static_lifetime, to_chalk_trait_id, + traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, + GenericArg, GenericArgData, Goal, Guidance, InEnvironment, InferenceVar, Interner, Lifetime, + ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, + TyBuilder, TyExt, TyKind, VariableKind, }; impl<'a> InferenceContext<'a> { @@ -717,6 +719,45 @@ impl<'a> InferenceTable<'a> { None } } + + pub(super) fn insert_type_vars(&mut self, ty: Ty) -> Ty { + fold_tys_and_consts( + ty, + |x, _| match x { + Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)), + Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)), + }, + DebruijnIndex::INNERMOST, + ) + } + + /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. + pub(super) fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { + match ty.kind(Interner) { + TyKind::Error => self.new_type_var(), + TyKind::InferenceVar(..) => { + let ty_resolved = self.resolve_ty_shallow(&ty); + if ty_resolved.is_unknown() { + self.new_type_var() + } else { + ty + } + } + _ => ty, + } + } + + /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it. + pub(super) fn insert_const_vars_shallow(&mut self, c: Const) -> Const { + let data = c.data(Interner); + match &data.value { + ConstValue::Concrete(cc) => match cc.interned { + crate::ConstScalar::Unknown => self.new_const_var(data.ty.clone()), + _ => c, + }, + _ => c, + } + } } impl<'a> fmt::Debug for InferenceTable<'a> { diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 797e9ad0e9..adadbb888b 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -103,7 +103,7 @@ impl ImplTraitLoweringState { #[derive(Debug)] pub struct TyLoweringContext<'a> { pub db: &'a dyn HirDatabase, - pub resolver: &'a Resolver, + resolver: &'a Resolver, in_binders: DebruijnIndex, /// Note: Conceptually, it's thinkable that we could be in a location where /// some type params should be represented as placeholders, and others diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 813beaa364..17a0c12c3e 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -4183,3 +4183,106 @@ fn test() { "#, ); } + +#[test] +fn associated_type_in_struct_expr_path() { + // FIXME: All annotation should be resolvable. + // For lines marked as unstable, see rust-lang/rust#86935. + // FIXME: Remove the comments once stablized. + check_types( + r#" +trait Trait { + type Assoc; + fn f(); +} + +struct S { x: u32 } + +impl Trait for () { + type Assoc = S; + + fn f() { + let x = 42; + let a = Self::Assoc { x }; + // ^ S + let a = ::Assoc { x }; // unstable + // ^ {unknown} + + // should be `Copy` but we don't track ownership anyway. + let value = S { x }; + if let Self::Assoc { x } = value {} + // ^ u32 + if let ::Assoc { x } = value {} // unstable + // ^ {unknown} + } +} + "#, + ); +} + +#[test] +fn associted_type_in_struct_expr_path_enum() { + // FIXME: All annotation should be resolvable. + // For lines marked as unstable, see rust-lang/rust#86935. + // FIXME: Remove the comments once stablized. + check_types( + r#" +trait Trait { + type Assoc; + fn f(); +} + +enum E { + Unit, + Struct { x: u32 }, +} + +impl Trait for () { + type Assoc = E; + + fn f() { + let a = Self::Assoc::Unit; + // ^ E + let a = ::Assoc::Unit; + // ^ E + let a = ::Unit; + // ^ E + let a = <::Assoc>::Unit; + // ^ E + + // should be `Copy` but we don't track ownership anyway. + let value = E::Unit; + if let Self::Assoc::Unit = value {} + // ^^^^^^^^^^^^^^^^^ E + if let ::Assoc::Unit = value {} + // ^^^^^^^^^^^^^^^^^^^ E + if let ::Unit = value {} + // ^^^^^^^^^^^^^^^^^^^ E + if let <::Assoc>::Unit = value {} + // ^^^^^^^^^^^^^^^^^^^^^ E + + let x = 42; + let a = Self::Assoc::Struct { x }; + // ^ E + let a = ::Assoc::Struct { x }; // unstable + // ^ {unknown} + let a = ::Struct { x }; // unstable + // ^ {unknown} + let a = <::Assoc>::Struct { x }; // unstable + // ^ {unknown} + + // should be `Copy` but we don't track ownership anyway. + let value = E::Struct { x: 42 }; + if let Self::Assoc::Struct { x } = value {} + // ^ u32 + if let ::Assoc::Struct { x } = value {} // unstable + // ^ {unknown} + if let ::Struct { x } = value {} // unstable + // ^ {unknown} + if let <::Assoc>::Struct { x } = value {} // unstable + // ^ {unknown} + } +} + "#, + ); +}