Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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),
Comment thread
wackbyte marked this conversation as resolved.
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 {
Comment thread
wackbyte marked this conversation as resolved.
UnaryOp::Neg => {
Comment thread
wackbyte marked this conversation as resolved.
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()),
Comment thread
wackbyte marked this conversation as resolved.
//_ => 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