Restore Boolean "if" instruction cleanup

By introducing the virtual "if" instruction we can clean up Boolean so
that we no longer have to refine the True and False instances. If we
also clean up Nil in a similar way it becomes easier to rewrite the
compiler in Inko, as it no longer needs to support the refining of
random objects.

This was originally introduced in
ecd11a07, but later reverted. In this
commit we re-introduce these changes. Refining True and False makes
various compiler internals in the self-hosting compiler less efficient,
as it has to support refining any object even when this only happens for
True, False, and Nil. By cleaning up True and False we can reduce this
down to just Nil, making it easier to find a way of cleaning that up
too.
parent d30f885c
......@@ -1583,6 +1583,10 @@ module Inkoc
typedb.byte_array_type.new_instance
end
def on_raw_if(node, _)
node.arguments.fetch(1).type.new_instance
end
def define_block_signature(node, scope, expected_block = nil)
define_type_parameters(node, scope)
define_argument_types(node, scope, expected_block)
......
......@@ -1481,6 +1481,33 @@ module Inkoc
raw_unary_instruction(:RandomBytes, node, body)
end
def on_raw_if(node, body)
loc = node.location
rec_node = node.arguments.fetch(0)
result = body.register(rec_node.type)
receiver = process_node(rec_node, body)
body.instruct(:GotoNextBlockIfTrue, receiver, loc)
# The block used for the "false" argument.
if_false = process_node(node.arguments.fetch(2), body)
body.instruct(:Unary, :SetRegister, result, if_false, loc)
body.instruct(:SkipNextBlock, loc)
# The block used for the "true" argument.
body.add_connected_basic_block
if_true = process_node(node.arguments.fetch(1), body)
body.instruct(:Unary, :SetRegister, result, if_true, loc)
body.add_connected_basic_block
result
end
def on_return(node, body)
location = node.location
register =
......
......@@ -8,62 +8,34 @@ import std::conversion::ToString
impl Boolean {
# Returns the `Boolean` that is the opposite of `self`.
def not -> Boolean {
False
_INKOC.if(self, False, True)
}
}
impl Conditional for Boolean {
def if_true!(R)(block: do -> R) -> ?R {
block.call
}
def if_false!(R)(block: do -> R) -> ?R {
Nil
}
def if!(R)(true: do -> R, false: do -> R) -> R {
true.call
}
def and(other: do -> Boolean) -> Boolean {
other.call
}
def or(other: do -> Boolean) -> Boolean {
True
_INKOC.if(self, true, false).call
}
}
impl ToString for Boolean {
def to_string -> String {
_INKOC.get_attribute(self, '@_object_name') as String
}
}
impl Conditional for False {
def if_true!(R)(block: do -> R) -> ?R {
Nil
_INKOC.if(self, block.call, Nil)
}
def if_false!(R)(block: do -> R) -> ?R {
block.call
}
def if!(R)(true: do -> R, false: do -> R) -> R {
false.call
_INKOC.if(self, Nil, block.call)
}
def and(other: do -> Boolean) -> Boolean {
False
_INKOC.if(self, other.call, False)
}
def or(other: do -> Boolean) -> Boolean {
other.call
_INKOC.if(self, True, other.call)
}
}
impl False {
def not -> Boolean {
True
impl ToString for Boolean {
def to_string -> String {
_INKOC.if(self, 'True', 'False')
}
}
......@@ -2,82 +2,6 @@ import std::map::DefaultHasher
import std::test
import std::test::assert
test.group('std::boolean::Boolean.not') do (g) {
g.test('Returning the opposite of Boolean') {
assert.false(Boolean.not)
}
}
test.group('std::boolean::Boolean.if_true') do (g) {
g.test('The supplied Block is always executed') {
let number = Boolean.if_true {
10
}
assert.equal(number, 10)
}
}
test.group('std::boolean::Boolean.if_false') do (g) {
g.test('The supplied Block is never executed') {
let number = Boolean.if_false {
10
}
assert.equal(number, Nil)
}
}
test.group('std::boolean::Boolean.if') do (g) {
g.test('The Block passed to the "true" argument is always executed') {
let number = Boolean.if(true: { 10 }, false: { 20 })
assert.equal(number, 10)
}
}
test.group('std::boolean::Boolean.and') do (g) {
g.test('The supplied Block is always executed') {
assert.equal(Boolean.and({ True }), True)
}
}
test.group('std::boolean::Boolean.or') do (g) {
g.test('The return value is always True') {
assert.equal(Boolean.or({ False }), True)
}
}
test.group('std::boolean::Boolean.==') do (g) {
g.test('Comparing Boolean with other booleans') {
assert.equal(Boolean, Boolean)
assert.not_equal(Boolean, True)
assert.not_equal(Boolean, False)
}
}
test.group('std::boolean::Boolean.to_string') do (g) {
g.test('Converting Boolean to a String') {
assert.equal(Boolean.to_string, 'Boolean')
}
}
test.group('std::boolean::Boolean.hash') do (g) {
g.test('Hashing a Boolean') {
let hasher1 = DefaultHasher.new(1, 2)
let hasher2 = DefaultHasher.new(1, 2)
Boolean.hash(hasher1)
Boolean.hash(hasher2)
# The exact hash value may change between OS processes or releases, so all
# we can do is assert that the value is the same every time we send `hash`
# to `Boolean`.
assert.equal(hasher1.to_hash, hasher2.to_hash)
}
}
test.group('std::boolean::True.not') do (g) {
g.test('Returning the opposite of True') {
assert.false(True.not)
......
......@@ -478,10 +478,6 @@ test.group('std::mirror::BlockMirror.format') do (g) {
}
test.group('std::mirror::BooleanMirror.format') do (g) {
g.test('Formatting Boolean') {
assert.equal(format(BooleanMirror.new(Boolean)), 'Boolean')
}
g.test('Formatting True') {
assert.equal(format(BooleanMirror.new(True)), 'True')
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment