From 518f1d19d80a1ad1526054029cef7bcba7b3ff3a Mon Sep 17 00:00:00 2001 From: Bas Zalmstra Date: Sun, 29 Dec 2019 16:22:57 +0100 Subject: [PATCH] feat: add simple binary operation type checking --- crates/mun_hir/src/expr.rs | 4 +-- crates/mun_hir/src/ty/infer.rs | 10 +++++- crates/mun_hir/src/ty/op.rs | 36 ++++++++++++++++--- .../snapshots/tests__invalid_binary_ops.snap | 13 +++++++ crates/mun_hir/src/ty/tests.rs | 13 +++++++ 5 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 crates/mun_hir/src/ty/snapshots/tests__invalid_binary_ops.snap diff --git a/crates/mun_hir/src/expr.rs b/crates/mun_hir/src/expr.rs index 651665ecf..1dcaedd8d 100644 --- a/crates/mun_hir/src/expr.rs +++ b/crates/mun_hir/src/expr.rs @@ -326,9 +326,7 @@ pub enum Pat { } impl Pat { - pub fn walk_child_pats(&self, mut _f: impl FnMut(PatId)) { - unreachable!() - } + pub fn walk_child_pats(&self, mut _f: impl FnMut(PatId)) {} } // Queries diff --git a/crates/mun_hir/src/ty/infer.rs b/crates/mun_hir/src/ty/infer.rs index 0f0c8caec..06777a8a9 100644 --- a/crates/mun_hir/src/ty/infer.rs +++ b/crates/mun_hir/src/ty/infer.rs @@ -296,7 +296,15 @@ impl<'a, D: HirDatabase> InferenceResultBuilder<'a, D> { }) } }; - let rhs_expected = op::binary_op_rhs_expectation(*op, lhs_ty); + let rhs_expected = op::binary_op_rhs_expectation(*op, lhs_ty.clone()); + if lhs_ty != Ty::Unknown && rhs_expected == Ty::Unknown { + self.diagnostics + .push(InferenceDiagnostic::CannotApplyBinaryOp { + id: tgt_expr, + lhs: lhs_ty, + rhs: rhs_expected.clone(), + }) + } let rhs_ty = self.infer_expr(*rhs, &Expectation::has_type(rhs_expected)); op::binary_op_return_ty(*op, rhs_ty) } diff --git a/crates/mun_hir/src/ty/op.rs b/crates/mun_hir/src/ty/op.rs index 1b0aa20b7..c7daef9aa 100644 --- a/crates/mun_hir/src/ty/op.rs +++ b/crates/mun_hir/src/ty/op.rs @@ -1,12 +1,40 @@ -use crate::{BinaryOp, Ty, TypeCtor}; +use crate::{ApplicationTy, BinaryOp, CmpOp, Ty, TypeCtor}; -pub(super) fn binary_op_rhs_expectation(_op: BinaryOp, lhs_ty: Ty) -> Ty { - lhs_ty +/// Given a binary operation and the type on the left of that operation, returns the expected type +/// for the right hand side of the operation or `Ty::Unknown` if such an operation is invalid. +pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { + match op { + BinaryOp::LogicOp(..) => Ty::simple(TypeCtor::Bool), + BinaryOp::Assignment { op: None } | BinaryOp::CmpOp(CmpOp::Eq { .. }) => match lhs_ty { + Ty::Apply(ApplicationTy { ctor, .. }) => match ctor { + TypeCtor::Int | TypeCtor::Float | TypeCtor::Bool => lhs_ty, + _ => Ty::Unknown, + }, + _ => Ty::Unknown, + }, + BinaryOp::CmpOp(CmpOp::Ord { .. }) + | BinaryOp::Assignment { op: Some(_) } + | BinaryOp::ArithOp(_) => match lhs_ty { + Ty::Apply(ApplicationTy { ctor, .. }) => match ctor { + TypeCtor::Int | TypeCtor::Float => lhs_ty, + _ => Ty::Unknown, + }, + _ => Ty::Unknown, + }, + } } +/// For a binary operation with the specified type on the right hand side of the operation, return +/// the return type of that operation. pub(super) fn binary_op_return_ty(op: BinaryOp, rhs_ty: Ty) -> Ty { match op { - BinaryOp::ArithOp(_) => rhs_ty, + BinaryOp::ArithOp(_) => match rhs_ty { + Ty::Apply(ApplicationTy { ctor, .. }) => match ctor { + TypeCtor::Int | TypeCtor::Float => rhs_ty, + _ => Ty::Unknown, + }, + _ => Ty::Unknown, + }, BinaryOp::CmpOp(_) | BinaryOp::LogicOp(_) => Ty::simple(TypeCtor::Bool), BinaryOp::Assignment { .. } => Ty::Empty, } diff --git a/crates/mun_hir/src/ty/snapshots/tests__invalid_binary_ops.snap b/crates/mun_hir/src/ty/snapshots/tests__invalid_binary_ops.snap new file mode 100644 index 000000000..56c94066d --- /dev/null +++ b/crates/mun_hir/src/ty/snapshots/tests__invalid_binary_ops.snap @@ -0,0 +1,13 @@ +--- +source: crates/mun_hir/src/ty/tests.rs +expression: "fn foo() {\n let b = false;\n let n = 1;\n let _ = b + n; // error: invalid binary operation\n}" +--- +[57; 62): cannot apply binary operator +[9; 100) '{ ...tion }': nothing +[19; 20) 'b': bool +[23; 28) 'false': bool +[38; 39) 'n': int +[42; 43) '1': int +[57; 58) 'b': bool +[57; 62) 'b + n': int +[61; 62) 'n': int diff --git a/crates/mun_hir/src/ty/tests.rs b/crates/mun_hir/src/ty/tests.rs index 439f22907..df6ff1693 100644 --- a/crates/mun_hir/src/ty/tests.rs +++ b/crates/mun_hir/src/ty/tests.rs @@ -159,6 +159,19 @@ fn infer_while() { ) } +#[test] +fn invalid_binary_ops() { + infer_snapshot( + r#" + fn foo() { + let b = false; + let n = 1; + let _ = b + n; // error: invalid binary operation + } + "#, + ) +} + fn infer_snapshot(text: &str) { let text = text.trim().replace("\n ", "\n"); insta::assert_snapshot!(insta::_macro_support::AutoName, infer(&text), &text);