Correctly infer - and ! using std::ops::{Neg,Not}

This commit is contained in:
Emil Lauridsen 2019-12-13 12:44:42 +01:00
parent 95dc2de8e9
commit 7705209051
5 changed files with 114 additions and 23 deletions

View file

@ -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 { pub fn std_result_result() -> Path {
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE]) Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE])
} }

View file

@ -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_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(b"RangeToInclusive");
pub const RANGE_TO_TYPE: Name = Name::new_inline_ascii(b"RangeTo"); 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 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 // Builtin Macros
pub const FILE_MACRO: Name = Name::new_inline_ascii(b"file"); pub const FILE_MACRO: Name = Name::new_inline_ascii(b"file");

View file

@ -36,8 +36,8 @@ use ra_prof::profile;
use super::{ use super::{
primitive::{FloatTy, IntTy}, primitive::{FloatTy, IntTy},
traits::{Guidance, Obligation, ProjectionPredicate, Solution}, traits::{Guidance, Obligation, ProjectionPredicate, Solution},
ApplicationTy, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, ApplicationTy, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
Uncertain, TypeWalk, Uncertain,
}; };
use crate::{db::HirDatabase, infer::diagnostics::InferenceDiagnostic}; 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) self.db.trait_data(trait_).associated_type_by_name(&name::OK_TYPE)
} }
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
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<TypeAliasId> {
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<TypeAliasId> { fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
let path = known::std_future_future(); let path = known::std_future_future();
let trait_ = self.resolver.resolve_known_trait(self.db, &path)?; let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;

View file

@ -332,31 +332,36 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}, },
UnaryOp::Neg => { UnaryOp::Neg => {
match &inner_ty { match &inner_ty {
Ty::Apply(a_ty) => match a_ty.ctor { // Fast path for builtins
TypeCtor::Int(Uncertain::Unknown) Ty::Apply(ApplicationTy {
| TypeCtor::Int(Uncertain::Known(IntTy { ctor:
signedness: Signedness::Signed, TypeCtor::Int(Uncertain::Known(IntTy {
.. signedness: Signedness::Signed,
})) ..
| TypeCtor::Float(..) => inner_ty, })),
_ => Ty::Unknown, ..
}, })
Ty::Infer(InferTy::IntVar(..)) | Ty::Infer(InferTy::FloatVar(..)) => { | Ty::Apply(ApplicationTy {
inner_ty ctor: TypeCtor::Int(Uncertain::Unknown),
} ..
// FIXME: resolve ops::Neg trait })
_ => Ty::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 => { UnaryOp::Not => {
match &inner_ty { match &inner_ty {
Ty::Apply(a_ty) => match a_ty.ctor { // Fast path for builtins
TypeCtor::Bool | TypeCtor::Int(_) => inner_ty, Ty::Apply(ApplicationTy { ctor: TypeCtor::Bool, .. })
_ => Ty::Unknown, | Ty::Apply(ApplicationTy { ctor: TypeCtor::Int(_), .. })
}, | Ty::Infer(InferTy::IntVar(..)) => inner_ty,
Ty::Infer(InferTy::IntVar(..)) => inner_ty, // Otherwise we resolve via the std::ops::Not trait
// FIXME: resolve ops::Not trait for inner_ty _ => self
_ => Ty::Unknown, .resolve_associated_type(inner_ty, self.resolve_ops_not_output()),
} }
} }
} }

View file

@ -115,6 +115,70 @@ mod collections {
assert_eq!("&str", type_at_pos(&db, pos)); 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] #[test]
fn infer_from_bound_1() { fn infer_from_bound_1() {
assert_snapshot!( assert_snapshot!(