From a6146d35b1615cf5fb908b29f34e58bfde3bf96d Mon Sep 17 00:00:00 2001 From: Marcus Klaas de Vries Date: Thu, 10 Jan 2019 13:54:58 +0100 Subject: Implement type inference for literals (WIP) --- crates/ra_hir/src/expr.rs | 78 +++++++++++++++++++++++++++- crates/ra_hir/src/ty.rs | 33 +++++++++++- crates/ra_hir/src/ty/tests.rs | 20 +++++++ crates/ra_hir/src/ty/tests/data/basics.txt | 2 +- crates/ra_hir/src/ty/tests/data/literals.txt | 10 ++++ 5 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 crates/ra_hir/src/ty/tests/data/literals.txt (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index f0936e9f3..e07725d05 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -5,7 +5,10 @@ use rustc_hash::FxHashMap; use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; use ra_db::{LocalSyntaxPtr, Cancelable}; -use ra_syntax::ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner}; +use ra_syntax::{ + SyntaxKind, + ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner} +}; use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName}; @@ -103,6 +106,19 @@ impl BodySyntaxMapping { } } +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Literal { + String(String), + ByteString(Vec), + Char(char), + Bool(bool), + Byte(u8), + Int, // this and float need additional information + Float, + Tuple { values: Vec }, + Array { values: Vec }, +} + #[derive(Debug, Clone, Eq, PartialEq)] pub enum Expr { /// This is produced if syntax tree does not have a required expression piece. @@ -186,6 +202,7 @@ pub enum Expr { Tuple { exprs: Vec, }, + Literal(Literal), } pub use ra_syntax::ast::PrefixOp as UnaryOp; @@ -305,6 +322,20 @@ impl Expr { f(*expr); } } + Expr::Literal(l) => match l { + Literal::Array { values } | Literal::Tuple { values } => { + for &val in values { + f(val); + } + } + Literal::String(..) + | Literal::ByteString(..) + | Literal::Byte(..) + | Literal::Bool(..) + | Literal::Char(..) + | Literal::Int + | Literal::Float => {} + }, } } } @@ -633,13 +664,56 @@ impl ExprCollector { let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect(); self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr) } + ast::ExprKind::Literal(e) => { + let child = e.syntax().children().next(); + + if let Some(c) = child { + let lit = match c.kind() { + SyntaxKind::INT_NUMBER => Literal::Int, + SyntaxKind::FLOAT_NUMBER => Literal::Float, + SyntaxKind::STRING => { + // FIXME: this likely includes the " characters + let text = c.text().to_string(); + Literal::String(text) + } + SyntaxKind::ARRAY_EXPR => { + // TODO: recursively call to self + Literal::Array { values: vec![] } + } + SyntaxKind::PAREN_EXPR => { + // TODO: recursively call to self + Literal::Tuple { values: vec![] } + } + SyntaxKind::TRUE_KW => Literal::Bool(true), + SyntaxKind::FALSE_KW => Literal::Bool(false), + SyntaxKind::BYTE_STRING => { + // FIXME: this is completely incorrect for a variety + // of reasons, but at least it gives the right type + let bytes = c.text().to_string().into_bytes(); + Literal::ByteString(bytes) + } + SyntaxKind::CHAR => { + let character = c.text().char_at(1).unwrap_or('X'); + Literal::Char(character) + } + SyntaxKind::BYTE => { + let character = c.text().char_at(1).unwrap_or('X'); + Literal::Byte(character as u8) + } + _ => return self.alloc_expr(Expr::Missing, syntax_ptr), + }; + + self.alloc_expr(Expr::Literal(lit), syntax_ptr) + } else { + self.alloc_expr(Expr::Missing, syntax_ptr) + } + } // TODO implement HIR for these: ast::ExprKind::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::ExprKind::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::ExprKind::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::ExprKind::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), - ast::ExprKind::Literal(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), } } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index fa46ddfe9..0baa205a1 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -38,7 +38,7 @@ use crate::{ db::HirDatabase, type_ref::{TypeRef, Mutability}, name::KnownName, - expr::{Body, Expr, ExprId, PatId, UnaryOp, BinaryOp, Statement}, + expr::{Body, Expr, Literal, ExprId, PatId, UnaryOp, BinaryOp, Statement}, }; fn transpose(x: Cancelable>) -> Option> { @@ -1067,6 +1067,37 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Ty::Tuple(Arc::from(ty_vec)) } + Expr::Literal(lit) => match lit { + 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 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(); + for &expr in values { + let inner_ty = self.infer_expr(expr, &Expectation::none())?; + inner_tys.push(inner_ty); + } + Ty::Tuple(Arc::from(inner_tys)) + } + Literal::Array { values } => { + // simply take the type of the first element for now + let inner_ty = match values.get(0) { + Some(&expr) => self.infer_expr(expr, &Expectation::none())?, + None => Ty::Unknown, + }; + // TODO: we should return a Ty::Array when it becomes + // available + Ty::Slice(Arc::new(inner_ty)) + } + // TODO + Literal::Int | Literal::Float => Ty::Unknown, + }, }; // use a new type variable if we got Ty::Unknown here let ty = self.insert_type_vars_shallow(ty); diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 920188fc9..97d3d222f 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -132,6 +132,26 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { ); } +#[test] +fn infer_literals() { + check_inference( + r#" +fn test() { + 5i32; + "hello"; + b"bytes"; + 'c'; + b'b'; + 3.14; + 5000; + (0u32, -5isize); + [true, true, false] +} +"#, + "literals.txt", + ); +} + #[test] fn infer_backwards() { check_inference( diff --git a/crates/ra_hir/src/ty/tests/data/basics.txt b/crates/ra_hir/src/ty/tests/data/basics.txt index ac7faae0a..e02947ba8 100644 --- a/crates/ra_hir/src/ty/tests/data/basics.txt +++ b/crates/ra_hir/src/ty/tests/data/basics.txt @@ -9,5 +9,5 @@ [69; 70) 'd': &str [76; 82) '1usize': [unknown] [88; 94) '1isize': [unknown] -[100; 106) '"test"': [unknown] +[100; 106) '"test"': &str [112; 118) '1.0f32': [unknown] diff --git a/crates/ra_hir/src/ty/tests/data/literals.txt b/crates/ra_hir/src/ty/tests/data/literals.txt new file mode 100644 index 000000000..8b22eee31 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/literals.txt @@ -0,0 +1,10 @@ +[11; 135) '{ ...lse] }': () +[17; 21) '5i32': [unknown] +[27; 34) '"hello"': &str +[40; 48) 'b"bytes"': &[u8] +[54; 57) ''c'': char +[63; 67) 'b'b'': u8 +[73; 77) '3.14': [unknown] +[83; 87) '5000': [unknown] +[93; 108) '(0u32, -5isize)': [unknown] +[114; 133) '[true,...false]': () -- cgit v1.2.3 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 From 1574715be5d3fc7e07160708810dcbc9c1b01733 Mon Sep 17 00:00:00 2001 From: Marcus Klaas de Vries Date: Thu, 10 Jan 2019 18:08:54 +0100 Subject: Use type variables to determine exact type for ambiguous numeric literals --- crates/ra_hir/src/expr.rs | 17 ++++++++++- crates/ra_hir/src/ty.rs | 45 ++++++++++++++++++++++++---- crates/ra_hir/src/ty/tests.rs | 1 + crates/ra_hir/src/ty/tests/data/basics.txt | 2 +- crates/ra_hir/src/ty/tests/data/literals.txt | 9 +++--- crates/ra_hir/src/ty/tests/data/struct.txt | 4 +-- 6 files changed, 64 insertions(+), 14 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index bc8515836..2798937a6 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -699,7 +699,22 @@ impl ExprCollector { // TODO: actually parse integer Literal::Int(0u64, ty) } - SyntaxKind::FLOAT_NUMBER => Literal::Float(0, UncertainFloatTy::Unknown), + SyntaxKind::FLOAT_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("f64") { + UncertainFloatTy::Known(FloatTy::F64) + } else if text.ends_with("f32") { + UncertainFloatTy::Known(FloatTy::F32) + } else { + UncertainFloatTy::Unknown + }; + + // TODO: actually parse value + Literal::Float(0, ty) + } SyntaxKind::STRING => { // FIXME: this likely includes the " characters let text = c.text().to_string(); diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 13a1c2907..b4b338874 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -114,6 +114,8 @@ impl UnifyValue for TypeVarValue { #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum InferTy { TypeVar(TypeVarId), + IntVar(TypeVarId), + FloatVar(TypeVarId), } /// When inferring an expression, we propagate downward whatever type hint we @@ -718,12 +720,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .iter() .zip(ts2.iter()) .all(|(t1, t2)| self.unify(t1, t2)), - (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) => { + (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) + | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2))) + | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) => { // both type vars are unknown since we tried to resolve them self.var_unification_table.union(*tv1, *tv2); true } - (Ty::Infer(InferTy::TypeVar(tv)), other) | (other, Ty::Infer(InferTy::TypeVar(tv))) => { + (Ty::Infer(InferTy::TypeVar(tv)), other) + | (other, Ty::Infer(InferTy::TypeVar(tv))) + | (Ty::Infer(InferTy::IntVar(tv)), other) + | (other, Ty::Infer(InferTy::IntVar(tv))) + | (Ty::Infer(InferTy::FloatVar(tv)), other) + | (other, Ty::Infer(InferTy::FloatVar(tv))) => { // the type var is unknown since we tried to resolve it self.var_unification_table .union_value(*tv, TypeVarValue::Known(other.clone())); @@ -739,10 +748,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { )) } + fn new_integer_var(&mut self) -> Ty { + Ty::Infer(InferTy::IntVar( + self.var_unification_table.new_key(TypeVarValue::Unknown), + )) + } + + fn new_float_var(&mut self) -> Ty { + Ty::Infer(InferTy::FloatVar( + self.var_unification_table.new_key(TypeVarValue::Unknown), + )) + } + /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it. fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { match ty { Ty::Unknown => self.new_type_var(), + Ty::Int(primitive::UncertainIntTy::Unknown) => self.new_integer_var(), + Ty::Float(primitive::UncertainFloatTy::Unknown) => self.new_float_var(), _ => ty, } } @@ -757,12 +780,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { /// known type. fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty { ty.fold(&mut |ty| match ty { - Ty::Infer(InferTy::TypeVar(tv)) => { + Ty::Infer(InferTy::TypeVar(tv)) + | Ty::Infer(InferTy::IntVar(tv)) + | Ty::Infer(InferTy::FloatVar(tv)) => { if let Some(known_ty) = self.var_unification_table.probe_value(tv).known() { // known_ty may contain other variables that are known by now self.resolve_ty_as_possible(known_ty.clone()) } else { - Ty::Infer(InferTy::TypeVar(tv)) + ty } } _ => ty, @@ -790,12 +815,20 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { /// replaced by Ty::Unknown. fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { ty.fold(&mut |ty| match ty { - Ty::Infer(InferTy::TypeVar(tv)) => { + Ty::Infer(i) => { + let tv = match i { + InferTy::TypeVar(tv) | InferTy::IntVar(tv) | InferTy::FloatVar(tv) => tv, + }; + if let Some(known_ty) = self.var_unification_table.probe_value(tv).known() { // known_ty may contain other variables that are known by now self.resolve_ty_completely(known_ty.clone()) } else { - Ty::Unknown + match i { + InferTy::TypeVar(..) => Ty::Unknown, + InferTy::IntVar(..) => Ty::Int(primitive::UncertainIntTy::Unknown), + InferTy::FloatVar(..) => Ty::Float(primitive::UncertainFloatTy::Unknown), + } } } _ => ty, diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 97d3d222f..0c43415a6 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -145,6 +145,7 @@ fn test() { 3.14; 5000; (0u32, -5isize); + false; [true, true, false] } "#, diff --git a/crates/ra_hir/src/ty/tests/data/basics.txt b/crates/ra_hir/src/ty/tests/data/basics.txt index 4a3b69b7e..e65fe07aa 100644 --- a/crates/ra_hir/src/ty/tests/data/basics.txt +++ b/crates/ra_hir/src/ty/tests/data/basics.txt @@ -10,4 +10,4 @@ [76; 82) '1usize': usize [88; 94) '1isize': isize [100; 106) '"test"': &str -[112; 118) '1.0f32': [unknown] +[112; 118) '1.0f32': f32 diff --git a/crates/ra_hir/src/ty/tests/data/literals.txt b/crates/ra_hir/src/ty/tests/data/literals.txt index e139d57a8..df435edd7 100644 --- a/crates/ra_hir/src/ty/tests/data/literals.txt +++ b/crates/ra_hir/src/ty/tests/data/literals.txt @@ -1,10 +1,11 @@ -[11; 135) '{ ...lse] }': () +[11; 146) '{ ...lse] }': () [17; 21) '5i32': i32 [27; 34) '"hello"': &str [40; 48) 'b"bytes"': &[u8] [54; 57) ''c'': char [63; 67) 'b'b'': u8 -[73; 77) '3.14': [unknown] -[83; 87) '5000': [unknown] +[73; 77) '3.14': {float} +[83; 87) '5000': {integer} [93; 108) '(0u32, -5isize)': [unknown] -[114; 133) '[true,...false]': () +[114; 119) 'false': bool +[125; 144) '[true,...false]': () diff --git a/crates/ra_hir/src/ty/tests/data/struct.txt b/crates/ra_hir/src/ty/tests/data/struct.txt index 7b324c82f..dcdf61363 100644 --- a/crates/ra_hir/src/ty/tests/data/struct.txt +++ b/crates/ra_hir/src/ty/tests/data/struct.txt @@ -2,14 +2,14 @@ [82; 83) 'c': [unknown] [86; 87) 'C': [unknown] [86; 90) 'C(1)': [unknown] -[88; 89) '1': [unknown] +[88; 89) '1': {integer} [96; 97) 'B': [unknown] [107; 108) 'a': A [114; 133) 'A { b:...C(1) }': A [121; 122) 'B': B [127; 128) 'C': [unknown] [127; 131) 'C(1)': C -[129; 130) '1': [unknown] +[129; 130) '1': {integer} [139; 140) 'a': A [139; 142) 'a.b': B [148; 149) 'a': A -- cgit v1.2.3 From 81bc8e4973fefd0ff31d08206c374fb58aa8b6e0 Mon Sep 17 00:00:00 2001 From: Marcus Klaas de Vries Date: Fri, 11 Jan 2019 09:47:31 +0100 Subject: don't try to treat arrays and tuples as literals --- crates/ra_hir/src/expr.rs | 19 +---------- crates/ra_hir/src/ty.rs | 49 ++++++++++++---------------- crates/ra_hir/src/ty/tests.rs | 2 -- crates/ra_hir/src/ty/tests/data/literals.txt | 10 +++--- crates/ra_hir/src/ty/tests/data/struct.txt | 4 +-- 5 files changed, 27 insertions(+), 57 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 2798937a6..52af2af45 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -115,8 +115,6 @@ pub enum Literal { Bool(bool), Int(u64, UncertainIntTy), Float(u64, UncertainFloatTy), // FIXME: f64 is not Eq - Tuple { values: Vec }, - Array { values: Vec }, } #[derive(Debug, Clone, Eq, PartialEq)] @@ -322,14 +320,7 @@ impl Expr { f(*expr); } } - Expr::Literal(l) => match l { - Literal::Array { values } | Literal::Tuple { values } => { - for &val in values { - f(val); - } - } - _ => {} - }, + Expr::Literal(_) => {} } } } @@ -720,14 +711,6 @@ impl ExprCollector { let text = c.text().to_string(); Literal::String(text) } - SyntaxKind::ARRAY_EXPR => { - // TODO: recursively call to self - Literal::Array { values: vec![] } - } - SyntaxKind::PAREN_EXPR => { - // TODO: recursively call to self - Literal::Tuple { values: vec![] } - } SyntaxKind::TRUE_KW => Literal::Bool(true), SyntaxKind::FALSE_KW => Literal::Bool(false), SyntaxKind::BYTE_STRING => { diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index b4b338874..de5ec5b46 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -107,9 +107,9 @@ impl UnifyValue for TypeVarValue { } } -/// The kinds of placeholders we need during type inference. Currently, we only -/// have type variables; in the future, we will probably also need int and float -/// variables, for inference of literal values (e.g. `100` could be one of +/// The kinds of placeholders we need during type inference. There's seperate +/// values for general types, and for integer and float variables. The latter +/// two are used for inference of literal values (e.g. `100` could be one of /// several integer types). #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum InferTy { @@ -118,6 +118,20 @@ pub enum InferTy { FloatVar(TypeVarId), } +impl InferTy { + fn fallback_value(self) -> Ty { + match self { + InferTy::TypeVar(..) => Ty::Unknown, + InferTy::IntVar(..) => { + Ty::Int(primitive::UncertainIntTy::Signed(primitive::IntTy::I32)) + } + InferTy::FloatVar(..) => { + Ty::Float(primitive::UncertainFloatTy::Known(primitive::FloatTy::F64)) + } + } + } +} + /// When inferring an expression, we propagate downward whatever type hint we /// are able in the form of an `Expectation`. #[derive(Clone, PartialEq, Eq, Debug)] @@ -156,8 +170,6 @@ pub enum Ty { /// 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::UncertainFloatTy), @@ -199,8 +211,9 @@ pub enum Ty { // above function pointer type. Once we implement generics, we will probably // need this as well. - // A trait, defined with `dyn trait`. + // A trait, defined with `dyn Trait`. // Dynamic(), + // The anonymous type of a closure. Used to represent the type of // `|a| a`. // Closure(DefId, ClosureSubsts<'tcx>), @@ -824,11 +837,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { // known_ty may contain other variables that are known by now self.resolve_ty_completely(known_ty.clone()) } else { - match i { - InferTy::TypeVar(..) => Ty::Unknown, - InferTy::IntVar(..) => Ty::Int(primitive::UncertainIntTy::Unknown), - InferTy::FloatVar(..) => Ty::Float(primitive::UncertainFloatTy::Unknown), - } + i.fallback_value() } } _ => ty, @@ -1111,24 +1120,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Ty::Ref(slice_type, Mutability::Shared) } Literal::Char(..) => Ty::Char, - Literal::Tuple { values } => { - let mut inner_tys = Vec::new(); - for &expr in values { - let inner_ty = self.infer_expr(expr, &Expectation::none())?; - inner_tys.push(inner_ty); - } - Ty::Tuple(Arc::from(inner_tys)) - } - Literal::Array { values } => { - // simply take the type of the first element for now - let inner_ty = match values.get(0) { - Some(&expr) => self.infer_expr(expr, &Expectation::none())?, - None => Ty::Unknown, - }; - // TODO: we should return a Ty::Array when it becomes - // available - Ty::Slice(Arc::new(inner_ty)) - } Literal::Int(_v, ty) => Ty::Int(*ty), Literal::Float(_v, ty) => Ty::Float(*ty), }, diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 0c43415a6..53ea99874 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -144,9 +144,7 @@ fn test() { b'b'; 3.14; 5000; - (0u32, -5isize); false; - [true, true, false] } "#, "literals.txt", diff --git a/crates/ra_hir/src/ty/tests/data/literals.txt b/crates/ra_hir/src/ty/tests/data/literals.txt index df435edd7..8d6079c98 100644 --- a/crates/ra_hir/src/ty/tests/data/literals.txt +++ b/crates/ra_hir/src/ty/tests/data/literals.txt @@ -1,11 +1,9 @@ -[11; 146) '{ ...lse] }': () +[11; 101) '{ ...lse; }': () [17; 21) '5i32': i32 [27; 34) '"hello"': &str [40; 48) 'b"bytes"': &[u8] [54; 57) ''c'': char [63; 67) 'b'b'': u8 -[73; 77) '3.14': {float} -[83; 87) '5000': {integer} -[93; 108) '(0u32, -5isize)': [unknown] -[114; 119) 'false': bool -[125; 144) '[true,...false]': () +[73; 77) '3.14': f64 +[83; 87) '5000': i32 +[93; 98) 'false': bool diff --git a/crates/ra_hir/src/ty/tests/data/struct.txt b/crates/ra_hir/src/ty/tests/data/struct.txt index dcdf61363..be9e12d02 100644 --- a/crates/ra_hir/src/ty/tests/data/struct.txt +++ b/crates/ra_hir/src/ty/tests/data/struct.txt @@ -2,14 +2,14 @@ [82; 83) 'c': [unknown] [86; 87) 'C': [unknown] [86; 90) 'C(1)': [unknown] -[88; 89) '1': {integer} +[88; 89) '1': i32 [96; 97) 'B': [unknown] [107; 108) 'a': A [114; 133) 'A { b:...C(1) }': A [121; 122) 'B': B [127; 128) 'C': [unknown] [127; 131) 'C(1)': C -[129; 130) '1': {integer} +[129; 130) '1': i32 [139; 140) 'a': A [139; 142) 'a.b': B [148; 149) 'a': A -- cgit v1.2.3 From 606d66a714bb8fe07f35a6af83d04ab576b9a0e1 Mon Sep 17 00:00:00 2001 From: Marcus Klaas de Vries Date: Fri, 11 Jan 2019 11:27:07 +0100 Subject: Start moving literal interpretation to the AST (WIP) --- crates/ra_hir/src/expr.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 52af2af45..3f2689781 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -649,10 +649,9 @@ impl ExprCollector { let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect(); self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr) } - ast::ExprKind::Literal(e) => { - let child = e.syntax().children().next(); - - if let Some(c) = child { + ast::ExprKind::LiteralExpr(e) => { + if let Some(child) = e.literal() { + let c = child.syntax(); let lit = match c.kind() { SyntaxKind::INT_NUMBER => { let text = c.text().to_string(); -- cgit v1.2.3 From a9a6a50c759302e8a8d59bf6c53c72ec804324b3 Mon Sep 17 00:00:00 2001 From: Marcus Klaas de Vries Date: Mon, 14 Jan 2019 19:30:21 +0100 Subject: Fixup tests --- crates/ra_hir/src/expr.rs | 141 ++++++++++---------------- crates/ra_hir/src/name.rs | 2 +- crates/ra_hir/src/ty.rs | 46 +++++---- crates/ra_hir/src/ty/tests.rs | 5 +- crates/ra_hir/src/ty/tests/data/binary_op.txt | 86 ++++++++-------- crates/ra_hir/src/ty/tests/data/literals.txt | 3 +- crates/ra_hir/src/ty/tests/data/tuple.txt | 18 ++-- 7 files changed, 135 insertions(+), 166 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 3f2689781..2f3432870 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -6,12 +6,11 @@ use rustc_hash::FxHashMap; use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; use ra_db::{LocalSyntaxPtr, Cancelable}; use ra_syntax::{ - SyntaxKind, - ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner} + ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner, LiteralFlavor} }; use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName}; -use crate::ty::primitive::{UintTy, IntTy, FloatTy, UncertainIntTy, UncertainFloatTy}; +use crate::ty::primitive::{UintTy, UncertainIntTy, UncertainFloatTy}; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ExprId(RawId); @@ -649,93 +648,59 @@ impl ExprCollector { let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect(); self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr) } - ast::ExprKind::LiteralExpr(e) => { - if let Some(child) = e.literal() { - let c = child.syntax(); - let lit = match c.kind() { - 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 => { - 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("f64") { - UncertainFloatTy::Known(FloatTy::F64) - } else if text.ends_with("f32") { - UncertainFloatTy::Known(FloatTy::F32) - } else { - UncertainFloatTy::Unknown - }; - - // TODO: actually parse value - Literal::Float(0, ty) - } - SyntaxKind::STRING => { - // FIXME: this likely includes the " characters - let text = c.text().to_string(); - Literal::String(text) - } - SyntaxKind::TRUE_KW => Literal::Bool(true), - SyntaxKind::FALSE_KW => Literal::Bool(false), - SyntaxKind::BYTE_STRING => { - // FIXME: this is completely incorrect for a variety - // of reasons, but at least it gives the right type - let bytes = c.text().to_string().into_bytes(); - Literal::ByteString(bytes) - } - SyntaxKind::CHAR => { - let character = c.text().char_at(1).unwrap_or('X'); - Literal::Char(character) + ast::ExprKind::Literal(e) => { + let child = if let Some(child) = e.literal_expr() { + child + } else { + return self.alloc_expr(Expr::Missing, syntax_ptr); + }; + let c = child.syntax(); + + let lit = match child.flavor() { + LiteralFlavor::IntNumber { suffix } => { + let known_name = suffix + .map(|s| Name::new(s)) + .and_then(|name| UncertainIntTy::from_name(&name)); + + if let Some(kn) = known_name { + Literal::Int(0u64, kn) + } else { + Literal::Int(0u64, UncertainIntTy::Unknown) } - SyntaxKind::BYTE => { - let character = c.text().char_at(1).unwrap_or('X'); - Literal::Int( - character as u8 as u64, - UncertainIntTy::Unsigned(UintTy::U8), - ) + } + LiteralFlavor::FloatNumber { suffix } => { + let known_name = suffix + .map(|s| Name::new(s)) + .and_then(|name| UncertainFloatTy::from_name(&name)); + + if let Some(kn) = known_name { + Literal::Float(0u64, kn) + } else { + Literal::Float(0u64, UncertainFloatTy::Unknown) } - _ => return self.alloc_expr(Expr::Missing, syntax_ptr), - }; - - self.alloc_expr(Expr::Literal(lit), syntax_ptr) - } else { - self.alloc_expr(Expr::Missing, syntax_ptr) - } + } + LiteralFlavor::ByteString => { + // FIXME: this is completely incorrect for a variety + // of reasons, but at least it gives the right type + let bytes = c.text().to_string().into_bytes(); + Literal::ByteString(bytes) + } + LiteralFlavor::String => { + // FIXME: this likely includes the " characters + let text = c.text().to_string(); + Literal::String(text) + } + LiteralFlavor::Byte => { + let character = c.text().char_at(1).unwrap_or('X'); + Literal::Int(character as u8 as u64, UncertainIntTy::Unsigned(UintTy::U8)) + } + LiteralFlavor::Bool => Literal::Bool(true), + LiteralFlavor::Char => { + let character = c.text().char_at(1).unwrap_or('X'); + Literal::Char(character) + } + }; + self.alloc_expr(Expr::Literal(lit), syntax_ptr) } // TODO implement HIR for these: diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index d9683549c..8d786d2ac 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs @@ -23,7 +23,7 @@ impl fmt::Debug for Name { } impl Name { - fn new(text: SmolStr) -> Name { + pub(crate) fn new(text: SmolStr) -> Name { Name { text } } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index de5ec5b46..b6577ee5e 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -111,7 +111,7 @@ impl UnifyValue for TypeVarValue { /// values for general types, and for integer and float variables. The latter /// two are used for inference of literal values (e.g. `100` could be one of /// several integer types). -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum InferTy { TypeVar(TypeVarId), IntVar(TypeVarId), @@ -119,6 +119,12 @@ pub enum InferTy { } impl InferTy { + fn to_inner(self) -> TypeVarId { + match self { + InferTy::TypeVar(ty) | InferTy::IntVar(ty) | InferTy::FloatVar(ty) => ty, + } + } + fn fallback_value(self) -> Ty { match self { InferTy::TypeVar(..) => Ty::Unknown, @@ -326,18 +332,19 @@ impl Ty { path: &Path, ) -> Cancelable { if let Some(name) = path.as_ident() { - if let Some(KnownName::Bool) = name.as_known_name() { - return Ok(Ty::Bool); - } else if let Some(KnownName::Char) = name.as_known_name() { - 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::UncertainIntTy::from_name(name) { + if let Some(int_ty) = primitive::UncertainIntTy::from_name(name) { return Ok(Ty::Int(int_ty)); } 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())); + } else if let Some(known) = name.as_known_name() { + match known { + KnownName::Bool => return Ok(Ty::Bool), + KnownName::Char => return Ok(Ty::Char), + KnownName::Str => return Ok(Ty::Str), + _ => {} + } } } @@ -793,10 +800,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { /// known type. fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty { ty.fold(&mut |ty| match ty { - Ty::Infer(InferTy::TypeVar(tv)) - | Ty::Infer(InferTy::IntVar(tv)) - | Ty::Infer(InferTy::FloatVar(tv)) => { - if let Some(known_ty) = self.var_unification_table.probe_value(tv).known() { + Ty::Infer(tv) => { + let inner = tv.to_inner(); + if let Some(known_ty) = self.var_unification_table.probe_value(inner).known() { // known_ty may contain other variables that are known by now self.resolve_ty_as_possible(known_ty.clone()) } else { @@ -811,8 +817,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { /// otherwise, return ty. fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { match ty { - Ty::Infer(InferTy::TypeVar(tv)) => { - match self.var_unification_table.probe_value(*tv).known() { + Ty::Infer(tv) => { + let inner = tv.to_inner(); + match self.var_unification_table.probe_value(inner).known() { Some(known_ty) => { // The known_ty can't be a type var itself Cow::Owned(known_ty.clone()) @@ -828,16 +835,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { /// replaced by Ty::Unknown. fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { ty.fold(&mut |ty| match ty { - Ty::Infer(i) => { - let tv = match i { - InferTy::TypeVar(tv) | InferTy::IntVar(tv) | InferTy::FloatVar(tv) => tv, - }; - - if let Some(known_ty) = self.var_unification_table.probe_value(tv).known() { + Ty::Infer(tv) => { + let inner = tv.to_inner(); + if let Some(known_ty) = self.var_unification_table.probe_value(inner).known() { // known_ty may contain other variables that are known by now self.resolve_ty_completely(known_ty.clone()) } else { - i.fallback_value() + tv.fallback_value() } } _ => ty, diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 53ea99874..cbdb2a4b7 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -145,6 +145,7 @@ fn test() { 3.14; 5000; false; + true; } "#, "literals.txt", @@ -199,7 +200,7 @@ fn f(x: bool) -> i32 { 0i32 } -fn test() { +fn test() -> bool { let x = a && b; let y = true || false; let z = x == y; @@ -296,8 +297,6 @@ fn test(x: &str, y: isize) { let b = (a, x); let c = (y, x); let d = (c, x); - - // we have not infered these case yet. let e = (1, "e"); let f = (e, "d"); } 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 7fdb8a900..58a727691 100644 --- a/crates/ra_hir/src/ty/tests/data/binary_op.txt +++ b/crates/ra_hir/src/ty/tests/data/binary_op.txt @@ -1,46 +1,46 @@ [6; 7) 'x': bool [22; 34) '{ 0i32 }': i32 [28; 32) '0i32': i32 -[46; 342) '{ ... < 3 }': bool -[56; 57) 'x': bool -[60; 61) 'a': bool -[60; 66) 'a && b': bool -[65; 66) 'b': bool -[76; 77) 'y': bool -[80; 84) 'true': bool -[80; 93) 'true || false': bool -[88; 93) 'false': bool -[103; 104) 'z': bool -[107; 108) 'x': bool -[107; 113) 'x == y': bool -[112; 113) 'y': bool -[123; 134) 'minus_forty': isize -[144; 152) '-40isize': isize -[145; 152) '40isize': isize -[162; 163) 'h': bool -[166; 177) 'minus_forty': isize -[166; 188) 'minus_...ONST_2': bool -[181; 188) 'CONST_2': isize -[198; 199) 'c': i32 -[202; 203) 'f': fn(bool) -> i32 -[202; 211) 'f(z || y)': i32 -[202; 215) 'f(z || y) + 5': i32 -[204; 205) 'z': bool -[204; 210) 'z || y': bool -[209; 210) 'y': bool -[214; 215) '5': i32 -[225; 226) 'd': [unknown] -[229; 230) 'b': [unknown] -[240; 241) 'g': () -[244; 255) 'minus_forty': isize -[244; 260) 'minus_...y ^= i': () -[259; 260) 'i': isize -[270; 273) 'ten': usize -[283; 285) '10': usize -[295; 308) 'ten_is_eleven': bool -[311; 314) 'ten': usize -[311; 326) 'ten == some_num': bool -[318; 326) 'some_num': usize -[333; 336) 'ten': usize -[333; 340) 'ten < 3': bool -[339; 340) '3': usize +[54; 350) '{ ... < 3 }': bool +[64; 65) 'x': bool +[68; 69) 'a': bool +[68; 74) 'a && b': bool +[73; 74) 'b': bool +[84; 85) 'y': bool +[88; 92) 'true': bool +[88; 101) 'true || false': bool +[96; 101) 'false': bool +[111; 112) 'z': bool +[115; 116) 'x': bool +[115; 121) 'x == y': bool +[120; 121) 'y': bool +[131; 142) 'minus_forty': isize +[152; 160) '-40isize': isize +[153; 160) '40isize': isize +[170; 171) 'h': bool +[174; 185) 'minus_forty': isize +[174; 196) 'minus_...ONST_2': bool +[189; 196) 'CONST_2': isize +[206; 207) 'c': i32 +[210; 211) 'f': fn(bool) -> i32 +[210; 219) 'f(z || y)': i32 +[210; 223) 'f(z || y) + 5': i32 +[212; 213) 'z': bool +[212; 218) 'z || y': bool +[217; 218) 'y': bool +[222; 223) '5': i32 +[233; 234) 'd': [unknown] +[237; 238) 'b': [unknown] +[248; 249) 'g': () +[252; 263) 'minus_forty': isize +[252; 268) 'minus_...y ^= i': () +[267; 268) 'i': isize +[278; 281) 'ten': usize +[291; 293) '10': usize +[303; 316) 'ten_is_eleven': bool +[319; 322) 'ten': usize +[319; 334) 'ten == some_num': bool +[326; 334) 'some_num': usize +[341; 344) 'ten': usize +[341; 348) 'ten < 3': bool +[347; 348) '3': usize diff --git a/crates/ra_hir/src/ty/tests/data/literals.txt b/crates/ra_hir/src/ty/tests/data/literals.txt index 8d6079c98..6e82f458f 100644 --- a/crates/ra_hir/src/ty/tests/data/literals.txt +++ b/crates/ra_hir/src/ty/tests/data/literals.txt @@ -1,4 +1,4 @@ -[11; 101) '{ ...lse; }': () +[11; 111) '{ ...rue; }': () [17; 21) '5i32': i32 [27; 34) '"hello"': &str [40; 48) 'b"bytes"': &[u8] @@ -7,3 +7,4 @@ [73; 77) '3.14': f64 [83; 87) '5000': i32 [93; 98) 'false': bool +[104; 108) 'true': bool diff --git a/crates/ra_hir/src/ty/tests/data/tuple.txt b/crates/ra_hir/src/ty/tests/data/tuple.txt index 96169180d..a95d3c286 100644 --- a/crates/ra_hir/src/ty/tests/data/tuple.txt +++ b/crates/ra_hir/src/ty/tests/data/tuple.txt @@ -1,6 +1,6 @@ [9; 10) 'x': &str [18; 19) 'y': isize -[28; 214) '{ ...d"); }': () +[28; 170) '{ ...d"); }': () [38; 39) 'a': (u32, &str) [55; 63) '(1, "a")': (u32, &str) [56; 57) '1': u32 @@ -17,11 +17,11 @@ [117; 123) '(c, x)': ((isize, &str), &str) [118; 119) 'c': (isize, &str) [121; 122) 'x': &str -[177; 178) 'e': ([unknown], [unknown]) -[181; 189) '(1, "e")': ([unknown], [unknown]) -[182; 183) '1': [unknown] -[185; 188) '"e"': [unknown] -[199; 200) 'f': (([unknown], [unknown]), [unknown]) -[203; 211) '(e, "d")': (([unknown], [unknown]), [unknown]) -[204; 205) 'e': ([unknown], [unknown]) -[207; 210) '"d"': [unknown] +[133; 134) 'e': (i32, &str) +[137; 145) '(1, "e")': (i32, &str) +[138; 139) '1': i32 +[141; 144) '"e"': &str +[155; 156) 'f': ((i32, &str), &str) +[159; 167) '(e, "d")': ((i32, &str), &str) +[160; 161) 'e': (i32, &str) +[163; 166) '"d"': &str -- cgit v1.2.3 From 26893487722d07b3f31a6addfc88e6948620989c Mon Sep 17 00:00:00 2001 From: Marcus Klaas de Vries Date: Mon, 14 Jan 2019 19:46:10 +0100 Subject: Give literal expression default values for now --- crates/ra_hir/src/expr.rs | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 2f3432870..2696592a4 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -654,7 +654,6 @@ impl ExprCollector { } else { return self.alloc_expr(Expr::Missing, syntax_ptr); }; - let c = child.syntax(); let lit = match child.flavor() { LiteralFlavor::IntNumber { suffix } => { @@ -663,9 +662,9 @@ impl ExprCollector { .and_then(|name| UncertainIntTy::from_name(&name)); if let Some(kn) = known_name { - Literal::Int(0u64, kn) + Literal::Int(Default::default(), kn) } else { - Literal::Int(0u64, UncertainIntTy::Unknown) + Literal::Int(Default::default(), UncertainIntTy::Unknown) } } LiteralFlavor::FloatNumber { suffix } => { @@ -674,31 +673,18 @@ impl ExprCollector { .and_then(|name| UncertainFloatTy::from_name(&name)); if let Some(kn) = known_name { - Literal::Float(0u64, kn) + Literal::Float(Default::default(), kn) } else { - Literal::Float(0u64, UncertainFloatTy::Unknown) + Literal::Float(Default::default(), UncertainFloatTy::Unknown) } } - LiteralFlavor::ByteString => { - // FIXME: this is completely incorrect for a variety - // of reasons, but at least it gives the right type - let bytes = c.text().to_string().into_bytes(); - Literal::ByteString(bytes) - } - LiteralFlavor::String => { - // FIXME: this likely includes the " characters - let text = c.text().to_string(); - Literal::String(text) - } + LiteralFlavor::ByteString => Literal::ByteString(Default::default()), + LiteralFlavor::String => Literal::String(Default::default()), LiteralFlavor::Byte => { - let character = c.text().char_at(1).unwrap_or('X'); - Literal::Int(character as u8 as u64, UncertainIntTy::Unsigned(UintTy::U8)) - } - LiteralFlavor::Bool => Literal::Bool(true), - LiteralFlavor::Char => { - let character = c.text().char_at(1).unwrap_or('X'); - Literal::Char(character) + Literal::Int(Default::default(), UncertainIntTy::Unsigned(UintTy::U8)) } + LiteralFlavor::Bool => Literal::Bool(Default::default()), + LiteralFlavor::Char => Literal::Char(Default::default()), }; self.alloc_expr(Expr::Literal(lit), syntax_ptr) } -- cgit v1.2.3 From d67eabb512a08a451a649c7f20e4e9ae1860a8a0 Mon Sep 17 00:00:00 2001 From: Marcus Klaas de Vries Date: Mon, 14 Jan 2019 20:56:14 +0100 Subject: Fix type inference for raw (byte) strings --- crates/ra_hir/src/ty/tests.rs | 12 +++++++++--- crates/ra_hir/src/ty/tests/data/literals.txt | 4 +++- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index cbdb2a4b7..8aacb1a7f 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -135,7 +135,7 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { #[test] fn infer_literals() { check_inference( - r#" + r##" fn test() { 5i32; "hello"; @@ -146,8 +146,14 @@ fn test() { 5000; false; true; -} -"#, + r#" + //! doc + // non-doc + mod foo {} + "#; + br#"yolo"#; +} +"##, "literals.txt", ); } diff --git a/crates/ra_hir/src/ty/tests/data/literals.txt b/crates/ra_hir/src/ty/tests/data/literals.txt index 6e82f458f..84ee2c11b 100644 --- a/crates/ra_hir/src/ty/tests/data/literals.txt +++ b/crates/ra_hir/src/ty/tests/data/literals.txt @@ -1,4 +1,4 @@ -[11; 111) '{ ...rue; }': () +[11; 201) '{ ...o"#; }': () [17; 21) '5i32': i32 [27; 34) '"hello"': &str [40; 48) 'b"bytes"': &[u8] @@ -8,3 +8,5 @@ [83; 87) '5000': i32 [93; 98) 'false': bool [104; 108) 'true': bool +[114; 182) 'r#" ... "#': &str +[188; 198) 'br#"yolo"#': &[u8] -- cgit v1.2.3 From 37ba237e6686d94783d1f025d23823ad7c0cb0c8 Mon Sep 17 00:00:00 2001 From: Marcus Klaas de Vries Date: Mon, 14 Jan 2019 21:52:08 +0100 Subject: Address issues found in review --- crates/ra_hir/src/expr.rs | 18 ++++++++---------- crates/ra_hir/src/ty.rs | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 2696592a4..5081466a2 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -661,22 +661,20 @@ impl ExprCollector { .map(|s| Name::new(s)) .and_then(|name| UncertainIntTy::from_name(&name)); - if let Some(kn) = known_name { - Literal::Int(Default::default(), kn) - } else { - Literal::Int(Default::default(), UncertainIntTy::Unknown) - } + Literal::Int( + Default::default(), + known_name.unwrap_or(UncertainIntTy::Unknown), + ) } LiteralFlavor::FloatNumber { suffix } => { let known_name = suffix .map(|s| Name::new(s)) .and_then(|name| UncertainFloatTy::from_name(&name)); - if let Some(kn) = known_name { - Literal::Float(Default::default(), kn) - } else { - Literal::Float(Default::default(), UncertainFloatTy::Unknown) - } + Literal::Float( + Default::default(), + known_name.unwrap_or(UncertainFloatTy::Unknown), + ) } LiteralFlavor::ByteString => Literal::ByteString(Default::default()), LiteralFlavor::String => Literal::String(Default::default()), diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index b6577ee5e..5579db8fb 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -107,7 +107,7 @@ impl UnifyValue for TypeVarValue { } } -/// The kinds of placeholders we need during type inference. There's seperate +/// The kinds of placeholders we need during type inference. There's separate /// values for general types, and for integer and float variables. The latter /// two are used for inference of literal values (e.g. `100` could be one of /// several integer types). -- cgit v1.2.3