From 95dc2de8e979264e1c76ce5594e8a63547a7956e Mon Sep 17 00:00:00 2001 From: Emil Lauridsen Date: Fri, 13 Dec 2019 12:44:07 +0100 Subject: [PATCH 1/2] Add helper for resolving associated type of trait in infer --- crates/ra_hir_ty/src/infer.rs | 18 ++++++++++ crates/ra_hir_ty/src/infer/expr.rs | 54 ++++-------------------------- 2 files changed, 25 insertions(+), 47 deletions(-) diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index d16f1eb46d6..62d5c8803cc 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -338,6 +338,24 @@ fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { self.table.resolve_ty_shallow(ty) } + fn resolve_associated_type(&mut self, inner_ty: Ty, assoc_ty: Option) -> Ty { + match assoc_ty { + Some(res_assoc_ty) => { + let ty = self.table.new_type_var(); + let projection = ProjectionPredicate { + ty: ty.clone(), + projection_ty: ProjectionTy { + associated_ty: res_assoc_ty, + parameters: Substs::single(inner_ty), + }, + }; + self.obligations.push(Obligation::Projection(projection)); + self.resolve_ty_as_possible(ty) + } + None => Ty::Unknown, + } + } + /// Recurses through the given type, normalizing associated types mentioned /// in it by replacing them by type variables and registering obligations to /// resolve later. This should be done once for every type we get from some diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 2c296987c8a..6110f5abd33 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -19,8 +19,8 @@ method_resolution, op, traits::InEnvironment, utils::{generics, variant_data, Generics}, - CallableDef, InferTy, IntTy, Mutability, Obligation, ProjectionPredicate, ProjectionTy, Substs, - TraitRef, Ty, TypeCtor, TypeWalk, Uncertain, + ApplicationTy, CallableDef, InferTy, IntTy, Mutability, Obligation, Substs, TraitRef, Ty, + TypeCtor, TypeWalk, Uncertain, }; use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch}; @@ -95,21 +95,8 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { Expr::For { iterable, body, pat } => { 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.table.new_type_var(); - let projection = ProjectionPredicate { - ty: pat_ty.clone(), - projection_ty: ProjectionTy { - associated_ty: into_iter_item_alias, - parameters: Substs::single(iterable_ty), - }, - }; - self.obligations.push(Obligation::Projection(projection)); - self.resolve_ty_as_possible(pat_ty) - } - None => Ty::Unknown, - }; + let pat_ty = + self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); self.infer_pat(*pat, &pat_ty, BindingMode::default()); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); @@ -284,40 +271,13 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { } Expr::Await { expr } => { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); - let ty = match self.resolve_future_future_output() { - Some(future_future_output_alias) => { - let ty = self.table.new_type_var(); - let projection = ProjectionPredicate { - ty: ty.clone(), - projection_ty: ProjectionTy { - associated_ty: future_future_output_alias, - parameters: Substs::single(inner_ty), - }, - }; - self.obligations.push(Obligation::Projection(projection)); - self.resolve_ty_as_possible(ty) - } - None => Ty::Unknown, - }; + let ty = + self.resolve_associated_type(inner_ty, self.resolve_future_future_output()); ty } Expr::Try { expr } => { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); - let ty = match self.resolve_ops_try_ok() { - Some(ops_try_ok_alias) => { - let ty = self.table.new_type_var(); - let projection = ProjectionPredicate { - ty: ty.clone(), - projection_ty: ProjectionTy { - associated_ty: ops_try_ok_alias, - parameters: Substs::single(inner_ty), - }, - }; - self.obligations.push(Obligation::Projection(projection)); - self.resolve_ty_as_possible(ty) - } - None => Ty::Unknown, - }; + let ty = self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok()); ty } Expr::Cast { expr, type_ref } => { From 77052090515c1bb2a00236b3a57cdd778e581c8c Mon Sep 17 00:00:00 2001 From: Emil Lauridsen Date: Fri, 13 Dec 2019 12:44:42 +0100 Subject: [PATCH 2/2] Correctly infer - and ! using std::ops::{Neg,Not} --- crates/ra_hir_def/src/path.rs | 8 ++++ crates/ra_hir_expand/src/name.rs | 2 + crates/ra_hir_ty/src/infer.rs | 16 ++++++- crates/ra_hir_ty/src/infer/expr.rs | 47 +++++++++++--------- crates/ra_hir_ty/src/tests/traits.rs | 64 ++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 23 deletions(-) diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index ec9d13e8270..50f0cad94f2 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs @@ -342,6 +342,14 @@ pub fn std_ops_range_to_inclusive() -> Path { ) } + pub fn std_ops_neg() -> Path { + Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::NEG_TYPE]) + } + + pub fn std_ops_not() -> Path { + Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::NOT_TYPE]) + } + pub fn std_result_result() -> Path { Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE]) } diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index 4f2f702c01f..9e68dd98d70 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs @@ -152,6 +152,8 @@ fn as_name(&self) -> Name { pub const RANGE_TO_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(b"RangeToInclusive"); pub const RANGE_TO_TYPE: Name = Name::new_inline_ascii(b"RangeTo"); pub const RANGE_TYPE: Name = Name::new_inline_ascii(b"Range"); +pub const NEG_TYPE: Name = Name::new_inline_ascii(b"Neg"); +pub const NOT_TYPE: Name = Name::new_inline_ascii(b"Not"); // Builtin Macros pub const FILE_MACRO: Name = Name::new_inline_ascii(b"file"); diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 62d5c8803cc..a1201b3e479 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -36,8 +36,8 @@ use super::{ primitive::{FloatTy, IntTy}, traits::{Guidance, Obligation, ProjectionPredicate, Solution}, - ApplicationTy, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, - Uncertain, + ApplicationTy, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, + TypeWalk, Uncertain, }; use crate::{db::HirDatabase, infer::diagnostics::InferenceDiagnostic}; @@ -433,6 +433,18 @@ fn resolve_ops_try_ok(&self) -> Option { self.db.trait_data(trait_).associated_type_by_name(&name::OK_TYPE) } + fn resolve_ops_neg_output(&self) -> Option { + let path = known::std_ops_neg(); + let trait_ = self.resolver.resolve_known_trait(self.db, &path)?; + self.db.trait_data(trait_).associated_type_by_name(&name::OUTPUT_TYPE) + } + + fn resolve_ops_not_output(&self) -> Option { + let path = known::std_ops_not(); + let trait_ = self.resolver.resolve_known_trait(self.db, &path)?; + self.db.trait_data(trait_).associated_type_by_name(&name::OUTPUT_TYPE) + } + fn resolve_future_future_output(&self) -> Option { let path = known::std_future_future(); let trait_ = self.resolver.resolve_known_trait(self.db, &path)?; diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 6110f5abd33..f8c00a7b4a3 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -332,31 +332,36 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { }, UnaryOp::Neg => { match &inner_ty { - Ty::Apply(a_ty) => match a_ty.ctor { - TypeCtor::Int(Uncertain::Unknown) - | TypeCtor::Int(Uncertain::Known(IntTy { - signedness: Signedness::Signed, - .. - })) - | TypeCtor::Float(..) => inner_ty, - _ => Ty::Unknown, - }, - Ty::Infer(InferTy::IntVar(..)) | Ty::Infer(InferTy::FloatVar(..)) => { - inner_ty - } - // FIXME: resolve ops::Neg trait - _ => Ty::Unknown, + // Fast path for builtins + Ty::Apply(ApplicationTy { + ctor: + TypeCtor::Int(Uncertain::Known(IntTy { + signedness: Signedness::Signed, + .. + })), + .. + }) + | Ty::Apply(ApplicationTy { + ctor: TypeCtor::Int(Uncertain::Unknown), + .. + }) + | Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(_), .. }) + | Ty::Infer(InferTy::IntVar(..)) + | Ty::Infer(InferTy::FloatVar(..)) => inner_ty, + // Otherwise we resolve via the std::ops::Neg trait + _ => self + .resolve_associated_type(inner_ty, self.resolve_ops_neg_output()), } } UnaryOp::Not => { match &inner_ty { - Ty::Apply(a_ty) => match a_ty.ctor { - TypeCtor::Bool | TypeCtor::Int(_) => inner_ty, - _ => Ty::Unknown, - }, - Ty::Infer(InferTy::IntVar(..)) => inner_ty, - // FIXME: resolve ops::Not trait for inner_ty - _ => Ty::Unknown, + // Fast path for builtins + Ty::Apply(ApplicationTy { ctor: TypeCtor::Bool, .. }) + | Ty::Apply(ApplicationTy { ctor: TypeCtor::Int(_), .. }) + | Ty::Infer(InferTy::IntVar(..)) => inner_ty, + // Otherwise we resolve via the std::ops::Not trait + _ => self + .resolve_associated_type(inner_ty, self.resolve_ops_not_output()), } } } diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 93c5f9a15a3..6139adb72e5 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -115,6 +115,70 @@ impl crate::iter::IntoIterator for Vec { assert_eq!("&str", type_at_pos(&db, pos)); } +#[test] +fn infer_ops_neg() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:std + +struct Bar; +struct Foo; + +impl std::ops::Neg for Bar { + type Output = Foo; +} + +fn test() { + let a = Bar; + let b = -a; + b<|>; +} + +//- /std.rs crate:std + +#[prelude_import] use ops::*; +mod ops { + pub trait Neg { + type Output; + } +} +"#, + ); + assert_eq!("Foo", type_at_pos(&db, pos)); +} + +#[test] +fn infer_ops_not() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:std + +struct Bar; +struct Foo; + +impl std::ops::Not for Bar { + type Output = Foo; +} + +fn test() { + let a = Bar; + let b = !a; + b<|>; +} + +//- /std.rs crate:std + +#[prelude_import] use ops::*; +mod ops { + pub trait Not { + type Output; + } +} +"#, + ); + assert_eq!("Foo", type_at_pos(&db, pos)); +} + #[test] fn infer_from_bound_1() { assert_snapshot!(