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 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) (limited to 'crates/ra_hir/src/expr.rs') 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), } } -- 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 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 13 deletions(-) (limited to 'crates/ra_hir/src/expr.rs') 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), }; -- 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 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'crates/ra_hir/src/expr.rs') 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(); -- 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 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) (limited to 'crates/ra_hir/src/expr.rs') 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 => { -- 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/src/expr.rs') 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 +++++++++++++++++----------------------------- 1 file changed, 53 insertions(+), 88 deletions(-) (limited to 'crates/ra_hir/src/expr.rs') 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: -- 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/src/expr.rs') 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 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 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'crates/ra_hir/src/expr.rs') 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()), -- cgit v1.2.3