Implement type inference for literals (WIP)

This commit is contained in:
Marcus Klaas de Vries 2019-01-10 13:54:58 +01:00
parent 8caff4e034
commit a6146d35b1
8 changed files with 166 additions and 5 deletions

View file

@ -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<u8>),
Char(char),
Bool(bool),
Byte(u8),
Int, // this and float need additional information
Float,
Tuple { values: Vec<ExprId> },
Array { values: Vec<ExprId> },
}
#[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<ExprId>,
},
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),
}
}

View file

@ -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<T>(x: Cancelable<Option<T>>) -> Option<Cancelable<T>> {
@ -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);

View file

@ -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(

View file

@ -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]

View file

@ -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]': ()