From 5f5dc20d85dead5fbd51d163451f796255c9faea Mon Sep 17 00:00:00 2001 From: Marcus Klaas de Vries Date: Thu, 10 Jan 2019 16:03:15 +0100 Subject: Try implementing integer type inference (WIP) --- crates/ra_hir/src/expr.rs | 58 +++++++++++++++++++++------ crates/ra_hir/src/ty.rs | 53 ++++++++++++------------ crates/ra_hir/src/ty/primitive.rs | 50 +++++++++++++++++++++++ crates/ra_hir/src/ty/tests/data/basics.txt | 4 +- crates/ra_hir/src/ty/tests/data/binary_op.txt | 2 +- crates/ra_hir/src/ty/tests/data/let.txt | 4 +- crates/ra_hir/src/ty/tests/data/literals.txt | 2 +- 7 files changed, 128 insertions(+), 45 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index e07725d05..bc8515836 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -11,6 +11,7 @@ use ra_syntax::{ }; use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName}; +use crate::ty::primitive::{UintTy, IntTy, FloatTy, UncertainIntTy, UncertainFloatTy}; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ExprId(RawId); @@ -112,9 +113,8 @@ pub enum Literal { ByteString(Vec), Char(char), Bool(bool), - Byte(u8), - Int, // this and float need additional information - Float, + Int(u64, UncertainIntTy), + Float(u64, UncertainFloatTy), // FIXME: f64 is not Eq Tuple { values: Vec }, Array { values: Vec }, } @@ -328,13 +328,7 @@ impl Expr { f(val); } } - Literal::String(..) - | Literal::ByteString(..) - | Literal::Byte(..) - | Literal::Bool(..) - | Literal::Char(..) - | Literal::Int - | Literal::Float => {} + _ => {} }, } } @@ -669,8 +663,43 @@ impl ExprCollector { if let Some(c) = child { let lit = match c.kind() { - SyntaxKind::INT_NUMBER => Literal::Int, - SyntaxKind::FLOAT_NUMBER => Literal::Float, + SyntaxKind::INT_NUMBER => { + let text = c.text().to_string(); + + // FIXME: don't do it like this. maybe use something like + // the IntTy::from_name functions + let ty = if text.ends_with("isize") { + UncertainIntTy::Signed(IntTy::Isize) + } else if text.ends_with("i128") { + UncertainIntTy::Signed(IntTy::I128) + } else if text.ends_with("i64") { + UncertainIntTy::Signed(IntTy::I64) + } else if text.ends_with("i32") { + UncertainIntTy::Signed(IntTy::I32) + } else if text.ends_with("i16") { + UncertainIntTy::Signed(IntTy::I16) + } else if text.ends_with("i8") { + UncertainIntTy::Signed(IntTy::I8) + } else if text.ends_with("usize") { + UncertainIntTy::Unsigned(UintTy::Usize) + } else if text.ends_with("u128") { + UncertainIntTy::Unsigned(UintTy::U128) + } else if text.ends_with("u64") { + UncertainIntTy::Unsigned(UintTy::U64) + } else if text.ends_with("u32") { + UncertainIntTy::Unsigned(UintTy::U32) + } else if text.ends_with("u16") { + UncertainIntTy::Unsigned(UintTy::U16) + } else if text.ends_with("u8") { + UncertainIntTy::Unsigned(UintTy::U8) + } else { + UncertainIntTy::Unknown + }; + + // TODO: actually parse integer + Literal::Int(0u64, ty) + } + SyntaxKind::FLOAT_NUMBER => Literal::Float(0, UncertainFloatTy::Unknown), SyntaxKind::STRING => { // FIXME: this likely includes the " characters let text = c.text().to_string(); @@ -698,7 +727,10 @@ impl ExprCollector { } SyntaxKind::BYTE => { let character = c.text().char_at(1).unwrap_or('X'); - Literal::Byte(character as u8) + Literal::Int( + character as u8 as u64, + UncertainIntTy::Unsigned(UintTy::U8), + ) } _ => return self.alloc_expr(Expr::Missing, syntax_ptr), }; diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 0baa205a1..13a1c2907 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -14,7 +14,7 @@ //! rustc. mod autoderef; -mod primitive; +pub(crate) mod primitive; #[cfg(test)] mod tests; pub(crate) mod method_resolution; @@ -151,14 +151,13 @@ pub enum Ty { /// (a non-surrogate code point). Written as `char`. Char, - /// A primitive signed integer type. For example, `i32`. - Int(primitive::IntTy), - - /// A primitive unsigned integer type. For example, `u32`. - Uint(primitive::UintTy), + /// A primitive integer type. For example, `i32`. + Int(primitive::UncertainIntTy), + // /// A primitive unsigned integer type. For example, `u32`. + // Uint(primitive::UintTy), /// A primitive floating-point type. For example, `f64`. - Float(primitive::FloatTy), + Float(primitive::UncertainFloatTy), /// Structures, enumerations and unions. Adt { @@ -318,11 +317,9 @@ impl Ty { return Ok(Ty::Char); } else if let Some(KnownName::Str) = name.as_known_name() { return Ok(Ty::Str); - } else if let Some(int_ty) = primitive::IntTy::from_name(name) { + } else if let Some(int_ty) = primitive::UncertainIntTy::from_name(name) { return Ok(Ty::Int(int_ty)); - } else if let Some(uint_ty) = primitive::UintTy::from_name(name) { - return Ok(Ty::Uint(uint_ty)); - } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { + } else if let Some(float_ty) = primitive::UncertainFloatTy::from_name(name) { return Ok(Ty::Float(float_ty)); } else if name.as_known_name() == Some(KnownName::SelfType) { return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type())); @@ -392,7 +389,6 @@ impl fmt::Display for Ty { Ty::Bool => write!(f, "bool"), Ty::Char => write!(f, "char"), Ty::Int(t) => write!(f, "{}", t.ty_to_string()), - Ty::Uint(t) => write!(f, "{}", t.ty_to_string()), Ty::Float(t) => write!(f, "{}", t.ty_to_string()), Ty::Str => write!(f, "str"), Ty::Slice(t) => write!(f, "[{}]", t), @@ -587,7 +583,7 @@ fn binary_op_return_ty(op: BinaryOp, rhs_ty: Ty) -> Ty { | BinaryOp::BitwiseAnd | BinaryOp::BitwiseOr | BinaryOp::BitwiseXor => match rhs_ty { - Ty::Uint(..) | Ty::Int(..) | Ty::Float(..) => rhs_ty, + Ty::Int(..) | Ty::Float(..) => rhs_ty, _ => Ty::Unknown, }, BinaryOp::RangeRightOpen | BinaryOp::RangeRightClosed => Ty::Unknown, @@ -598,7 +594,7 @@ fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { match op { BinaryOp::BooleanAnd | BinaryOp::BooleanOr => Ty::Bool, BinaryOp::Assignment | BinaryOp::EqualityTest => match lhs_ty { - Ty::Uint(..) | Ty::Int(..) | Ty::Float(..) | Ty::Str | Ty::Char | Ty::Bool => lhs_ty, + Ty::Int(..) | Ty::Float(..) | Ty::Str | Ty::Char | Ty::Bool => lhs_ty, _ => Ty::Unknown, }, BinaryOp::LesserEqualTest @@ -625,7 +621,7 @@ fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { | BinaryOp::BitwiseAnd | BinaryOp::BitwiseOr | BinaryOp::BitwiseXor => match lhs_ty { - Ty::Uint(..) | Ty::Int(..) | Ty::Float(..) => lhs_ty, + Ty::Int(..) | Ty::Float(..) => lhs_ty, _ => Ty::Unknown, }, _ => Ty::Unknown, @@ -695,13 +691,17 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { match (&*ty1, &*ty2) { (Ty::Unknown, ..) => true, (.., Ty::Unknown) => true, - (Ty::Bool, _) - | (Ty::Str, _) - | (Ty::Never, _) - | (Ty::Char, _) - | (Ty::Int(..), Ty::Int(..)) - | (Ty::Uint(..), Ty::Uint(..)) - | (Ty::Float(..), Ty::Float(..)) => ty1 == ty2, + (Ty::Int(t1), Ty::Int(t2)) => match (t1, t2) { + (primitive::UncertainIntTy::Unknown, _) + | (_, primitive::UncertainIntTy::Unknown) => true, + _ => t1 == t2, + }, + (Ty::Float(t1), Ty::Float(t2)) => match (t1, t2) { + (primitive::UncertainFloatTy::Unknown, _) + | (_, primitive::UncertainFloatTy::Unknown) => true, + _ => t1 == t2, + }, + (Ty::Bool, _) | (Ty::Str, _) | (Ty::Never, _) | (Ty::Char, _) => ty1 == ty2, ( Ty::Adt { def_id: def_id1, .. @@ -1071,11 +1071,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Literal::Bool(..) => Ty::Bool, Literal::String(..) => Ty::Ref(Arc::new(Ty::Str), Mutability::Shared), Literal::ByteString(..) => { - let byte_type = Arc::new(Ty::Uint(primitive::UintTy::U8)); + let byte_type = Arc::new(Ty::Int(primitive::UncertainIntTy::Unsigned( + primitive::UintTy::U8, + ))); let slice_type = Arc::new(Ty::Slice(byte_type)); Ty::Ref(slice_type, Mutability::Shared) } - Literal::Byte(..) => Ty::Uint(primitive::UintTy::U8), Literal::Char(..) => Ty::Char, Literal::Tuple { values } => { let mut inner_tys = Vec::new(); @@ -1095,8 +1096,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { // available Ty::Slice(Arc::new(inner_ty)) } - // TODO - Literal::Int | Literal::Float => Ty::Unknown, + Literal::Int(_v, ty) => Ty::Int(*ty), + Literal::Float(_v, ty) => Ty::Float(*ty), }, }; // use a new type variable if we got Ty::Unknown here diff --git a/crates/ra_hir/src/ty/primitive.rs b/crates/ra_hir/src/ty/primitive.rs index 498d42d52..5741ca90d 100644 --- a/crates/ra_hir/src/ty/primitive.rs +++ b/crates/ra_hir/src/ty/primitive.rs @@ -2,6 +2,56 @@ use std::fmt; use crate::{Name, KnownName}; +#[derive(Debug, Clone, Eq, PartialEq, Hash, Copy)] +pub enum UncertainIntTy { + Unknown, + Unsigned(UintTy), + Signed(IntTy), +} + +impl UncertainIntTy { + pub fn ty_to_string(&self) -> &'static str { + match *self { + UncertainIntTy::Unknown => "{integer}", + UncertainIntTy::Signed(ty) => ty.ty_to_string(), + UncertainIntTy::Unsigned(ty) => ty.ty_to_string(), + } + } + + pub fn from_name(name: &Name) -> Option { + if let Some(ty) = IntTy::from_name(name) { + Some(UncertainIntTy::Signed(ty)) + } else if let Some(ty) = UintTy::from_name(name) { + Some(UncertainIntTy::Unsigned(ty)) + } else { + None + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Copy)] +pub enum UncertainFloatTy { + Unknown, + Known(FloatTy), +} + +impl UncertainFloatTy { + pub fn ty_to_string(&self) -> &'static str { + match *self { + UncertainFloatTy::Unknown => "{float}", + UncertainFloatTy::Known(ty) => ty.ty_to_string(), + } + } + + pub fn from_name(name: &Name) -> Option { + if let Some(ty) = FloatTy::from_name(name) { + Some(UncertainFloatTy::Known(ty)) + } else { + None + } + } +} + #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] pub enum IntTy { Isize, diff --git a/crates/ra_hir/src/ty/tests/data/basics.txt b/crates/ra_hir/src/ty/tests/data/basics.txt index e02947ba8..4a3b69b7e 100644 --- a/crates/ra_hir/src/ty/tests/data/basics.txt +++ b/crates/ra_hir/src/ty/tests/data/basics.txt @@ -7,7 +7,7 @@ [55; 56) 'b': isize [62; 63) 'c': ! [69; 70) 'd': &str -[76; 82) '1usize': [unknown] -[88; 94) '1isize': [unknown] +[76; 82) '1usize': usize +[88; 94) '1isize': isize [100; 106) '"test"': &str [112; 118) '1.0f32': [unknown] diff --git a/crates/ra_hir/src/ty/tests/data/binary_op.txt b/crates/ra_hir/src/ty/tests/data/binary_op.txt index 8a515ac5e..7fdb8a900 100644 --- a/crates/ra_hir/src/ty/tests/data/binary_op.txt +++ b/crates/ra_hir/src/ty/tests/data/binary_op.txt @@ -16,7 +16,7 @@ [112; 113) 'y': bool [123; 134) 'minus_forty': isize [144; 152) '-40isize': isize -[145; 152) '40isize': [unknown] +[145; 152) '40isize': isize [162; 163) 'h': bool [166; 177) 'minus_forty': isize [166; 188) 'minus_...ONST_2': bool diff --git a/crates/ra_hir/src/ty/tests/data/let.txt b/crates/ra_hir/src/ty/tests/data/let.txt index 30f4a2cf5..8815dba41 100644 --- a/crates/ra_hir/src/ty/tests/data/let.txt +++ b/crates/ra_hir/src/ty/tests/data/let.txt @@ -1,6 +1,6 @@ [11; 71) '{ ...= b; }': () -[21; 22) 'a': [unknown] -[25; 31) '1isize': [unknown] +[21; 22) 'a': isize +[25; 31) '1isize': isize [41; 42) 'b': usize [52; 53) '1': usize [63; 64) 'c': usize diff --git a/crates/ra_hir/src/ty/tests/data/literals.txt b/crates/ra_hir/src/ty/tests/data/literals.txt index 8b22eee31..e139d57a8 100644 --- a/crates/ra_hir/src/ty/tests/data/literals.txt +++ b/crates/ra_hir/src/ty/tests/data/literals.txt @@ -1,5 +1,5 @@ [11; 135) '{ ...lse] }': () -[17; 21) '5i32': [unknown] +[17; 21) '5i32': i32 [27; 34) '"hello"': &str [40; 48) 'b"bytes"': &[u8] [54; 57) ''c'': char -- cgit v1.2.3