Merge #2550
2550: Infer - and ! using std::ops::{Neg, Not} r=flodiebold a=kiljacken Found some low hanging fruit while taking a cursory look at index inferring. Co-authored-by: Emil Lauridsen <mine809@gmail.com>
This commit is contained in:
commit
9c9f4635b4
@ -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 {
|
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])
|
||||||
}
|
}
|
||||||
|
@ -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_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");
|
||||||
|
@ -36,8 +36,8 @@
|
|||||||
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};
|
||||||
|
|
||||||
@ -338,6 +338,24 @@ fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
|
|||||||
self.table.resolve_ty_shallow(ty)
|
self.table.resolve_ty_shallow(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_associated_type(&mut self, inner_ty: Ty, assoc_ty: Option<TypeAliasId>) -> 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
|
/// Recurses through the given type, normalizing associated types mentioned
|
||||||
/// in it by replacing them by type variables and registering obligations to
|
/// 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
|
/// resolve later. This should be done once for every type we get from some
|
||||||
@ -415,6 +433,18 @@ fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> {
|
|||||||
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)?;
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
method_resolution, op,
|
method_resolution, op,
|
||||||
traits::InEnvironment,
|
traits::InEnvironment,
|
||||||
utils::{generics, variant_data, Generics},
|
utils::{generics, variant_data, Generics},
|
||||||
CallableDef, InferTy, IntTy, Mutability, Obligation, ProjectionPredicate, ProjectionTy, Substs,
|
ApplicationTy, CallableDef, InferTy, IntTy, Mutability, Obligation, Substs, TraitRef, Ty,
|
||||||
TraitRef, Ty, TypeCtor, TypeWalk, Uncertain,
|
TypeCtor, TypeWalk, Uncertain,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch};
|
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 } => {
|
Expr::For { iterable, body, pat } => {
|
||||||
let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
|
let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
|
||||||
|
|
||||||
let pat_ty = match self.resolve_into_iter_item() {
|
let pat_ty =
|
||||||
Some(into_iter_item_alias) => {
|
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.infer_pat(*pat, &pat_ty, BindingMode::default());
|
self.infer_pat(*pat, &pat_ty, BindingMode::default());
|
||||||
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
|
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 } => {
|
Expr::Await { expr } => {
|
||||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||||
let ty = match self.resolve_future_future_output() {
|
let ty =
|
||||||
Some(future_future_output_alias) => {
|
self.resolve_associated_type(inner_ty, self.resolve_future_future_output());
|
||||||
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,
|
|
||||||
};
|
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
Expr::Try { expr } => {
|
Expr::Try { expr } => {
|
||||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||||
let ty = match self.resolve_ops_try_ok() {
|
let ty = self.resolve_associated_type(inner_ty, 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,
|
|
||||||
};
|
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
Expr::Cast { expr, type_ref } => {
|
Expr::Cast { expr, type_ref } => {
|
||||||
@ -372,31 +332,36 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
|||||||
},
|
},
|
||||||
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()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,70 @@ impl<T> crate::iter::IntoIterator for Vec<T> {
|
|||||||
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!(
|
||||||
|
Loading…
Reference in New Issue
Block a user