Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 8aef04f

Browse files
committed
Resolve and normalize path segments one by one in variant resolution
1 parent 7a98e24 commit 8aef04f

File tree

4 files changed

+198
-46
lines changed

4 files changed

+198
-46
lines changed

crates/hir-def/src/resolver.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ enum Scope {
7979
ExprScope(ExprScope),
8080
}
8181

82-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
82+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8383
pub enum TypeNs {
8484
SelfType(ImplId),
8585
GenericParam(TypeParamId),

crates/hir-ty/src/infer.rs

Lines changed: 69 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use std::ops::Index;
1717
use std::sync::Arc;
1818

19-
use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
19+
use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
2020
use either::Either;
2121
use hir_def::{
2222
body::Body,
@@ -37,10 +37,10 @@ use rustc_hash::{FxHashMap, FxHashSet};
3737
use stdx::{always, never};
3838

3939
use crate::{
40-
db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
41-
lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, AliasEq, AliasTy, Const,
42-
DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId,
43-
Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
40+
db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
41+
static_lifetime, to_assoc_type_id, AliasEq, AliasTy, DomainGoal, GenericArg, Goal, ImplTraitId,
42+
InEnvironment, Interner, ProjectionTy, RpitId, Substitution, TraitRef, Ty, TyBuilder, TyExt,
43+
TyKind,
4444
};
4545

4646
// This lint has a false positive here. See the link below for details.
@@ -744,43 +744,13 @@ impl<'a> InferenceContext<'a> {
744744
self.result.standard_types.unknown.clone()
745745
}
746746

747-
/// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
748-
fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
749-
let data = c.data(Interner);
750-
match &data.value {
751-
ConstValue::Concrete(cc) => match cc.interned {
752-
crate::ConstScalar::Unknown => self.table.new_const_var(data.ty.clone()),
753-
_ => c,
754-
},
755-
_ => c,
756-
}
757-
}
758-
759747
/// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
760748
fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
761-
match ty.kind(Interner) {
762-
TyKind::Error => self.table.new_type_var(),
763-
TyKind::InferenceVar(..) => {
764-
let ty_resolved = self.resolve_ty_shallow(&ty);
765-
if ty_resolved.is_unknown() {
766-
self.table.new_type_var()
767-
} else {
768-
ty
769-
}
770-
}
771-
_ => ty,
772-
}
749+
self.table.insert_type_vars_shallow(ty)
773750
}
774751

775752
fn insert_type_vars(&mut self, ty: Ty) -> Ty {
776-
fold_tys_and_consts(
777-
ty,
778-
|x, _| match x {
779-
Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)),
780-
Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)),
781-
},
782-
DebruijnIndex::INNERMOST,
783-
)
753+
self.table.insert_type_vars(ty)
784754
}
785755

786756
fn push_obligation(&mut self, o: DomainGoal) {
@@ -850,8 +820,6 @@ impl<'a> InferenceContext<'a> {
850820
None => return (self.err_ty(), None),
851821
};
852822
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
853-
// FIXME: this should resolve assoc items as well, see this example:
854-
// https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
855823
let (resolution, unresolved) = if value_ns {
856824
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
857825
Some(ResolveValueResult::ValueNs(value)) => match value {
@@ -905,8 +873,68 @@ impl<'a> InferenceContext<'a> {
905873
TypeNs::SelfType(impl_id) => {
906874
let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
907875
let substs = generics.placeholder_subst(self.db);
908-
let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
909-
self.resolve_variant_on_alias(ty, unresolved, mod_path)
876+
let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
877+
878+
let Some(mut remaining_idx) = unresolved else {
879+
return self.resolve_variant_on_alias(ty, None, mod_path);
880+
};
881+
882+
let mut remaining_segments = path.segments().skip(remaining_idx);
883+
884+
// We need to try resolving unresolved segments one by one because each may resolve
885+
// to a projection, which `TyLoweringContext` cannot handle on its own.
886+
while !remaining_segments.is_empty() {
887+
let resolved_segment = path.segments().get(remaining_idx - 1).unwrap();
888+
let current_segment = remaining_segments.take(1);
889+
890+
// If we can resolve to an enum variant, it takes priority over associated type
891+
// of the same name.
892+
if let Some((AdtId::EnumId(id), _)) = ty.as_adt() {
893+
let enum_data = self.db.enum_data(id);
894+
let name = current_segment.first().unwrap().name;
895+
if let Some(local_id) = enum_data.variant(name) {
896+
let variant = EnumVariantId { parent: id, local_id };
897+
return if remaining_segments.len() == 1 {
898+
(ty, Some(variant.into()))
899+
} else {
900+
// We still have unresolved paths, but enum variants never have
901+
// associated types!
902+
(self.err_ty(), None)
903+
};
904+
}
905+
}
906+
907+
// `lower_partly_resolved_path()` returns `None` as type namespace unless
908+
// `remaining_segments` is empty, which is never the case here. We don't know
909+
// which namespace the new `ty` is in until normalized anyway.
910+
(ty, _) = ctx.lower_partly_resolved_path(
911+
resolution,
912+
resolved_segment,
913+
current_segment,
914+
false,
915+
);
916+
917+
ty = self.table.insert_type_vars(ty);
918+
ty = self.table.normalize_associated_types_in(ty);
919+
ty = self.table.resolve_ty_shallow(&ty);
920+
if ty.is_unknown() {
921+
return (self.err_ty(), None);
922+
}
923+
924+
// FIXME(inherent_associated_types): update `resolution` based on `ty` here.
925+
remaining_idx += 1;
926+
remaining_segments = remaining_segments.skip(1);
927+
}
928+
929+
let variant = ty.as_adt().and_then(|(id, _)| match id {
930+
AdtId::StructId(s) => Some(VariantId::StructId(s)),
931+
AdtId::UnionId(u) => Some(VariantId::UnionId(u)),
932+
AdtId::EnumId(_) => {
933+
// FIXME Error E0071, expected struct, variant or union type, found enum `Foo`
934+
None
935+
}
936+
});
937+
(ty, variant)
910938
}
911939
TypeNs::TypeAliasId(it) => {
912940
let container = it.lookup(self.db.upcast()).container;

crates/hir-ty/src/infer/unify.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ use chalk_ir::{
77
IntTy, TyVariableKind, UniverseIndex,
88
};
99
use chalk_solve::infer::ParameterEnaVariableExt;
10+
use either::Either;
1011
use ena::unify::UnifyKey;
1112
use hir_expand::name;
1213
use stdx::never;
1314

1415
use super::{InferOk, InferResult, InferenceContext, TypeError};
1516
use crate::{
16-
db::HirDatabase, fold_tys, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq,
17-
AliasTy, BoundVar, Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance,
18-
InEnvironment, InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt,
19-
Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
17+
db::HirDatabase, fold_tys, fold_tys_and_consts, static_lifetime, to_chalk_trait_id,
18+
traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex,
19+
GenericArg, GenericArgData, Goal, Guidance, InEnvironment, InferenceVar, Interner, Lifetime,
20+
ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty,
21+
TyBuilder, TyExt, TyKind, VariableKind,
2022
};
2123

2224
impl<'a> InferenceContext<'a> {
@@ -717,6 +719,45 @@ impl<'a> InferenceTable<'a> {
717719
None
718720
}
719721
}
722+
723+
pub(super) fn insert_type_vars(&mut self, ty: Ty) -> Ty {
724+
fold_tys_and_consts(
725+
ty,
726+
|x, _| match x {
727+
Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)),
728+
Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)),
729+
},
730+
DebruijnIndex::INNERMOST,
731+
)
732+
}
733+
734+
/// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
735+
pub(super) fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
736+
match ty.kind(Interner) {
737+
TyKind::Error => self.new_type_var(),
738+
TyKind::InferenceVar(..) => {
739+
let ty_resolved = self.resolve_ty_shallow(&ty);
740+
if ty_resolved.is_unknown() {
741+
self.new_type_var()
742+
} else {
743+
ty
744+
}
745+
}
746+
_ => ty,
747+
}
748+
}
749+
750+
/// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
751+
pub(super) fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
752+
let data = c.data(Interner);
753+
match &data.value {
754+
ConstValue::Concrete(cc) => match cc.interned {
755+
crate::ConstScalar::Unknown => self.new_const_var(data.ty.clone()),
756+
_ => c,
757+
},
758+
_ => c,
759+
}
760+
}
720761
}
721762

722763
impl<'a> fmt::Debug for InferenceTable<'a> {

crates/hir-ty/src/tests/traits.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4181,3 +4181,86 @@ fn test() {
41814181
"#,
41824182
);
41834183
}
4184+
4185+
#[test]
4186+
fn associated_type_in_struct_expr_path() {
4187+
// FIXME: All annotation should be resolvable.
4188+
// For lines marked as unstable, see rust-lang/rust#86935.
4189+
// FIXME: Remove the comments once stablized.
4190+
check_types(
4191+
r#"
4192+
trait Trait {
4193+
type Assoc;
4194+
fn f();
4195+
}
4196+
4197+
struct S { x: u32 }
4198+
4199+
impl Trait for () {
4200+
type Assoc = S;
4201+
4202+
fn f() {
4203+
let x = 42;
4204+
let a = Self::Assoc { x };
4205+
// ^ S
4206+
let a = <Self>::Assoc { x }; // unstable
4207+
// ^ {unknown}
4208+
4209+
// should be `Copy` but we don't track ownership anyway.
4210+
let value = S { x };
4211+
if let Self::Assoc { x } = value {}
4212+
// ^ u32
4213+
if let <Self>::Assoc { x } = value {} // unstable
4214+
// ^ {unknown}
4215+
}
4216+
}
4217+
"#,
4218+
);
4219+
}
4220+
4221+
#[test]
4222+
fn associted_type_in_struct_expr_path_enum() {
4223+
// FIXME: All annotation should be resolvable.
4224+
// For lines marked as unstable, see rust-lang/rust#86935.
4225+
// FIXME: Remove the comments once stablized.
4226+
check_types(
4227+
r#"
4228+
trait Trait {
4229+
type Assoc;
4230+
fn f();
4231+
}
4232+
4233+
enum E {
4234+
Unit,
4235+
Struct { x: u32 },
4236+
}
4237+
4238+
impl Trait for () {
4239+
type Assoc = E;
4240+
4241+
fn f() {
4242+
let x = 42;
4243+
let a = Self::Assoc::Struct { x };
4244+
// ^ E
4245+
let a = <Self>::Assoc::Struct { x }; // unstable
4246+
// ^ {unknown}
4247+
let a = <Self::Assoc>::Struct { x }; // unstable
4248+
// ^ {unknown}
4249+
let a = <<Self>::Assoc>::Struct { x }; // unstable
4250+
// ^ {unknown}
4251+
4252+
// should be `Copy` but we don't track ownership anyway.
4253+
let value = E::Struct { x: 42 };
4254+
if let Self::Assoc::Struct { x } = value {}
4255+
// ^ u32
4256+
if let <Self>::Assoc::Struct { x } = value {} // unstable
4257+
// ^ {unknown}
4258+
if let <Self::Assoc>::Struct { x } = value {} // unstable
4259+
// ^ {unknown}
4260+
if let <<Self>::Assoc>::Struct { x } = value {} // unstable
4261+
// ^ {unknown}
4262+
}
4263+
}
4264+
"#,
4265+
);
4266+
}

0 commit comments

Comments
 (0)