mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 15:15:24 +00:00
Resolve and normalize path segments one by one in variant resolution
This commit is contained in:
parent
7a98e24777
commit
8aef04f1a7
4 changed files with 198 additions and 46 deletions
|
@ -79,7 +79,7 @@ enum Scope {
|
||||||
ExprScope(ExprScope),
|
ExprScope(ExprScope),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum TypeNs {
|
pub enum TypeNs {
|
||||||
SelfType(ImplId),
|
SelfType(ImplId),
|
||||||
GenericParam(TypeParamId),
|
GenericParam(TypeParamId),
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
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 either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body,
|
body::Body,
|
||||||
|
@ -37,10 +37,10 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use stdx::{always, never};
|
use stdx::{always, never};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
|
db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
|
||||||
lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, AliasEq, AliasTy, Const,
|
static_lifetime, to_assoc_type_id, AliasEq, AliasTy, DomainGoal, GenericArg, Goal, ImplTraitId,
|
||||||
DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId,
|
InEnvironment, Interner, ProjectionTy, RpitId, Substitution, TraitRef, Ty, TyBuilder, TyExt,
|
||||||
Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This lint has a false positive here. See the link below for details.
|
// 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()
|
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.
|
/// 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 {
|
fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
|
||||||
match ty.kind(Interner) {
|
self.table.insert_type_vars_shallow(ty)
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_type_vars(&mut self, ty: Ty) -> Ty {
|
fn insert_type_vars(&mut self, ty: Ty) -> Ty {
|
||||||
fold_tys_and_consts(
|
self.table.insert_type_vars(ty)
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_obligation(&mut self, o: DomainGoal) {
|
fn push_obligation(&mut self, o: DomainGoal) {
|
||||||
|
@ -850,8 +820,6 @@ impl<'a> InferenceContext<'a> {
|
||||||
None => return (self.err_ty(), None),
|
None => return (self.err_ty(), None),
|
||||||
};
|
};
|
||||||
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
|
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 {
|
let (resolution, unresolved) = if value_ns {
|
||||||
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
|
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
|
||||||
Some(ResolveValueResult::ValueNs(value)) => match value {
|
Some(ResolveValueResult::ValueNs(value)) => match value {
|
||||||
|
@ -905,8 +873,68 @@ impl<'a> InferenceContext<'a> {
|
||||||
TypeNs::SelfType(impl_id) => {
|
TypeNs::SelfType(impl_id) => {
|
||||||
let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
|
let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
|
||||||
let substs = generics.placeholder_subst(self.db);
|
let substs = generics.placeholder_subst(self.db);
|
||||||
let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
|
let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
|
||||||
self.resolve_variant_on_alias(ty, unresolved, mod_path)
|
|
||||||
|
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) => {
|
TypeNs::TypeAliasId(it) => {
|
||||||
let container = it.lookup(self.db.upcast()).container;
|
let container = it.lookup(self.db.upcast()).container;
|
||||||
|
|
|
@ -7,16 +7,18 @@ use chalk_ir::{
|
||||||
IntTy, TyVariableKind, UniverseIndex,
|
IntTy, TyVariableKind, UniverseIndex,
|
||||||
};
|
};
|
||||||
use chalk_solve::infer::ParameterEnaVariableExt;
|
use chalk_solve::infer::ParameterEnaVariableExt;
|
||||||
|
use either::Either;
|
||||||
use ena::unify::UnifyKey;
|
use ena::unify::UnifyKey;
|
||||||
use hir_expand::name;
|
use hir_expand::name;
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
|
||||||
use super::{InferOk, InferResult, InferenceContext, TypeError};
|
use super::{InferOk, InferResult, InferenceContext, TypeError};
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, fold_tys, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq,
|
db::HirDatabase, fold_tys, fold_tys_and_consts, static_lifetime, to_chalk_trait_id,
|
||||||
AliasTy, BoundVar, Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance,
|
traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex,
|
||||||
InEnvironment, InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt,
|
GenericArg, GenericArgData, Goal, Guidance, InEnvironment, InferenceVar, Interner, Lifetime,
|
||||||
Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
|
ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty,
|
||||||
|
TyBuilder, TyExt, TyKind, VariableKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'a> InferenceContext<'a> {
|
impl<'a> InferenceContext<'a> {
|
||||||
|
@ -717,6 +719,45 @@ impl<'a> InferenceTable<'a> {
|
||||||
None
|
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> {
|
impl<'a> fmt::Debug for InferenceTable<'a> {
|
||||||
|
|
|
@ -4181,3 +4181,86 @@ 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 = <Self>::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 <Self>::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 x = 42;
|
||||||
|
let a = Self::Assoc::Struct { x };
|
||||||
|
// ^ E
|
||||||
|
let a = <Self>::Assoc::Struct { x }; // unstable
|
||||||
|
// ^ {unknown}
|
||||||
|
let a = <Self::Assoc>::Struct { x }; // unstable
|
||||||
|
// ^ {unknown}
|
||||||
|
let a = <<Self>::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 <Self>::Assoc::Struct { x } = value {} // unstable
|
||||||
|
// ^ {unknown}
|
||||||
|
if let <Self::Assoc>::Struct { x } = value {} // unstable
|
||||||
|
// ^ {unknown}
|
||||||
|
if let <<Self>::Assoc>::Struct { x } = value {} // unstable
|
||||||
|
// ^ {unknown}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue