Merge #1501
1501: Infer for loop variable r=flodiebold a=unrealhoang My take on https://github.com/rust-analyzer/rust-analyzer/issues/1425 Co-authored-by: Unreal Hoang <unrealhoang@gmail.com>
This commit is contained in:
commit
1b38ca3b87
@ -86,6 +86,11 @@ impl Name {
|
||||
"Self" => KnownName::SelfType,
|
||||
"self" => KnownName::SelfParam,
|
||||
"macro_rules" => KnownName::MacroRules,
|
||||
|
||||
"std" => KnownName::Std,
|
||||
"iter" => KnownName::Iter,
|
||||
"IntoIterator" => KnownName::IntoIterator,
|
||||
"Item" => KnownName::Item,
|
||||
_ => return None,
|
||||
};
|
||||
Some(name)
|
||||
@ -157,6 +162,11 @@ pub(crate) enum KnownName {
|
||||
SelfParam,
|
||||
|
||||
MacroRules,
|
||||
|
||||
Std,
|
||||
Iter,
|
||||
IntoIterator,
|
||||
Item,
|
||||
}
|
||||
|
||||
impl AsName for KnownName {
|
||||
@ -182,6 +192,10 @@ impl AsName for KnownName {
|
||||
KnownName::SelfType => "Self",
|
||||
KnownName::SelfParam => "self",
|
||||
KnownName::MacroRules => "macro_rules",
|
||||
KnownName::Std => "std",
|
||||
KnownName::Iter => "iter",
|
||||
KnownName::IntoIterator => "IntoIterator",
|
||||
KnownName::Item => "Item",
|
||||
};
|
||||
Name::new(s.into())
|
||||
}
|
||||
|
@ -28,24 +28,28 @@ use test_utils::tested_by;
|
||||
|
||||
use super::{
|
||||
autoderef, method_resolution, op, primitive,
|
||||
traits::{Guidance, Obligation, Solution},
|
||||
ApplicationTy, CallableDef, Substs, TraitRef, Ty, TypableDef, TypeCtor,
|
||||
traits::{Guidance, Obligation, ProjectionPredicate, Solution},
|
||||
ApplicationTy, CallableDef, ProjectionTy, Substs, TraitRef, Ty, TypableDef, TypeCtor,
|
||||
};
|
||||
use crate::{
|
||||
adt::VariantDef,
|
||||
code_model::{ModuleDef::Trait, TypeAlias},
|
||||
diagnostics::DiagnosticSink,
|
||||
expr::{
|
||||
self, Array, BinaryOp, BindingAnnotation, Body, Expr, ExprId, FieldPat, Literal, Pat,
|
||||
PatId, Statement, UnaryOp,
|
||||
},
|
||||
generics::{GenericParams, HasGenericParams},
|
||||
nameres::Namespace,
|
||||
path::{GenericArg, GenericArgs},
|
||||
resolve::{Resolution, Resolver},
|
||||
nameres::{Namespace, PerNs},
|
||||
path::{GenericArg, GenericArgs, PathKind, PathSegment},
|
||||
resolve::{
|
||||
Resolution::{self, Def},
|
||||
Resolver,
|
||||
},
|
||||
ty::infer::diagnostics::InferenceDiagnostic,
|
||||
type_ref::{Mutability, TypeRef},
|
||||
AdtDef, ConstData, DefWithBody, FnData, Function, HirDatabase, ImplItem, ModuleDef, Name, Path,
|
||||
StructField,
|
||||
AdtDef, AsName, ConstData, DefWithBody, FnData, Function, HirDatabase, ImplItem, KnownName,
|
||||
ModuleDef, Name, Path, StructField,
|
||||
};
|
||||
|
||||
mod unify;
|
||||
@ -323,34 +327,53 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
fn resolve_obligations_as_possible(&mut self) {
|
||||
let obligations = mem::replace(&mut self.obligations, Vec::new());
|
||||
for obligation in obligations {
|
||||
let (solution, canonicalized) = match &obligation {
|
||||
match &obligation {
|
||||
Obligation::Trait(tr) => {
|
||||
let canonicalized = self.canonicalizer().canonicalize_trait_ref(tr.clone());
|
||||
(
|
||||
self.db.implements(
|
||||
self.resolver.krate().unwrap(),
|
||||
canonicalized.value.clone(),
|
||||
),
|
||||
canonicalized,
|
||||
)
|
||||
let solution = self
|
||||
.db
|
||||
.implements(self.resolver.krate().unwrap(), canonicalized.value.clone());
|
||||
match solution {
|
||||
Some(Solution::Unique(substs)) => {
|
||||
canonicalized.apply_solution(self, substs.0);
|
||||
}
|
||||
Some(Solution::Ambig(Guidance::Definite(substs))) => {
|
||||
canonicalized.apply_solution(self, substs.0);
|
||||
self.obligations.push(obligation);
|
||||
}
|
||||
Some(_) => {
|
||||
// FIXME use this when trying to resolve everything at the end
|
||||
self.obligations.push(obligation);
|
||||
}
|
||||
None => {
|
||||
// FIXME obligation cannot be fulfilled => diagnostic
|
||||
}
|
||||
};
|
||||
}
|
||||
Obligation::Projection(pr) => {
|
||||
let canonicalized = self.canonicalizer().canonicalize_projection(pr.clone());
|
||||
let solution = self
|
||||
.db
|
||||
.normalize(self.resolver.krate().unwrap(), canonicalized.value.clone());
|
||||
|
||||
match solution {
|
||||
Some(Solution::Unique(substs)) => {
|
||||
canonicalized.apply_solution(self, substs.0);
|
||||
}
|
||||
Some(Solution::Ambig(Guidance::Definite(substs))) => {
|
||||
canonicalized.apply_solution(self, substs.0);
|
||||
self.obligations.push(obligation);
|
||||
}
|
||||
Some(_) => {
|
||||
// FIXME use this when trying to resolve everything at the end
|
||||
self.obligations.push(obligation);
|
||||
}
|
||||
None => {
|
||||
// FIXME obligation cannot be fulfilled => diagnostic
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
match solution {
|
||||
Some(Solution::Unique(substs)) => {
|
||||
canonicalized.apply_solution(self, substs.0);
|
||||
}
|
||||
Some(Solution::Ambig(Guidance::Definite(substs))) => {
|
||||
canonicalized.apply_solution(self, substs.0);
|
||||
self.obligations.push(obligation);
|
||||
}
|
||||
Some(_) => {
|
||||
// FIXME use this when trying to resolve everything at the end
|
||||
self.obligations.push(obligation);
|
||||
}
|
||||
None => {
|
||||
// FIXME obligation cannot be fulfilled => diagnostic
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -967,8 +990,25 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
Ty::unit()
|
||||
}
|
||||
Expr::For { iterable, body, pat } => {
|
||||
let _iterable_ty = self.infer_expr(*iterable, &Expectation::none());
|
||||
self.infer_pat(*pat, &Ty::Unknown, BindingMode::default());
|
||||
let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
|
||||
|
||||
let pat_ty = match self.resolve_into_iter_item() {
|
||||
Some(into_iter_item_alias) => {
|
||||
let pat_ty = self.new_type_var();
|
||||
let projection = ProjectionPredicate {
|
||||
ty: pat_ty.clone(),
|
||||
projection_ty: ProjectionTy {
|
||||
associated_ty: into_iter_item_alias,
|
||||
parameters: vec![iterable_ty].into(),
|
||||
},
|
||||
};
|
||||
self.obligations.push(Obligation::Projection(projection));
|
||||
self.resolve_ty_as_possible(&mut vec![], pat_ty)
|
||||
}
|
||||
None => Ty::Unknown,
|
||||
};
|
||||
|
||||
self.infer_pat(*pat, &pat_ty, BindingMode::default());
|
||||
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
|
||||
Ty::unit()
|
||||
}
|
||||
@ -1301,6 +1341,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
fn infer_body(&mut self) {
|
||||
self.infer_expr(self.body.body_expr(), &Expectation::has_type(self.return_ty.clone()));
|
||||
}
|
||||
|
||||
fn resolve_into_iter_item(&self) -> Option<TypeAlias> {
|
||||
let into_iter_path = Path {
|
||||
kind: PathKind::Abs,
|
||||
segments: vec![
|
||||
PathSegment { name: KnownName::Std.as_name(), args_and_bindings: None },
|
||||
PathSegment { name: KnownName::Iter.as_name(), args_and_bindings: None },
|
||||
PathSegment { name: KnownName::IntoIterator.as_name(), args_and_bindings: None },
|
||||
],
|
||||
};
|
||||
|
||||
match self.resolver.resolve_path_segments(self.db, &into_iter_path).into_fully_resolved() {
|
||||
PerNs { types: Some(Def(Trait(trait_))), .. } => {
|
||||
Some(trait_.associated_type_by_name(self.db, KnownName::Item.as_name())?)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The ID of a type variable.
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use super::InferenceContext;
|
||||
use crate::db::HirDatabase;
|
||||
use crate::ty::{Canonical, InferTy, TraitRef, Ty};
|
||||
use crate::ty::{Canonical, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty};
|
||||
|
||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b, D>
|
||||
@ -86,6 +86,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn do_canonicalize_projection_ty(&mut self, projection_ty: ProjectionTy) -> ProjectionTy {
|
||||
let params = projection_ty
|
||||
.parameters
|
||||
.iter()
|
||||
.map(|ty| self.do_canonicalize_ty(ty.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
ProjectionTy { associated_ty: projection_ty.associated_ty, parameters: params.into() }
|
||||
}
|
||||
|
||||
fn do_canonicalize_projection_predicate(
|
||||
&mut self,
|
||||
projection: ProjectionPredicate,
|
||||
) -> ProjectionPredicate {
|
||||
let ty = self.do_canonicalize_ty(projection.ty);
|
||||
let projection_ty = self.do_canonicalize_projection_ty(projection.projection_ty);
|
||||
|
||||
ProjectionPredicate { ty, projection_ty }
|
||||
}
|
||||
|
||||
pub fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> {
|
||||
let result = self.do_canonicalize_ty(ty);
|
||||
self.into_canonicalized(result)
|
||||
@ -95,6 +114,14 @@ where
|
||||
let result = self.do_canonicalize_trait_ref(trait_ref);
|
||||
self.into_canonicalized(result)
|
||||
}
|
||||
|
||||
pub fn canonicalize_projection(
|
||||
mut self,
|
||||
projection: ProjectionPredicate,
|
||||
) -> Canonicalized<ProjectionPredicate> {
|
||||
let result = self.do_canonicalize_projection_predicate(projection);
|
||||
self.into_canonicalized(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Canonicalized<T> {
|
||||
|
@ -20,6 +20,42 @@ use crate::{
|
||||
// against snapshots of the expected results using insta. Use cargo-insta to
|
||||
// update the snapshots.
|
||||
|
||||
#[test]
|
||||
fn infer_for_loop() {
|
||||
let (mut db, pos) = MockDatabase::with_position(
|
||||
r#"
|
||||
//- /main.rs
|
||||
struct Vec<T> {}
|
||||
impl<T> Vec<T> {
|
||||
fn new() -> Self { Vec {} }
|
||||
fn push(&mut self, t: T) { }
|
||||
}
|
||||
|
||||
impl<T> ::std::iter::IntoIterator for Vec<T> {
|
||||
type Item=T;
|
||||
}
|
||||
fn test() {
|
||||
let v = Vec::new();
|
||||
v.push("foo");
|
||||
for x in v {
|
||||
x<|>;
|
||||
}
|
||||
}
|
||||
|
||||
//- /lib.rs
|
||||
mod iter {
|
||||
trait IntoIterator {
|
||||
type Item;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
db.set_crate_graph_from_fixture(crate_graph! {
|
||||
"main": ("/main.rs", ["std"]),
|
||||
"std": ("/lib.rs", []),
|
||||
});
|
||||
assert_eq!("&str", type_at_pos(&db, pos));
|
||||
}
|
||||
#[test]
|
||||
fn infer_basics() {
|
||||
assert_snapshot_matches!(
|
||||
|
@ -75,7 +75,7 @@ pub enum Obligation {
|
||||
/// Prove that a certain type implements a trait (the type is the `Self` type
|
||||
/// parameter to the `TraitRef`).
|
||||
Trait(TraitRef),
|
||||
// Projection(ProjectionPredicate),
|
||||
Projection(ProjectionPredicate),
|
||||
}
|
||||
|
||||
impl Obligation {
|
||||
|
Loading…
x
Reference in New Issue
Block a user