2445: Infer range types r=flodiebold a=oxalica



Co-authored-by: oxalica <oxalicc@pm.me>
This commit is contained in:
bors[bot] 2019-11-29 19:34:02 +00:00 committed by GitHub
commit 10c8e5eecb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 185 additions and 1 deletions

View file

@ -429,10 +429,19 @@ where
let index = self.collect_expr_opt(e.index());
self.alloc_expr(Expr::Index { base, index }, syntax_ptr)
}
ast::Expr::RangeExpr(e) => {
let lhs = e.start().map(|lhs| self.collect_expr(lhs));
let rhs = e.end().map(|rhs| self.collect_expr(rhs));
match e.op_kind() {
Some(range_type) => {
self.alloc_expr(Expr::Range { lhs, rhs, range_type }, syntax_ptr)
}
None => self.alloc_expr(Expr::Missing, syntax_ptr),
}
}
// FIXME implement HIR for these:
ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
ast::Expr::MacroCall(e) => match self.expander.enter_expand(self.db, e) {
Some((mark, expansion)) => {
let id = self.collect_expr(expansion);

View file

@ -14,6 +14,7 @@
use hir_expand::name::Name;
use ra_arena::{impl_arena_id, RawId};
use ra_syntax::ast::RangeOp;
use crate::{
builtin_type::{BuiltinFloat, BuiltinInt},
@ -130,6 +131,11 @@ pub enum Expr {
rhs: ExprId,
op: Option<BinaryOp>,
},
Range {
lhs: Option<ExprId>,
rhs: Option<ExprId>,
range_type: RangeOp,
},
Index {
base: ExprId,
index: ExprId,
@ -288,6 +294,14 @@ impl Expr {
f(*lhs);
f(*rhs);
}
Expr::Range { lhs, rhs, .. } => {
if let Some(lhs) = rhs {
f(*lhs);
}
if let Some(rhs) = lhs {
f(*rhs);
}
}
Expr::Index { base, index } => {
f(*base);
f(*index);

View file

@ -409,6 +409,36 @@ pub mod known {
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::TRY_TYPE])
}
pub fn std_ops_range() -> Path {
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::RANGE_TYPE])
}
pub fn std_ops_range_from() -> Path {
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::RANGE_FROM_TYPE])
}
pub fn std_ops_range_full() -> Path {
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::RANGE_FULL_TYPE])
}
pub fn std_ops_range_inclusive() -> Path {
Path::from_simple_segments(
PathKind::Abs,
vec![name::STD, name::OPS, name::RANGE_INCLUSIVE_TYPE],
)
}
pub fn std_ops_range_to() -> Path {
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::RANGE_TO_TYPE])
}
pub fn std_ops_range_to_inclusive() -> Path {
Path::from_simple_segments(
PathKind::Abs,
vec![name::STD, name::OPS, name::RANGE_TO_INCLUSIVE_TYPE],
)
}
pub fn std_result_result() -> Path {
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE])
}

View file

@ -140,6 +140,12 @@ pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result");
pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output");
pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
pub const RANGE_FROM_TYPE: Name = Name::new_inline_ascii(9, b"RangeFrom");
pub const RANGE_FULL_TYPE: Name = Name::new_inline_ascii(9, b"RangeFull");
pub const RANGE_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(14, b"RangeInclusive");
pub const RANGE_TO_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(16, b"RangeToInclusive");
pub const RANGE_TO_TYPE: Name = Name::new_inline_ascii(7, b"RangeTo");
pub const RANGE_TYPE: Name = Name::new_inline_ascii(5, b"Range");
// Builtin Macros
pub const FILE_MACRO: Name = Name::new_inline_ascii(4, b"file");

View file

@ -577,6 +577,42 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
Some(struct_.into())
}
fn resolve_range_full(&self) -> Option<AdtId> {
let path = known::std_ops_range_full();
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
Some(struct_.into())
}
fn resolve_range(&self) -> Option<AdtId> {
let path = known::std_ops_range();
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
Some(struct_.into())
}
fn resolve_range_inclusive(&self) -> Option<AdtId> {
let path = known::std_ops_range_inclusive();
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
Some(struct_.into())
}
fn resolve_range_from(&self) -> Option<AdtId> {
let path = known::std_ops_range_from();
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
Some(struct_.into())
}
fn resolve_range_to(&self) -> Option<AdtId> {
let path = known::std_ops_range_to();
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
Some(struct_.into())
}
fn resolve_range_to_inclusive(&self) -> Option<AdtId> {
let path = known::std_ops_range_to_inclusive();
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
Some(struct_.into())
}
}
/// The ID of a type variable.

View file

@ -12,6 +12,7 @@ use hir_def::{
AdtId, ContainerId, Lookup, StructFieldId,
};
use hir_expand::name::{self, Name};
use ra_syntax::ast::RangeOp;
use crate::{
autoderef, db::HirDatabase, method_resolution, op, traits::InEnvironment, utils::variant_data,
@ -415,6 +416,44 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
_ => Ty::Unknown,
},
Expr::Range { lhs, rhs, range_type } => {
let lhs_ty = lhs.map(|e| self.infer_expr(e, &Expectation::none()));
let rhs_expect = lhs_ty
.as_ref()
.map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone()));
let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect));
match (range_type, lhs_ty, rhs_ty) {
(RangeOp::Exclusive, None, None) => match self.resolve_range_full() {
Some(adt) => Ty::simple(TypeCtor::Adt(adt)),
None => Ty::Unknown,
},
(RangeOp::Exclusive, None, Some(ty)) => match self.resolve_range_to() {
Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
None => Ty::Unknown,
},
(RangeOp::Inclusive, None, Some(ty)) => {
match self.resolve_range_to_inclusive() {
Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
None => Ty::Unknown,
}
}
(RangeOp::Exclusive, Some(_), Some(ty)) => match self.resolve_range() {
Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
None => Ty::Unknown,
},
(RangeOp::Inclusive, Some(_), Some(ty)) => {
match self.resolve_range_inclusive() {
Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
None => Ty::Unknown,
}
}
(RangeOp::Exclusive, Some(ty), None) => match self.resolve_range_from() {
Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
None => Ty::Unknown,
},
(RangeOp::Inclusive, _, None) => Ty::Unknown,
}
}
Expr::Index { base, index } => {
let _base_ty = self.infer_expr(*base, &Expectation::none());
let _index_ty = self.infer_expr(*index, &Expectation::none());

View file

@ -221,6 +221,56 @@ mod collections {
assert_eq!("&str", type_at_pos(&db, pos));
}
#[test]
fn infer_ranges() {
let (db, pos) = TestDB::with_position(
r#"
//- /main.rs crate:main deps:std
fn test() {
let a = ..;
let b = 1..;
let c = ..2u32;
let d = 1..2usize;
let e = ..=10;
let f = 'a'..='z';
let t = (a, b, c, d, e, f);
t<|>;
}
//- /std.rs crate:std
#[prelude_import] use prelude::*;
mod prelude {}
pub mod ops {
pub struct Range<Idx> {
pub start: Idx,
pub end: Idx,
}
pub struct RangeFrom<Idx> {
pub start: Idx,
}
struct RangeFull;
pub struct RangeInclusive<Idx> {
start: Idx,
end: Idx,
is_empty: u8,
}
pub struct RangeTo<Idx> {
pub end: Idx,
}
pub struct RangeToInclusive<Idx> {
pub end: Idx,
}
}
"#,
);
assert_eq!(
"(RangeFull, RangeFrom<i32>, RangeTo<u32>, Range<usize>, RangeToInclusive<i32>, RangeInclusive<char>)",
type_at_pos(&db, pos),
);
}
#[test]
fn infer_while_let() {
let (db, pos) = TestDB::with_position(