Skip to content

Commit

Permalink
Merge pull request #136 from jakbyte/unary-ops
Browse files Browse the repository at this point in the history
feat: unary operators
  • Loading branch information
baszalmstra authored Apr 19, 2020
2 parents c17448d + cc6ac75 commit 134bc5a
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 9 deletions.
65 changes: 64 additions & 1 deletion crates/mun_codegen/src/ir/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
};
use hir::{
ArenaId, ArithOp, BinaryOp, Body, CmpOp, Expr, ExprId, HirDisplay, InferenceResult, Literal,
Name, Ordering, Pat, PatId, Path, Resolution, Resolver, Statement, TypeCtor,
Name, Ordering, Pat, PatId, Path, Resolution, Resolver, Statement, TypeCtor, UnaryOp,
};
use inkwell::{
builder::Builder,
Expand Down Expand Up @@ -206,6 +206,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
Expr::BinaryOp { lhs, rhs, op } => {
self.gen_binary_op(expr, *lhs, *rhs, op.expect("missing op"))
}
Expr::UnaryOp { expr, op } => self.gen_unary_op(*expr, *op),
Expr::Call {
ref callee,
ref args,
Expand Down Expand Up @@ -624,6 +625,68 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
}
}

/// Generates IR to calculate a unary operation on an expression.
fn gen_unary_op(&mut self, expr: ExprId, op: UnaryOp) -> Option<BasicValueEnum> {
let ty = self.infer[expr].clone();
match ty.as_simple() {
Some(TypeCtor::Float(_ty)) => self.gen_unary_op_float(expr, op),
Some(TypeCtor::Int(ty)) => self.gen_unary_op_int(expr, op, ty.signedness),
Some(TypeCtor::Bool) => self.gen_unary_op_bool(expr, op),
_ => unimplemented!("unimplemented operation op{0}", ty.display(self.db)),
}
}

/// Generates IR to calculate a unary operation on a floating point value.
fn gen_unary_op_float(&mut self, expr: ExprId, op: UnaryOp) -> Option<BasicValueEnum> {
let value: FloatValue = self
.gen_expr(expr)
.map(|value| self.opt_deref_value(self.infer[expr].clone(), value))
.expect("no value")
.into_float_value();
match op {
UnaryOp::Neg => Some(self.builder.build_float_neg(value, "neg").into()),
_ => unimplemented!("Operator {:?} is not implemented for float", op),
}
}

/// Generates IR to calculate a unary operation on an integer value.
fn gen_unary_op_int(
&mut self,
expr: ExprId,
op: UnaryOp,
signedness: hir::Signedness,
) -> Option<BasicValueEnum> {
let value: IntValue = self
.gen_expr(expr)
.map(|value| self.opt_deref_value(self.infer[expr].clone(), value))
.expect("no value")
.into_int_value();
match op {
UnaryOp::Neg => {
if signedness == hir::Signedness::Signed {
Some(self.builder.build_int_neg(value, "neg").into())
} else {
unimplemented!("Operator {:?} is not implemented for unsigned integer", op)
}
}
UnaryOp::Not => Some(self.builder.build_not(value, "not").into()),
//_ => unimplemented!("Operator {:?} is not implemented for integer", op),
}
}

/// Generates IR to calculate a unary operation on a boolean value.
fn gen_unary_op_bool(&mut self, expr: ExprId, op: UnaryOp) -> Option<BasicValueEnum> {
let value: IntValue = self
.gen_expr(expr)
.map(|value| self.opt_deref_value(self.infer[expr].clone(), value))
.expect("no value")
.into_int_value();
match op {
UnaryOp::Not => Some(self.builder.build_not(value, "not").into()),
_ => unimplemented!("Operator {:?} is not implemented for boolean", op),
}
}

/// Generates IR to calculate a binary operation between two floating point values.
fn gen_binary_op_float(
&mut self,
Expand Down
51 changes: 51 additions & 0 deletions crates/mun_codegen/src/snapshots/test__unary_expressions.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
source: crates/mun_codegen/src/test.rs
expression: "pub fn negf(x: float) -> float {\n -x\n}\n\npub fn negi(x: int) -> int {\n -x\n}\n\npub fn notb(x: bool) -> bool {\n !x\n}\n\npub fn noti(x: int) -> int {\n !x\n}"
---
; == FILE IR =====================================
; ModuleID = 'main.mun'
source_filename = "main.mun"

%struct.MunTypeInfo = type { [16 x i8], i8 addrspace(4)*, i32, i8, i8 }

@global_type_table = external global [3 x %struct.MunTypeInfo addrspace(4)*]

define double @negf(double) {
body:
%neg = fsub double -0.000000e+00, %0
ret double %neg
}

define i64 @negi(i64) {
body:
%neg = sub i64 0, %0
ret i64 %neg
}

define i1 @notb(i1) {
body:
%not = xor i1 %0, true
ret i1 %not
}

define i64 @noti(i64) {
body:
%not = xor i64 %0, -1
ret i64 %not
}


; == GROUP IR ====================================
; ModuleID = 'group_name'
source_filename = "group_name"

%struct.MunTypeInfo = type { [16 x i8], i8 addrspace(4)*, i32, i8, i8 }

@"type_info::<core::i64>::name" = private unnamed_addr constant [10 x i8] c"core::i64\00"
@"type_info::<core::i64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", [10 x i8]* @"type_info::<core::i64>::name", i32 64, i8 8, i8 0 }
@"type_info::<core::f64>::name" = private unnamed_addr constant [10 x i8] c"core::f64\00"
@"type_info::<core::f64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", [10 x i8]* @"type_info::<core::f64>::name", i32 64, i8 8, i8 0 }
@"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", [11 x i8]* @"type_info::<core::bool>::name", i32 1, i8 1, i8 0 }
@global_type_table = global [3 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::<core::i64>", %struct.MunTypeInfo addrspace(4)* @"type_info::<core::f64>", %struct.MunTypeInfo addrspace(4)* @"type_info::<core::bool>"]

23 changes: 23 additions & 0 deletions crates/mun_codegen/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,29 @@ fn binary_expressions() {
);
}

#[test]
fn unary_expressions() {
test_snapshot(
r#"
pub fn negf(x: float) -> float {
-x
}
pub fn negi(x: int) -> int {
-x
}
pub fn notb(x: bool) -> bool {
!x
}
pub fn noti(x: int) -> int {
!x
}
"#,
);
}

#[test]
fn let_statement() {
test_snapshot(
Expand Down
21 changes: 21 additions & 0 deletions crates/mun_hir/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,27 @@ impl Diagnostic for CannotApplyBinaryOp {
}
}

#[derive(Debug)]
pub struct CannotApplyUnaryOp {
pub file: FileId,
pub expr: SyntaxNodePtr,
pub ty: Ty,
}

impl Diagnostic for CannotApplyUnaryOp {
fn message(&self) -> String {
"cannot apply unary operator".to_string()
}

fn source(&self) -> InFile<SyntaxNodePtr> {
InFile::new(self.file, self.expr)
}

fn as_any(&self) -> &(dyn Any + Send + 'static) {
self
}
}

#[derive(Debug)]
pub struct DuplicateDefinition {
pub file: FileId,
Expand Down
2 changes: 1 addition & 1 deletion crates/mun_hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub use crate::{
display::HirDisplay,
expr::{
resolver_for_expr, ArithOp, BinaryOp, Body, CmpOp, Expr, ExprId, ExprScopes, Literal,
LogicOp, Ordering, Pat, PatId, RecordLitField, Statement,
LogicOp, Ordering, Pat, PatId, RecordLitField, Statement, UnaryOp,
},
ids::ItemLoc,
input::{FileId, SourceRoot, SourceRootId},
Expand Down
59 changes: 52 additions & 7 deletions crates/mun_hir/src/ty/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
code_model::{DefWithBody, DefWithStruct, Struct},
diagnostics::DiagnosticSink,
expr,
expr::{Body, Expr, ExprId, Literal, Pat, PatId, RecordLitField, Statement},
expr::{Body, Expr, ExprId, Literal, Pat, PatId, RecordLitField, Statement, UnaryOp},
name_resolution::Namespace,
resolve::{Resolution, Resolver},
ty::infer::diagnostics::InferenceDiagnostic,
Expand Down Expand Up @@ -423,9 +423,38 @@ impl<'a, D: HirDatabase> InferenceResultBuilder<'a, D> {
}
}
}
Expr::UnaryOp { .. } => Ty::Unknown,
// Expr::UnaryOp { expr: _, op: _ } => {}
// Expr::Block { statements: _, tail: _ } => {}
Expr::UnaryOp { expr, op } => {
let ty =
self.infer_expr_inner(*expr, &Expectation::none(), &CheckParams::default());
if let Some(simple) = ty.as_simple() {
match op {
UnaryOp::Not => {
match simple {
TypeCtor::Bool | TypeCtor::Int(_) => ty,
_ => {
self.diagnostics.push(
InferenceDiagnostic::CannotApplyUnaryOp { id: *expr, ty },
);
Ty::Unknown
}
}
}
UnaryOp::Neg => {
match simple {
TypeCtor::Float(_) | TypeCtor::Int(_) => ty,
_ => {
self.diagnostics.push(
InferenceDiagnostic::CannotApplyUnaryOp { id: *expr, ty },
);
Ty::Unknown
}
}
}
}
} else {
Ty::Unknown
}
} // Expr::Block { statements: _, tail: _ } => {}
};

self.set_expr_type(tgt_expr, ty.clone());
Expand Down Expand Up @@ -923,9 +952,9 @@ impl From<PatId> for ExprOrPatId {
mod diagnostics {
use crate::diagnostics::{
AccessUnknownField, BreakOutsideLoop, BreakWithValueOutsideLoop, CannotApplyBinaryOp,
ExpectedFunction, FieldCountMismatch, IncompatibleBranch, InvalidLHS, LiteralOutOfRange,
MismatchedStructLit, MismatchedType, MissingElseBranch, MissingFields, NoFields,
NoSuchField, ParameterCountMismatch, ReturnMissingExpression,
CannotApplyUnaryOp, ExpectedFunction, FieldCountMismatch, IncompatibleBranch, InvalidLHS,
LiteralOutOfRange, MismatchedStructLit, MismatchedType, MissingElseBranch, MissingFields,
NoFields, NoSuchField, ParameterCountMismatch, ReturnMissingExpression,
};
use crate::{
adt::StructKind,
Expand Down Expand Up @@ -972,6 +1001,10 @@ mod diagnostics {
lhs: Ty,
rhs: Ty,
},
CannotApplyUnaryOp {
id: ExprId,
ty: Ty,
},
InvalidLHS {
id: ExprId,
lhs: ExprId,
Expand Down Expand Up @@ -1134,6 +1167,18 @@ mod diagnostics {
rhs: rhs.clone(),
});
}
InferenceDiagnostic::CannotApplyUnaryOp { id, ty } => {
let expr = body
.expr_syntax(*id)
.unwrap()
.value
.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
sink.push(CannotApplyUnaryOp {
file,
expr,
ty: ty.clone(),
});
}
InferenceDiagnostic::InvalidLHS { id, lhs } => {
let id = body
.expr_syntax(*id)
Expand Down
15 changes: 15 additions & 0 deletions crates/mun_hir/src/ty/snapshots/tests__infer_unary_ops.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
source: crates/mun_hir/src/ty/tests.rs
expression: "fn foo(a: int, b: bool) {\n a = -a;\n b = !b;\n}"
---
[7; 8) 'a': int
[15; 16) 'b': bool
[24; 51) '{ ... !b; }': nothing
[30; 31) 'a': int
[30; 36) 'a = -a': nothing
[34; 36) '-a': int
[35; 36) 'a': int
[42; 43) 'b': bool
[42; 48) 'b = !b': nothing
[46; 48) '!b': bool
[47; 48) 'b': bool
19 changes: 19 additions & 0 deletions crates/mun_hir/src/ty/snapshots/tests__invalid_unary_ops.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
source: crates/mun_hir/src/ty/tests.rs
expression: "fn bar(a: float, b: bool) {\n a = !a; // mismatched type\n b = -b; // mismatched type\n}"
---
[37; 38): cannot apply unary operator
[36; 38): mismatched type
[68; 69): cannot apply unary operator
[67; 69): mismatched type
[7; 8) 'a': float
[17; 18) 'b': bool
[26; 91) '{ ...type }': nothing
[32; 33) 'a': float
[32; 38) 'a = !a': nothing
[36; 38) '!a': {unknown}
[37; 38) 'a': float
[63; 64) 'b': bool
[63; 69) 'b = -b': nothing
[67; 69) '-b': {unknown}
[68; 69) 'b': bool
24 changes: 24 additions & 0 deletions crates/mun_hir/src/ty/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,30 @@ fn update_operators() {
)
}

#[test]
fn infer_unary_ops() {
infer_snapshot(
r#"
fn foo(a: int, b: bool) {
a = -a;
b = !b;
}
"#,
)
}

#[test]
fn invalid_unary_ops() {
infer_snapshot(
r#"
fn bar(a: float, b: bool) {
a = !a; // mismatched type
b = -b; // mismatched type
}
"#,
)
}

#[test]
fn infer_loop() {
infer_snapshot(
Expand Down

0 comments on commit 134bc5a

Please sign in to comment.