Implement type inference for boolean operators

This commit is contained in:
Marcus Klaas de Vries 2019-01-05 21:28:30 +01:00
parent 3e42a15878
commit 4fc233a02e
6 changed files with 92 additions and 4 deletions

View File

@ -26,7 +26,7 @@ use ena::unify::{InPlaceUnificationTable, UnifyKey, UnifyValue, NoError};
use ra_db::{LocalSyntaxPtr, Cancelable};
use ra_syntax::{
ast::{self, AstNode, LoopBodyOwner, ArgListOwner, PrefixOp},
ast::{self, AstNode, LoopBodyOwner, ArgListOwner, PrefixOp, BinOp},
SyntaxNodeRef
};
@ -906,7 +906,16 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
}
ast::Expr::RangeExpr(_e) => Ty::Unknown,
ast::Expr::BinExpr(_e) => Ty::Unknown,
ast::Expr::BinExpr(e) => match e.op() {
Some(BinOp::BooleanOr)
| Some(BinOp::BooleanAnd)
| Some(BinOp::EqualityTest)
| Some(BinOp::LesserEqualTest)
| Some(BinOp::GreaterEqualTest)
| Some(BinOp::LesserTest)
| Some(BinOp::GreaterTest) => Ty::Bool,
_ => Ty::Unknown,
},
ast::Expr::Literal(_e) => Ty::Unknown,
};
// use a new type variable if we got Ty::Unknown here

View File

@ -153,6 +153,23 @@ impl S {
);
}
#[test]
fn infer_boolean_op() {
check_inference(
r#"
fn test() {
let x = a && b;
let y = true || false;
let z = x == y;
let h = CONST_1 <= CONST_2;
10 < 3
}
"#,
"0008_boolean_op.txt",
);
}
fn infer(content: &str) -> String {
let (db, _, file_id) = MockDatabase::with_single_file(content);
let source_file = db.source_file(file_id);

View File

@ -0,0 +1,10 @@
[21; 22) 'x': bool
[68; 69) 'z': bool
[72; 78) 'x == y': bool
[45; 58) 'true || false': bool
[11; 125) '{ ... < 3 }': bool
[117; 123) '10 < 3': bool
[88; 89) 'h': bool
[41; 42) 'y': bool
[92; 110) 'CONST_...ONST_2': bool
[25; 31) 'a && b': bool

View File

@ -488,6 +488,45 @@ impl<'a> PrefixExpr<'a> {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum BinOp {
/// The `||` operator for boolean OR
BooleanOr,
/// The `&&` operator for boolean AND
BooleanAnd,
/// The `==` operator for equality testing
EqualityTest,
/// The `<=` operator for lesser-equal testing
LesserEqualTest,
/// The `>=` operator for greater-equal testing
GreaterEqualTest,
/// The `<` operator for comparison
LesserTest,
/// The `>` operator for comparison
GreaterTest,
// TODO: lots of others
}
impl<'a> BinExpr<'a> {
pub fn op(&self) -> Option<BinOp> {
self.syntax()
.children()
.filter_map(|c| {
match c.kind() {
PIPEPIPE => Some(BinOp::BooleanOr),
AMPAMP => Some(BinOp::BooleanAnd),
EQEQ => Some(BinOp::EqualityTest),
LTEQ => Some(BinOp::LesserEqualTest),
GTEQ => Some(BinOp::GreaterEqualTest),
L_ANGLE => Some(BinOp::LesserTest),
R_ANGLE => Some(BinOp::GreaterTest),
_ => None,
}
})
.next()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SelfParamFlavor {
/// self

View File

@ -217,7 +217,15 @@ impl<R: TreeRoot<RaTypes>> BinExprNode<R> {
}
impl<'a> BinExpr<'a> {}
impl<'a> BinExpr<'a> {
pub fn lhs(self) -> Option<Expr<'a>> {
super::child_opt(self)
}
pub fn rhs(self) -> Option<Expr<'a>> {
super::child_opt(self)
}
}
// BindPat
#[derive(Debug, Clone, Copy,)]

View File

@ -422,7 +422,12 @@ Grammar(
"RefExpr": (options: ["Expr"]),
"PrefixExpr": (options: ["Expr"]),
"RangeExpr": (),
"BinExpr": (),
"BinExpr": (
options: [
["lhs", "Expr"],
["rhs", "Expr"]
]
),
"String": (),
"Byte": (),
"ByteString": (),