diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index ec9d13e827..50f0cad94f 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs @@ -342,6 +342,14 @@ pub mod known { ) } + 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 4f2f702c01..9e68dd98d7 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs @@ -152,6 +152,8 @@ pub const RANGE_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(b"RangeInclusive") 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 62d5c8803c..a1201b3e47 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -36,8 +36,8 @@ use ra_prof::profile; 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 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { 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 6110f5abd3..f8c00a7b4a 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -332,31 +332,36 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }, 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 93c5f9a15a..6139adb72e 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -115,6 +115,70 @@ mod collections { 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!(