Skip to content

Commit

Permalink
Merge pull request #744 from candy-lang/add-tests
Browse files Browse the repository at this point in the history
Add tests to Core package
  • Loading branch information
JonasWanke authored Oct 26, 2023
2 parents de2cc9e + 400deca commit 8de67ee
Show file tree
Hide file tree
Showing 20 changed files with 556 additions and 41 deletions.
1 change: 1 addition & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"rust-lang.rust-analyzer",
"serayuzgur.crates",
"tamasfe.even-better-toml",
"usernamehw.errorlens",
"vadimcn.vscode-lldb"
]
}
2 changes: 1 addition & 1 deletion compiler/backend_inkwell/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ impl<'ctx> CodeGen<'ctx> {
let i64_type = self.context.i64_type();
let v = i64_type.const_int(
value
.clamp(&i64::MIN.into(), &i64::MAX.into())
.clamp(&u64::MIN.into(), &u64::MAX.into())
.try_into()
.unwrap(),
false,
Expand Down
63 changes: 58 additions & 5 deletions compiler/frontend/src/mir_optimize/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::pure::PurenessInsights;
use crate::mir::{Body, Expression, Id, VisibleExpressions};
use itertools::Itertools;
use rustc_hash::FxHashSet;
use std::mem;

Expand Down Expand Up @@ -185,15 +186,67 @@ impl Id {
return None;
}

if self_expr == other_expr {
return Some(true);
}

if !pureness.is_definition_const(self_expr) || !pureness.is_definition_const(other_expr) {
return None;
}

Some(false)
match (self_expr, other_expr) {
(Expression::Int(a), Expression::Int(b)) => Some(a == b),
(Expression::Text(a), Expression::Text(b)) => Some(a == b),
(
Expression::Tag {
symbol: symbol_a,
value: value_a,
},
Expression::Tag {
symbol: symbol_b,
value: value_b,
},
) => {
if symbol_a != symbol_b || value_a.is_some() != value_b.is_some() {
return Some(false);
}
if let (Some(a), Some(b)) = (value_a, value_b) {
return a.semantically_equals(*b, visible, pureness);
}
Some(true)
}
(Expression::Builtin(a), Expression::Builtin(b)) => Some(a == b),
(Expression::List(a), Expression::List(b)) => {
if a.len() != b.len() {
return Some(false);
}
for (a, b) in a.iter().zip_eq(b) {
if !a.semantically_equals(*b, visible, pureness)? {
return Some(false);
}
}
Some(true)
}
(Expression::Struct(a), Expression::Struct(b)) => {
if a.len() != b.len() {
return Some(false);
}
// TODO: Match keys and compare values.
None
}
// Expressions have different types.
(
Expression::Int(_)
| Expression::Text(_)
| Expression::Tag { .. }
| Expression::Builtin(_)
| Expression::List(_)
| Expression::Struct(_),
Expression::Int(_)
| Expression::Text(_)
| Expression::Tag { .. }
| Expression::Builtin(_)
| Expression::List(_)
| Expression::Struct(_),
) => Some(false),
_ => None,
}
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/vm/src/heap/object_heap/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ impl HeapInt {
operator_fn!(shift_right, Shr, shr);

pub fn bit_length(self, heap: &mut Heap) -> Int {
assert!(*self.get() >= 0.into());
Int::create(heap, true, self.get().bits())
}

Expand Down
23 changes: 11 additions & 12 deletions compiler/vm/src/heap/object_inline/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,25 @@ impl InlineInt {
}

pub fn shift_left(self, heap: &mut Heap, rhs: Self) -> Int {
let lhs = self.get();
let lhs: i64 = self.get();
#[allow(clippy::map_unwrap_or)]
rhs.try_get::<u32>()
.and_then(|rhs| {
// `checked_shl(…)` only checks that `rhs` doesn't exceed the number of bits in the
// type (i.e., `rhs < 64`). However, we need to check that the mathematical result
// is completely representable in our available bits and doesn't get truncated.

let bit_length = if lhs.is_negative() {
// One 1 is necessary for the sign.
i64::BITS - lhs.leading_ones() + 1
} else {
lhs.bit_length()
};

#[allow(clippy::cast_possible_truncation)]
let value_shift = Self::VALUE_SHIFT as u32;

if self.get().bit_length() + rhs < InlineObject::BITS - value_shift {
if bit_length + rhs < InlineObject::BITS - value_shift {
Some(lhs << rhs)
} else {
None
Expand Down Expand Up @@ -197,12 +204,8 @@ impl InlineObjectTrait for InlineInt {
#[extension_trait]
pub impl I64BitLength for i64 {
fn bit_length(self) -> u32 {
if self.is_negative() {
// One 1 is necessary for the sign.
Self::BITS - self.leading_ones() + 1
} else {
Self::BITS - self.leading_zeros()
}
assert!(!self.is_negative());
Self::BITS - self.leading_zeros()
}
}

Expand Down Expand Up @@ -278,10 +281,6 @@ mod tests {

#[test]
fn bit_length() {
assert_eq!(inline_int(-4).bit_length(), inline_int(3));
assert_eq!(inline_int(-3).bit_length(), inline_int(3));
assert_eq!(inline_int(-2).bit_length(), inline_int(2));
assert_eq!(inline_int(-1).bit_length(), inline_int(1));
assert_eq!(inline_int(0).bit_length(), inline_int(0));
assert_eq!(inline_int(1).bit_length(), inline_int(1));
assert_eq!(inline_int(2).bit_length(), inline_int(2));
Expand Down
3 changes: 3 additions & 0 deletions packages/Builtins/_.candy
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ intBitLength value :=
# intBitLength -3 => 2
# ```
needs (value | typeIs Int)
needs
(value | ✨.intAdd 1 | ✨.intCompareTo 0 | ✨.equals Greater)
"Value must be non-negative"
✨.intBitLength value

intBitwiseAnd a b :=
Expand Down
33 changes: 33 additions & 0 deletions packages/Core/bool.candy
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,36 @@ implies a b :=
needs (is a)
needs (is b)
a | not | or b

test =
[checkEquals] = use "..check"

checkEquals (is True) True
checkEquals (is False) True
checkEquals (is 3) False

checkEquals (not True) False
checkEquals (not False) True

## `lazyAnd` and `lazyOr` are automatically tested by the tests for the
## non-lazy variants.

checkEquals (True | and True) True
checkEquals (True | and False) False
checkEquals (False | and True) False
checkEquals (False | and False) False

checkEquals (True | or True) True
checkEquals (True | or False) True
checkEquals (False | or True) True
checkEquals (False | or False) False

checkEquals (True | xor True) False
checkEquals (True | xor False) True
checkEquals (False | xor True) True
checkEquals (False | xor False) False

checkEquals (True | implies True) True
checkEquals (True | implies False) False
checkEquals (False | implies True) True
checkEquals (False | implies False) True
14 changes: 10 additions & 4 deletions packages/Core/check.candy
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
bool = use "..bool"
[if] = use "..controlFlow"
[equals] = use "Builtins"

check condition :=
# Panics if the `condition` is `False`.
#
# This function is useful for ensuring that your mental model of the state of
# your program matches its actual state.
needs (bool.is condition)
needs condition "A check didn't succeed."
isConditionBool = condition %
True | False -> True
_ -> False
needs isConditionBool
needs condition "A check failed."

checkEquals actual expected :=
# Panics if `actual` and `expected` are different.
needs (actual | equals expected) "A check failed: Expected {actual} to be {expected}."
9 changes: 9 additions & 0 deletions packages/Core/controlFlow.candy
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,12 @@ repeat times body :=
recurse (times | builtins.intSubtract 1)
}
}

test =
[checkEquals] = use "..check"

checkEquals (ifElse True { Then } { Else }) Then
checkEquals (ifElse False { Then } { Else }) Else

checkEquals (if True { Then }) Then
checkEquals (if False { Then }) Nothing
37 changes: 37 additions & 0 deletions packages/Core/equality.candy
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
builtins = use "Builtins"

equals := builtins.equals

test =
[checkEquals] = use "..check"

checkEquals (equals 2 Foo) False

## numbers
checkEquals (equals 5 5) True
checkEquals (equals 3 5) False

## text
checkEquals (equals "Hey" "Hey") True
checkEquals (equals "A" "B") False

## tags
checkEquals (equals Kiwi Kiwi) True
checkEquals (equals Kiwi Banana) False
checkEquals (equals Kiwi (Kiwi 3)) False
checkEquals (equals (Kiwi 3) (Kiwi 3)) True
checkEquals (equals (Kiwi 5) (Kiwi 3)) False

## functions
foo = { a -> 4 }
checkEquals (equals foo foo) True
## TODO: Currently, this is not implemented correctly in the VM.
# checkEquals (equals foo { a -> 4 }) False

## lists
checkEquals (equals (1, 2, 3) (1, 2, 3)) True
checkEquals (equals (1, 2, 3) (1, 2)) False
checkEquals (equals (1, 2, 3) (1, 2, 30)) False

## structs
checkEquals (equals [Foo: 2] [Foo: 2]) True
checkEquals (equals [Foo: 2] [Bar: 2]) False
checkEquals (equals [Foo: 2] []) False
checkEquals (equals [Foo: 2] [Foo: 3]) False
14 changes: 14 additions & 0 deletions packages/Core/function.candy
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,17 @@ doNotRun body :=
# - Making the code invalid (for example, by deleting needed functions) results in compiler
# errors.
needs (is0 body)

test =
[checkEquals] = use "..check"

checkEquals (is {}) True
checkEquals (is 4) False
checkEquals (getArgumentCount {}) 0
checkEquals (getArgumentCount { a -> }) 1

checkEquals (is0 {}) True
checkEquals (is0 { a -> }) False

checkEquals (run { 4 }) 4
checkEquals (doNotRun { 4 }) Nothing
Loading

2 comments on commit 8de67ee

@jwbot
Copy link
Collaborator

@jwbot jwbot commented on 8de67ee Oct 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compiler

Benchmark suite Current: 8de67ee Previous: de2cc9e Ratio
Time: Compiler/hello_world 45674162 ns/iter (± 769465) 26462601 ns/iter (± 568109) 1.73
Time: Compiler/fibonacci 2952241170 ns/iter (± 2624802) 191761450 ns/iter (± 1627935) 15.40
Time: VM Runtime/hello_world 58151 ns/iter (± 11621) 67151 ns/iter (± 10242) 0.87

This comment was automatically generated by workflow using github-action-benchmark.

@jwbot
Copy link
Collaborator

@jwbot jwbot commented on 8de67ee Oct 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Compiler'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 2.

Benchmark suite Current: 8de67ee Previous: de2cc9e Ratio
Time: Compiler/fibonacci 2952241170 ns/iter (± 2624802) 191761450 ns/iter (± 1627935) 15.40

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.