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:
bors[bot] 2019-07-07 15:20:09 +00:00
commit 1b38ca3b87
5 changed files with 170 additions and 35 deletions

View file

@ -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())
}

View file

@ -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.

View file

@ -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> {

View file

@ -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!(

View file

@ -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 {