Split Nil into Nil and NilType

Nil is still the singleton instance, but its prototype is set to NilType
instead of Object. By splitting Nil this way we no longer refine
instances of ad-hoc instances, instead only refining objects defined
directly using the `object` keyword.
parent ecd11a07
Pipeline #105679110 passed with stages
in 13 minutes and 30 seconds
......@@ -1299,6 +1299,10 @@ module Inkoc
typedb.block_type.new_instance
end
def on_raw_get_nil_prototype(*)
typedb.nil_type.new_instance
end
def optional_array_element_value(array)
param = array.lookup_type_parameter(Config::ARRAY_TYPE_PARAMETER)
type = array.lookup_type_parameter_instance(param) || param
......
......@@ -1012,6 +1012,10 @@ module Inkoc
builtin_prototype_instruction(PrototypeID::BLOCK, node, body)
end
def on_raw_get_nil_prototype(node, body)
builtin_prototype_instruction(PrototypeID::NIL, node, body)
end
def on_raw_array_length(node, body)
raw_unary_instruction(:ArrayLength, node, body)
end
......
......@@ -10,5 +10,6 @@ module Inkoc
BLOCK = 5
BOOLEAN = 6
BYTE_ARRAY = 7
NIL = 8
end
end
......@@ -13,7 +13,7 @@
let Boolean = _INKOC.get_boolean_prototype
let True = _INKOC.get_true
let False = _INKOC.get_false
let Nil = _INKOC.get_nil
let NilType = _INKOC.get_nil_prototype
let Object = _INKOC.get_object_prototype
let String = _INKOC.get_string_prototype
......@@ -25,9 +25,7 @@ let ByteArray = _INKOC.get_byte_array_prototype
_INKOC.set_object_name(self, 'Inko')
_INKOC.set_object_name(Boolean, 'Boolean')
_INKOC.set_object_name(True, 'True')
_INKOC.set_object_name(False, 'False')
_INKOC.set_object_name(Nil, 'Nil')
_INKOC.set_object_name(NilType, 'NilType')
_INKOC.set_object_name(Object, 'Object')
_INKOC.set_object_name(String, 'String')
_INKOC.set_object_name(Integer, 'Integer')
......
......@@ -5,7 +5,8 @@ let Inko = _INKOC.get_toplevel
let Boolean = Inko::Boolean
let True = Inko::True
let False = Inko::False
let Nil = Inko::Nil
let NilType = Inko::NilType
let Nil = _INKOC.get_nil
let Object = Inko::Object
let String = Inko::String
let Integer = Inko::Integer
......
......@@ -165,7 +165,7 @@ impl Block {
# }
#
# number # => 0
def while_false(block: do) -> Nil {
def while_false(block: do) -> NilType {
call.if_true { return }
block.call
while_false(block)
......@@ -185,7 +185,7 @@ impl Block {
# }
#
# number # => 10
def while_true(block: do) -> Nil {
def while_true(block: do) -> NilType {
call.if_false { return }
block.call
while_true(block)
......
......@@ -42,7 +42,7 @@ impl ToString for Boolean {
}
impl Hash for Boolean {
def hash(hasher: Hasher) -> Nil {
def hash(hasher: Hasher) -> NilType {
hasher.write_boolean(self)
Nil
}
......
......@@ -774,7 +774,7 @@ object Lexer {
}
# Consumes any whitespace, until reaching the first non-whitespace character.
def consume_whitespace -> Nil {
def consume_whitespace -> NilType {
whitespace?.if_false {
return
}
......@@ -790,7 +790,7 @@ object Lexer {
consume_whitespace
}
def advance_line -> Nil {
def advance_line -> NilType {
@position += 1
@column = 1
@line += 1
......@@ -798,7 +798,7 @@ object Lexer {
Nil
}
def advance_one_byte -> Nil {
def advance_one_byte -> NilType {
@position += 1
@column += 1
......
......@@ -1197,7 +1197,7 @@ object Parser {
True
}
def require_token_type(token: Token, expected: String) !! ParseError -> Nil {
def require_token_type(token: Token, expected: String) !! ParseError -> NilType {
(token.type == expected).if_true {
return
}
......@@ -1315,14 +1315,14 @@ object Parser {
values
}
def greedy_list(stop: String, block: do !! ParseError) !! ParseError -> Nil {
def greedy_list(stop: String, block: do !! ParseError) !! ParseError -> NilType {
try list(stop: stop, block: block)
try token_of_type(stop)
Nil
}
def list(stop: String, block: do !! ParseError) !! ParseError -> Nil {
def list(stop: String, block: do !! ParseError) !! ParseError -> NilType {
{ peek_token.type == stop }.while_false {
try block.call
try list_separator_or_terminal(stop)
......@@ -1331,7 +1331,7 @@ object Parser {
Nil
}
def list_separator_or_terminal(type: String) !! ParseError -> Nil {
def list_separator_or_terminal(type: String) !! ParseError -> NilType {
let peeked = peek_token
(peeked.type == 'comma').if_true {
......
......@@ -56,7 +56,7 @@ impl SetIndex!(String, String) for ThisModule {
#
# env.remove('FOO') # => Nil
# env['FOO'] # => Nil
def remove(variable: String) -> Nil {
def remove(variable: String) -> NilType {
_INKOC.env_remove(variable)
}
......
......@@ -181,7 +181,7 @@ object Library {
impl Close for Library {
# Disposes of the current library.
def close -> Nil {
def close -> NilType {
_INKOC.drop(self)
}
}
......@@ -424,7 +424,7 @@ object LayoutBuilder {
}
# Disables the padding of members.
def disable_padding -> Nil {
def disable_padding -> NilType {
@padding = False
@alignment = 1
......
......@@ -347,7 +347,7 @@ impl ToString for Float {
}
impl Hash for Float {
def hash(hasher: Hasher) -> Nil {
def hash(hasher: Hasher) -> NilType {
hasher.write_float(self)
Nil
}
......
......@@ -19,7 +19,7 @@ trait Formatter: ToString {
#
# This method can be used to produce a placeholder when formatting an object
# that is too deeply nested.
def descend(block: do) -> Nil {
def descend(block: do) -> NilType {
block.call
Nil
}
......@@ -57,7 +57,7 @@ impl Formatter for DefaultFormatter {
#
# If nesting _is_ too great, a placeholder value is added to the buffer, and
# the supplied block is not executed.
def descend(block: do) -> Nil {
def descend(block: do) -> NilType {
(@nesting >= MAX_DEPTH).if_true {
push(PLACEHOLDER)
return
......
......@@ -54,7 +54,7 @@ def size(path: String) !! IOError -> Integer {
}
# Removes the file for the given path.
def remove(path: String) !! IOError -> Nil {
def remove(path: String) !! IOError -> NilType {
process.blocking {
try _INKOC.file_remove(path) else (err) throw IOError.new(err as String)
}
......@@ -147,7 +147,7 @@ def write_bytes(file, bytes: ByteArray) !! IOError -> Integer {
}
# Flushes the contents of a file to disk.
def flush(file) !! IOError -> Nil {
def flush(file) !! IOError -> NilType {
process.blocking {
try _INKOC.file_flush(file) else (err) throw IOError.new(err as String)
}
......
......@@ -32,7 +32,7 @@ import std::process
# import std::fs::dir
#
# try! dir.create(path: '/tmp/test/hello/world', recursive: True)
def create(path: ToPath, recursive = False) !! IOError -> Nil {
def create(path: ToPath, recursive = False) !! IOError -> NilType {
process.blocking {
try {
_INKOC.directory_create(path.to_path.to_string, recursive)
......@@ -67,7 +67,7 @@ def create(path: ToPath, recursive = False) !! IOError -> Nil {
#
# try! dir.create(path: '/tmp/foo/bar', recursive: True)
# try! dir.remove(path: '/tmp/foo', recursive: True)
def remove(path: ToPath, recursive = False) !! IOError -> Nil {
def remove(path: ToPath, recursive = False) !! IOError -> NilType {
process.blocking {
try {
_INKOC.directory_remove(path.to_path.to_string, recursive)
......
......@@ -23,7 +23,7 @@ trait FilePath {
# An object that allows the removal of an associated file on the filesystem.
trait Remove {
# Removes the file from the underlying file system.
def remove !! IOError -> Nil
def remove !! IOError -> NilType
}
# A file that can only be used for read operations.
......@@ -79,7 +79,7 @@ impl FilePath for ReadOnlyFile {
}
impl Close for ReadOnlyFile {
def close -> Nil {
def close -> NilType {
_INKOC.drop(self)
}
}
......@@ -122,7 +122,7 @@ impl Write for WriteOnlyFile {
try bits.write_string(file: self, data: data.to_string)
}
def flush !! IOError -> Nil {
def flush !! IOError -> NilType {
try bits.flush(self)
}
}
......@@ -146,13 +146,13 @@ impl FilePath for WriteOnlyFile {
}
impl Close for WriteOnlyFile {
def close -> Nil {
def close -> NilType {
_INKOC.drop(self)
}
}
impl Remove for WriteOnlyFile {
def remove !! IOError -> Nil {
def remove !! IOError -> NilType {
try ThisModule.remove(@path)
}
}
......@@ -201,7 +201,7 @@ impl Write for ReadWriteFile {
try bits.write_string(file: self, data: data.to_string)
}
def flush !! IOError -> Nil {
def flush !! IOError -> NilType {
try bits.flush(self)
}
}
......@@ -225,13 +225,13 @@ impl FilePath for ReadWriteFile {
}
impl Close for ReadWriteFile {
def close -> Nil {
def close -> NilType {
_INKOC.drop(self)
}
}
impl Remove for ReadWriteFile {
def remove !! IOError -> Nil {
def remove !! IOError -> NilType {
try ThisModule.remove(@path)
}
}
......@@ -248,7 +248,7 @@ impl Remove for ReadWriteFile {
#
# try! handle.write('hello')
# try! file.remove('/tmp/test.txt') # => Nil
def remove(path: ToPath) !! IOError -> Nil {
def remove(path: ToPath) !! IOError -> NilType {
try bits.remove(path.to_path.to_string)
}
......
......@@ -33,5 +33,5 @@ trait Hasher {
# A value that can be hashed.
trait Hash: Equal {
# Writes the hash for `self` into the given `Hasher`.
def hash(hasher: Hasher) -> Nil
def hash(hasher: Hasher) -> NilType
}
......@@ -146,7 +146,7 @@ impl Numeric for Integer {
}
impl Hash for Integer {
def hash(hasher: Hasher) -> Nil {
def hash(hasher: Hasher) -> NilType {
hasher.write_integer(self)
Nil
}
......
......@@ -19,7 +19,7 @@ trait Close {
# Closes the stream.
#
# After a stream is closed, any reads or writes might panic.
def close -> Nil
def close -> NilType
}
# Trait for retrieving the size of an IO object.
......@@ -60,7 +60,7 @@ trait Write {
def write_string(string: ToString) !! Error -> Integer
# Flushes any pending writes.
def flush !! Error -> Nil
def flush !! Error -> NilType
# Writes the given `String` to the stream, followed by a line separator.
#
......
......@@ -384,7 +384,7 @@ object Map!(K: Hash + Equal, V) {
}
# Resizes and rehashes `self`.
def rehash -> Nil {
def rehash -> NilType {
let old_buckets = @buckets
@capacity = @capacity * 2
......@@ -415,7 +415,7 @@ object Map!(K: Hash + Equal, V) {
#
# The `Pair` to insert must be pre-hashed using the `Hasher` used internally
# by this `Map`, otherwise it might not be retrieved later.
def insert_pair(mut pair: Pair!(K, V)) -> Nil {
def insert_pair(mut pair: Pair!(K, V)) -> NilType {
let mut index = desired_bucket(pair.hash)
{
......@@ -445,7 +445,7 @@ object Map!(K: Hash + Equal, V) {
}
# Rehashes an existing pair into the list of buckets.
def rehash_pair(mut pair: Pair!(K, V)) -> Nil {
def rehash_pair(mut pair: Pair!(K, V)) -> NilType {
let mut index = desired_bucket(pair.hash)
{
......@@ -508,7 +508,7 @@ object Map!(K: Hash + Equal, V) {
}
# Shifts all pairs to the left starting at the given bucket index.
def backwards_shift(mut index: Integer) -> Nil {
def backwards_shift(mut index: Integer) -> NilType {
let mut pair = @buckets[index]
{ pair.and { pair!.distance.positive? } }.while_true {
......
......@@ -893,9 +893,9 @@ impl Mirror for BooleanMirror {
# A mirror for `Nil`
object NilMirror {
# The `Nil` that is mirrored.
@subject: Nil
@subject: NilType
def init(subject: Nil) {
def init(subject: NilType) {
@subject = subject
}
}
......@@ -908,7 +908,7 @@ impl Format for NilMirror {
}
impl Mirror for NilMirror {
def subject -> Nil {
def subject -> NilType {
@subject
}
}
......@@ -991,7 +991,7 @@ impl Mirrored for Boolean {
}
}
impl Mirrored for Nil {
impl Mirrored for NilType {
def mirror -> NilMirror {
NilMirror.new(self)
}
......
......@@ -101,7 +101,7 @@ def address(socket, peer = False) !! IoError -> Array!(Dynamic) {
}
}
def bind(socket, address: String, port: Integer) !! IoError -> Nil {
def bind(socket, address: String, port: Integer) !! IoError -> NilType {
try {
_INKOC.socket_bind(socket, address, port)
} else (error) {
......@@ -111,7 +111,7 @@ def bind(socket, address: String, port: Integer) !! IoError -> Nil {
Nil
}
def connect(socket, address: String, port: Integer) !! IoError -> Nil {
def connect(socket, address: String, port: Integer) !! IoError -> NilType {
try {
_INKOC.socket_connect(socket, address, port)
} else (error) {
......@@ -182,7 +182,7 @@ def write_string(socket, string: String) !! IoError -> Integer {
}
}
def close(socket) -> Nil {
def close(socket) -> NilType {
_INKOC.drop(socket)
}
......@@ -197,7 +197,7 @@ def to_stream(socket, prototype) {
stream
}
def shutdown_read(socket) !! IoError -> Nil {
def shutdown_read(socket) !! IoError -> NilType {
try {
_INKOC.socket_shutdown(socket, SHUTDOWN_READ)
} else (error) {
......@@ -205,7 +205,7 @@ def shutdown_read(socket) !! IoError -> Nil {
}
}
def shutdown_write(socket) !! IoError -> Nil {
def shutdown_write(socket) !! IoError -> NilType {
try {
_INKOC.socket_shutdown(socket, SHUTDOWN_WRITE)
} else (error) {
......@@ -213,7 +213,7 @@ def shutdown_write(socket) !! IoError -> Nil {
}
}
def shutdown(socket) !! IoError -> Nil {
def shutdown(socket) !! IoError -> NilType {
try {
_INKOC.socket_shutdown(socket, SHUTDOWN_BOTH)
} else (error) {
......
......@@ -95,7 +95,7 @@ object Socket {
# let socket = try! Socket.new(domain: IPV4, kind: DGRAM)
#
# try! socket.bind(ip: '0.0.0.0', port: 9999)
def bind(ip: ToIpAddress, port: Integer) !! IoError -> Nil {
def bind(ip: ToIpAddress, port: Integer) !! IoError -> NilType {
try bits.bind(socket: self, address: ip.to_ip_address.to_string, port: port)
}
......@@ -113,7 +113,7 @@ object Socket {
# try! socket.bind(ip: '0.0.0.0', port: 9999)
# try! socket.listen
# try! client.connect(ip: '0.0.0.0', port: 9999)
def connect(ip: ToIpAddress, port: Integer) !! IoError -> Nil {
def connect(ip: ToIpAddress, port: Integer) !! IoError -> NilType {
try bits.connect(
socket: self,
address: ip.to_ip_address.to_string,
......@@ -430,17 +430,17 @@ object Socket {
}
# Shuts down the reading half of this socket.
def shutdown_read !! IoError -> Nil {
def shutdown_read !! IoError -> NilType {
try bits.shutdown_read(self)
}
# Shuts down the writing half of this socket.
def shutdown_write !! IoError -> Nil {
def shutdown_write !! IoError -> NilType {
try bits.shutdown_write(self)
}
# Shuts down both the reading and writing half of this socket.
def shutdown !! IoError -> Nil {
def shutdown !! IoError -> NilType {
try bits.shutdown(self)
}
}
......@@ -460,14 +460,14 @@ impl Write for Socket {
try bits.write_string(socket: self, string: string)
}
def flush -> Nil {
def flush -> NilType {
# Sockets can't be flushed, so this method is just a noop.
Nil
}
}
impl Close for Socket {
def close -> Nil {
def close -> NilType {
bits.close(self)
}
}
......@@ -525,7 +525,7 @@ object UdpSocket {
# let socket2 = try! UdpSocket.new(ip: '0.0.0.0', port: 41_000)
#
# try! socket1.connect(ip: '0.0.0.0', port: 41_000)
def connect(ip: ToIpAddress, port: Integer) !! IoError -> Nil {
def connect(ip: ToIpAddress, port: Integer) !! IoError -> NilType {
try @socket.connect(ip: ip, port: port)
}
......@@ -592,13 +592,13 @@ impl Write for UdpSocket {
try @socket.write_string(string)
}
def flush -> Nil {
def flush -> NilType {
@socket.flush
}
}
impl Close for UdpSocket {
def close -> Nil {
def close -> NilType {
@socket.close
}
}
......@@ -652,17 +652,17 @@ object TcpStream {
}
# Shuts down the reading half of this socket.
def shutdown_read !! IoError -> Nil {
def shutdown_read !! IoError -> NilType {
try @socket.shutdown_read
}
# Shuts down the writing half of this socket.
def shutdown_write !! IoError -> Nil {
def shutdown_write !! IoError -> NilType {
try @socket.shutdown_write
}
# Shuts down both the reading and writing half of this socket.
def shutdown !! IoError -> Nil {
def shutdown !! IoError -> NilType {
try @socket.shutdown
}
}
......@@ -682,13 +682,13 @@ impl Write for TcpStream {
try @socket.write_string(string)
}
def flush -> Nil {
def flush -> NilType {
@socket.flush
}
}
impl Close for TcpStream {
def close -> Nil {
def close -> NilType {
@socket.close
}
}
......@@ -794,7 +794,7 @@ object TcpListener {
}
impl Close for TcpListener {
def close -> Nil {
def close -> NilType {
@socket.close
}
}
......
......@@ -163,7 +163,7 @@ object Socket {
# let socket = try! Socket.new(DGRAM)
#
# try! socket.bind('/tmp/test.sock')
def bind(path: ToString) !! IoError -> Nil {
def bind(path: ToString) !! IoError -> NilType {
try bits.bind(socket: self, address: path.to_string, port: 0)
}
......@@ -182,7 +182,7 @@ object Socket {
# try! listener.listen
#
# try! stream.connect('/tmp/test.sock')
def connect(path: ToString) !! IoError -> Nil {
def connect(path: ToString) !! IoError -> NilType {
try bits.connect(socket: self, address: path.to_string, port: 0)
}
......@@ -323,17 +323,17 @@ object Socket {
}
# Shuts down the reading half of this socket.
def shutdown_read !! IoError -> Nil {
def shutdown_read !! IoError -> NilType {
try bits.shutdown_read(self)
}
# Shuts down the writing half of this socket.
def shutdown_write !! IoError -> Nil {
def shutdown_write !! IoError -> NilType {
try bits.shutdown_write(self)
}
# Shuts down both the reading and writing half of this socket.