diff --git a/compiler/src/mir/passes.rs b/compiler/src/mir/passes.rs index e63bd6a36..dccffa25f 100644 --- a/compiler/src/mir/passes.rs +++ b/compiler/src/mir/passes.rs @@ -1214,7 +1214,7 @@ impl<'a> LowerMethod<'a> { let loc = self.add_location(node.location().clone()); let rets = node.returns_value(); let ret = if rets && !ignore_ret { - self.output_expression(node, false) + self.output_expression(node) } else { self.expression(node) }; @@ -1334,7 +1334,7 @@ impl<'a> LowerMethod<'a> { // this case we need to clone the value type, as the original // value may still be in use after the body, hence the clone // argument is set to `true`. - self.output_expression(n, true) + self.output_expression(n) } else { self.expression(n) }; @@ -2228,7 +2228,7 @@ impl<'a> LowerMethod<'a> { fn return_expression(&mut self, node: hir::Return) -> RegisterId { let loc = self.add_location(node.location); let reg = if let Some(value) = node.value { - self.output_expression(value, false) + self.output_expression(value) } else { self.get_nil(loc) }; @@ -2242,7 +2242,7 @@ impl<'a> LowerMethod<'a> { fn throw_expression(&mut self, node: hir::Throw) -> RegisterId { let loc = self.add_location(node.location); - let reg = self.output_expression(node.value, false); + let reg = self.output_expression(node.value); self.mark_register_as_moved(reg); self.drop_all_registers(); @@ -2324,10 +2324,18 @@ impl<'a> LowerMethod<'a> { ) -> RegisterId { let loc = self.add_location(location); let val = self.expression(value); - let reg = self.new_register(value_type); - self.current_block_mut().increment(reg, val, loc); - reg + if value_type.is_value_type(self.db()) { + let reg = self.clone_value_type(val, value_type, false, loc); + + self.mark_register_as_available(reg); + reg + } else { + let reg = self.new_register(value_type); + + self.current_block_mut().increment(reg, val, loc); + reg + } } fn recover_expression(&mut self, node: hir::Recover) -> RegisterId { @@ -2657,6 +2665,10 @@ impl<'a> LowerMethod<'a> { self.mark_register_as_moved(reg); + if self.register_type(reg).is_permanent(self.db()) { + continue; + } + // If the value matched against is owned, it's destructured // as part of the match. This means any fields ignored need // to be dropped. If the match input is a reference no @@ -2741,6 +2753,10 @@ impl<'a> LowerMethod<'a> { self.mark_register_as_moved(reg); + if self.register_type(reg).is_permanent(self.db()) { + continue; + } + // If the register is the match input register, we always need to // drop it. If the input is a reference, we don't want to drop // unbound intermediate registers, because they were never @@ -3424,11 +3440,7 @@ impl<'a> LowerMethod<'a> { /// Returns the register to use for an output expression (`return` or /// `throw`). - fn output_expression( - &mut self, - node: hir::Expression, - clone_value_type: bool, - ) -> RegisterId { + fn output_expression(&mut self, node: hir::Expression) -> RegisterId { let loc = self.add_location(node.location().clone()); let reg = self.expression(node); @@ -3436,8 +3448,10 @@ impl<'a> LowerMethod<'a> { let typ = self.register_type(reg); - if clone_value_type && typ.is_value_type(self.db()) { - return self.clone_value_type(reg, typ, loc); + if typ.is_value_type(self.db()) { + let force_clone = !typ.is_owned_or_uni(self.db()); + + return self.clone_value_type(reg, typ, force_clone, loc); } if typ.is_owned_or_uni(self.db()) { @@ -3564,7 +3578,7 @@ impl<'a> LowerMethod<'a> { let typ = self.register_type(register); if typ.is_value_type(self.db()) { - return self.clone_value_type(register, typ, location); + return self.clone_value_type(register, typ, false, location); } self.check_field_move(register, location); @@ -3628,6 +3642,7 @@ impl<'a> LowerMethod<'a> { return self.clone_value_type( register, register_type, + false, location, ); } @@ -3643,6 +3658,25 @@ impl<'a> LowerMethod<'a> { return register; } + // References of value types passed to owned values should be cloned. + // This allows passing e.g. `ref Int` to something that expects `Int`, + // without the need for an explicit clone. + if register_type.is_value_type(self.db()) + && expected.map_or(false, |v| v.is_owned_or_uni(self.db())) + { + // In this case we force cloning, so expressions such as + // `foo(vals[0])` pass a clone instead of passing the returned ref + // directly. If we were to pass the ref directly, `foo` might drop + // it thinking its an owned value, then fail because a ref still + // exists. + return self.clone_value_type( + register, + register_type, + true, + location, + ); + } + // For reference types we only need to increment if they originate from // a variable or field, as regular registers can't be referred to more // than once. @@ -3676,9 +3710,10 @@ impl<'a> LowerMethod<'a> { &mut self, source: RegisterId, typ: types::TypeRef, + force_clone: bool, location: LocationId, ) -> RegisterId { - if self.register_kind(source).is_regular() { + if self.register_kind(source).is_regular() && !force_clone { self.mark_register_as_moved(source); // Value types not bound to any variables/fields don't need to be diff --git a/compiler/src/type_check/expressions.rs b/compiler/src/type_check/expressions.rs index 584fc9cbf..1b5bedf85 100644 --- a/compiler/src/type_check/expressions.rs +++ b/compiler/src/type_check/expressions.rs @@ -1279,7 +1279,10 @@ impl<'a> CheckMethodBody<'a> { nodes: &mut [hir::Expression], scope: &mut LexicalScope, ) -> TypeRef { - self.expressions(nodes, scope).pop().unwrap_or_else(TypeRef::nil) + self.expressions(nodes, scope) + .pop() + .unwrap_or_else(TypeRef::nil) + .value_type_as_owned(self.db()) } fn expressions_with_return( @@ -3225,7 +3228,12 @@ impl<'a> CheckMethodBody<'a> { ); } - node.resolved_type = expr.as_ref(self.db()); + node.resolved_type = if expr.is_value_type(self.db()) { + expr + } else { + expr.as_ref(self.db()) + }; + node.resolved_type } @@ -3250,7 +3258,12 @@ impl<'a> CheckMethodBody<'a> { return TypeRef::Error; } - node.resolved_type = expr.as_mut(self.db()); + node.resolved_type = if expr.is_value_type(self.db()) { + expr + } else { + expr.as_mut(self.db()) + }; + node.resolved_type } @@ -3637,7 +3650,7 @@ impl<'a> CheckMethodBody<'a> { ins.type_arguments(db) .copy_into(&mut ctx.type_arguments); - let returns = if raw_typ.is_owned_or_uni(db) { + let mut returns = if raw_typ.is_owned_or_uni(db) { let typ = raw_typ.inferred(db, &mut ctx, false); if receiver.is_ref(db) { @@ -3649,6 +3662,8 @@ impl<'a> CheckMethodBody<'a> { raw_typ.inferred(db, &mut ctx, receiver.is_ref(db)) }; + returns = returns.value_type_as_owned(self.db()); + if receiver.require_sendable_arguments(self.db()) && !returns.is_sendable(self.db()) { diff --git a/libstd/src/std/byte_array.inko b/libstd/src/std/byte_array.inko index 46a93db66..6aea10904 100644 --- a/libstd/src/std/byte_array.inko +++ b/libstd/src/std/byte_array.inko @@ -280,7 +280,7 @@ impl Drop for ByteArray { } } -impl Index[ref Int, Int] for ByteArray { +impl Index[Int, Int] for ByteArray { # Returns the byte at the given index. # # # Examples @@ -294,13 +294,13 @@ impl Index[ref Int, Int] for ByteArray { # # Panics # # This method panics if the index is out of bounds. - fn pub index(index: ref Int) -> Int { + fn pub index(index: Int) -> Int { bounds_check(index, length) _INKO.byte_array_get(self, index) } } -impl SetIndex[ref Int, Int] for ByteArray { +impl SetIndex[Int, Int] for ByteArray { # Stores a byte at the given index, then returns it. # # # Examples @@ -315,7 +315,7 @@ impl SetIndex[ref Int, Int] for ByteArray { # # Panics # # This method panics if the index is out of bounds. - fn pub mut set_index(index: ref Int, value: Int) { + fn pub mut set_index(index: Int, value: Int) { bounds_check(index, length) _INKO.byte_array_set(self, index, value) _INKO.moved(value) diff --git a/libstd/src/std/debug.inko b/libstd/src/std/debug.inko index 0faa5b0de..f19af9809 100644 --- a/libstd/src/std/debug.inko +++ b/libstd/src/std/debug.inko @@ -23,7 +23,7 @@ class pub StackFrame { impl Clone for StackFrame { fn pub clone -> Self { - Self { @path = @path.clone, @name = @name.clone, @line = @line.clone } + Self { @path = @path.clone, @name = @name, @line = @line } } } diff --git a/libstd/src/std/env.inko b/libstd/src/std/env.inko index 5d29afcc8..f65029b3f 100644 --- a/libstd/src/std/env.inko +++ b/libstd/src/std/env.inko @@ -118,7 +118,7 @@ fn pub working_directory !! Error -> Path { # import std::env # # try! env.working_directory = '..' -fn pub working_directory=(directory: ref String) !! Error { +fn pub working_directory=(directory: String) !! Error { try { _INKO.env_set_working_directory(directory) } else (e) { diff --git a/libstd/src/std/ffi.inko b/libstd/src/std/ffi.inko index 801cbac09..b7f91a92f 100644 --- a/libstd/src/std/ffi.inko +++ b/libstd/src/std/ffi.inko @@ -258,12 +258,12 @@ class pub Struct { # Returns the size in bytes of this `Struct`. fn pub size -> Int { - @layout.size.clone + @layout.size } # Returns the alignment of the members in this `Struct`. fn pub alignment -> Int { - @layout.alignment.clone + @layout.alignment } } @@ -274,7 +274,7 @@ impl Index[String, Any] for Struct { fn pub index(index: String) -> Any { let member = @layout.members[index] - @pointer.read(type: member.type.clone, offset: member.offset.clone) + @pointer.read(type: member.type.clone, offset: member.offset) } } @@ -283,7 +283,7 @@ impl SetIndex[String, Any] for Struct { fn pub mut set_index(index: String, value: Any) { let member = @layout.members[index] let type = member.type.clone - let offset = member.offset.clone + let offset = member.offset @pointer.write(type, offset, value) } @@ -336,7 +336,7 @@ class pub LayoutBuilder { @alignment = value.alignment } - @existing.insert(name.clone) + @existing.insert(name) @members.push(name) @types.push(value) } @@ -354,7 +354,7 @@ class pub LayoutBuilder { let members = Map.new let mut size = 0 let mut offset = 0 - let mut remaining_in_hole = @alignment.clone + let mut remaining_in_hole = @alignment let mut index = 0 while index < @members.length { @@ -370,30 +370,23 @@ class pub LayoutBuilder { size += padding offset += padding - remaining_in_hole = @alignment.clone + remaining_in_hole = @alignment } - members[name.clone] = Member { - @name = name.clone, - @type = type.clone, - @offset = offset.clone - } + members[name] = + Member { @name = name, @type = type.clone, @offset = offset } remaining_in_hole -= type_align size += type_align offset += type_align - if remaining_in_hole == 0 { remaining_in_hole = @alignment.clone } + if remaining_in_hole == 0 { remaining_in_hole = @alignment } index += 1 } - Layout { - @members = members, - @alignment = @alignment.clone, - @size = size - } + Layout { @members = members, @alignment = @alignment, @size = size } } fn move into_layout_without_padding -> Layout { @@ -405,21 +398,14 @@ class pub LayoutBuilder { let name = @members[index] let type = @types[index] - members[name.clone] = Member { - @name = name.clone, - @type = type.clone, - @offset = offset.clone - } + members[name] = + Member { @name = name, @type = type.clone, @offset = offset } offset += type.alignment index += 1 } - Layout { - @members = members, - @alignment = @alignment.clone, - @size = offset - } + Layout { @members = members, @alignment = @alignment, @size = offset } } } diff --git a/libstd/src/std/float.inko b/libstd/src/std/float.inko index a17752522..982dbec43 100644 --- a/libstd/src/std/float.inko +++ b/libstd/src/std/float.inko @@ -60,7 +60,7 @@ class builtin Float { # Parsing a `Float` with an exponent: # # Float.parse('1.2e1') # => Option.Some(12.0) - fn pub static parse(string: ref String) -> Option[Self] { + fn pub static parse(string: String) -> Option[Self] { let res = _INKO.string_to_float(string, -1, -1) if _INKO.is_undefined(res) { Option.None } else { Option.Some(res) } diff --git a/libstd/src/std/fs/file.inko b/libstd/src/std/fs/file.inko index e08076690..2d7d33cbf 100644 --- a/libstd/src/std/fs/file.inko +++ b/libstd/src/std/fs/file.inko @@ -162,7 +162,7 @@ impl Write for WriteOnlyFile { try _INKO.file_write_bytes(@fd, bytes) else (e) throw Error.from_int(e) } - fn pub mut write_string(string: ref String) !! Error -> Int { + fn pub mut write_string(string: String) !! Error -> Int { try _INKO.file_write_string(@fd, string) else (e) throw Error.from_int(e) } @@ -239,7 +239,7 @@ impl Write for ReadWriteFile { try _INKO.file_write_bytes(@fd, bytes) else (e) throw Error.from_int(e) } - fn pub mut write_string(string: ref String) !! Error -> Int { + fn pub mut write_string(string: String) !! Error -> Int { try _INKO.file_write_string(@fd, string) else (e) throw Error.from_int(e) } diff --git a/libstd/src/std/fs/path.inko b/libstd/src/std/fs/path.inko index 4d1bcffc0..ecdd361c5 100644 --- a/libstd/src/std/fs/path.inko +++ b/libstd/src/std/fs/path.inko @@ -18,7 +18,7 @@ fn pub separator -> String { # Returns `True` if the given `String` starts with a Windows drive name, such as # C:/. -fn starts_with_windows_drive_name?(path: ref String) -> Bool { +fn starts_with_windows_drive_name?(path: String) -> Bool { if path.size < 3 { return false } let start = path.byte(0) @@ -189,7 +189,7 @@ class pub Path { # Path.new('foo').absolute? # => False # Path.new('/foo').absolute? # => True fn pub absolute? -> Bool { - absolute_path?(@path.clone) + absolute_path?(@path) } # Returns `True` if this `Path` is a relative path. @@ -254,7 +254,7 @@ class pub Path { # # Path.new('/').directory # Path.new('/') fn pub directory -> Path { - let length = bytes_before_last_separator(@path.clone) + let length = bytes_before_last_separator(@path) if length < 0 { return Path.new('.') } @@ -308,7 +308,7 @@ impl ToString for Path { # # path.to_string # => '/dev/null' fn pub to_string -> String { - @path.clone + @path } } @@ -337,7 +337,7 @@ impl Size for Path { impl Clone for Path { fn pub clone -> Self { - Path.new(@path.clone) + Path.new(@path) } } diff --git a/libstd/src/std/index.inko b/libstd/src/std/index.inko index a9e8817fa..174ce5562 100644 --- a/libstd/src/std/index.inko +++ b/libstd/src/std/index.inko @@ -47,7 +47,7 @@ trait pub SetIndex[K, V] { # # Panics # # This method panics if the index is out of bounds. -fn pub bounds_check(index: ref Int, length: ref Int) { +fn pub bounds_check(index: Int, length: Int) { if index >= 0 and index < length { return } _INKO.panic("The index {index} is out of bounds (length: {length})") diff --git a/libstd/src/std/int.inko b/libstd/src/std/int.inko index 8fa83091e..eaa87600f 100644 --- a/libstd/src/std/int.inko +++ b/libstd/src/std/int.inko @@ -41,7 +41,7 @@ class builtin Int { # # Int.from_base2('11') # => Option.Some(3) # Int.from_base2('ff') # => Option.None - fn pub static from_base2(string: ref String) -> Option[Self] { + fn pub static from_base2(string: String) -> Option[Self] { let res = _INKO.string_to_int(string, 2, -1, -1) if _INKO.is_undefined(res) { Option.None } else { Option.Some(res) } @@ -56,7 +56,7 @@ class builtin Int { # # Int.from_base10('12') # => Option.Some(12) # Int.from_base10('ff') # => Option.None - fn pub static from_base10(string: ref String) -> Option[Self] { + fn pub static from_base10(string: String) -> Option[Self] { let res = _INKO.string_to_int(string, 10, -1, -1) if _INKO.is_undefined(res) { Option.None } else { Option.Some(res) } @@ -75,7 +75,7 @@ class builtin Int { # # Int.from_base16('ef') # => Option.Some(239) # Int.from_base16('zz') # => Option.None - fn pub static from_base16(string: ref String) -> Option[Self] { + fn pub static from_base16(string: String) -> Option[Self] { let res = _INKO.string_to_int(string, 16, -1, -1) if _INKO.is_undefined(res) { Option.None } else { Option.Some(res) } diff --git a/libstd/src/std/io.inko b/libstd/src/std/io.inko index a6edf8503..122b969a3 100644 --- a/libstd/src/std/io.inko +++ b/libstd/src/std/io.inko @@ -243,10 +243,10 @@ trait pub Write { fn pub mut write_bytes(bytes: ref ByteArray) !! Error -> Int # Writes a `String` to the stream, returning the number of bytes written. - fn pub mut write_string(string: ref String) !! Error -> Int + fn pub mut write_string(string: String) !! Error -> Int # Writes a `String` followed by a Unix newline to the stream. - fn pub mut print(string: ref String) !! Error -> Int { + fn pub mut print(string: String) !! Error -> Int { try { write_string(string) } + try { write_string("\n") } } diff --git a/libstd/src/std/iter.inko b/libstd/src/std/iter.inko index 69f025faf..d967e3d66 100644 --- a/libstd/src/std/iter.inko +++ b/libstd/src/std/iter.inko @@ -506,9 +506,9 @@ trait pub Bytes[E]: Iter[Int, E] { # let vals = [10, 20, 30].into_iter # # iter.join(vals, ',') => '10,20,30' -fn pub join[T: ToString, E](iter: Iter[T, E], with: ref String) !! E -> String { +fn pub join[T: ToString, E](iter: Iter[T, E], with: String) !! E -> String { let result = try iter.reduce(StringBuffer.new) fn (buff, val) { - if buff.length > 0 { buff.push(with.clone) } + if buff.length > 0 { buff.push(with) } buff.push(val.to_string) buff diff --git a/libstd/src/std/json.inko b/libstd/src/std/json.inko index 065d46054..6daefc85b 100644 --- a/libstd/src/std/json.inko +++ b/libstd/src/std/json.inko @@ -305,7 +305,7 @@ impl Equal for Json { # 10 MiB _per string_. You can change this limit by adjusting the value of the # `max_string_size` field. class pub Parser { - let @string: ref String + let @string: String let @index: Int let @size: Int let @line: Int @@ -322,7 +322,7 @@ class pub Parser { let pub @max_string_size: Int # Returns a new parser that will parse the given `String`. - fn pub static new(string: ref String) -> Self { + fn pub static new(string: String) -> Self { Self { @string = string, @index = 0, @@ -388,7 +388,7 @@ class pub Parser { fn mut string_value !! Error -> String { advance - let start = @index.clone + let start = @index loop { match current { @@ -434,7 +434,7 @@ class pub Parser { throw error("'{slice_string(start)}' is an invalid escape sequence") } case replacement -> { - buffer.push(replacement.clone) + buffer.push(replacement) @index += 2 } } @@ -700,12 +700,12 @@ class pub Parser { error("The character '{char}' is unexpected") } - fn slice_string(start: ref Int) -> String { - @string.slice_bytes(start.clone, length: @index - start) + fn slice_string(start: Int) -> String { + @string.slice_bytes(start, length: @index - start) } fn error(message: String) -> Error { - Error { @message = message, @line = @line.clone, @offset = @index.clone } + Error { @message = message, @line = @line, @offset = @index } } } @@ -806,7 +806,7 @@ class pub Generator { } fn mut indent { - if @pretty { @depth.times fn (_) { @buffer.push(@spaces.clone) } } + if @pretty { @depth.times fn (_) { @buffer.push(@spaces) } } } fn mut newline { @@ -825,6 +825,6 @@ class pub Generator { # import std::json # # try! json.parse('[10]') # => Json.Array([Json.Int(10)]) -fn pub parse(string: ref String) !! Error -> Json { +fn pub parse(string: String) !! Error -> Json { try Parser.new(string).parse } diff --git a/libstd/src/std/map.inko b/libstd/src/std/map.inko index 5b0242afe..c4276d406 100644 --- a/libstd/src/std/map.inko +++ b/libstd/src/std/map.inko @@ -40,7 +40,7 @@ class pub Entry[K: Hash + Equal, V] { } fn hash -> Int { - @hash.clone + @hash } fn move into_value -> V { @@ -165,7 +165,7 @@ class pub Map[K: Hash + Equal, V] { fn pub mut remove(key: ref K) -> Option[V] { let mut slot = slot_index(hash_key(key)) let mut dist = 0 - let mut index = @slots[slot].clone + let mut index = @slots[slot] # For the removal we need both the slot and the entry index, so we have to # duplicate the logic of `entries_index()` here, as this is the only place @@ -179,7 +179,7 @@ class pub Map[K: Hash + Equal, V] { if entry.key == key { break } slot = slot_index(slot + 1) - index = @slots[slot].clone + index = @slots[slot] dist += 1 } @@ -198,7 +198,7 @@ class pub Map[K: Hash + Equal, V] { slot = slot_index(slot + 1) loop { - let mut index = @slots[slot].clone + let mut index = @slots[slot] if index == EMPTY { break } @@ -405,7 +405,7 @@ class pub Map[K: Hash + Equal, V] { let mut slot = slot_index(rehash.hash) loop { - let index = @slots[slot].clone + let index = @slots[slot] if index == EMPTY { @slots[slot] = rehash_index @@ -432,7 +432,7 @@ class pub Map[K: Hash + Equal, V] { let mut slot = slot_index(insert.hash) loop { - let index = @slots[slot].clone + let index = @slots[slot] if index == EMPTY { @slots[slot] = @entries.length @@ -484,7 +484,7 @@ class pub Map[K: Hash + Equal, V] { loop { stolen.distance += 1 - let index = @slots[slot].clone + let index = @slots[slot] if index == EMPTY { @slots[slot] = stolen_index @@ -496,7 +496,7 @@ class pub Map[K: Hash + Equal, V] { if entry.distance < stolen.distance { @slots[slot] = stolen_index - stolen_index = index.clone + stolen_index = index stolen = entry } @@ -509,14 +509,14 @@ class pub Map[K: Hash + Equal, V] { let mut dist = 0 loop { - let index = @slots[slot].clone + let index = @slots[slot] if index == EMPTY { return EMPTY } let entry = @entries[index] if dist > entry.distance { return EMPTY } - if entry.key == key { return index.clone } + if entry.key == key { return index } slot = slot_index(slot + 1) dist += 1 diff --git a/libstd/src/std/net/ip.inko b/libstd/src/std/net/ip.inko index 01add078b..bb0512fe1 100644 --- a/libstd/src/std/net/ip.inko +++ b/libstd/src/std/net/ip.inko @@ -36,7 +36,7 @@ let IPV6_STRING_MAXIMUM_LENGTH = 45 let IPV4_TOIPV6_SHIFT = 8 # Converts a pair of IPv4 octets into a single IPv6 hextet. -fn octets_to_hextet(first: ref Int, second: ref Int) -> Int { +fn octets_to_hextet(first: Int, second: Int) -> Int { first << IPV4_TOIPV6_SHIFT | second } @@ -101,7 +101,7 @@ class pub enum IpAddress { # import std::net::ip::IpAddress # # IpAddress.parse('::1') # => Option.Some(IpAddress.V6(Ipv6Address.new(0, 0, 0, 0, 0, 0, 0, 1))) - fn pub static parse(address: ref String) -> Option[Self] { + fn pub static parse(address: String) -> Option[Self] { if address.contains?(':') { Ipv6Address.parse(address).map fn (v) { V6(v) } } else { @@ -229,7 +229,7 @@ class pub Ipv6Address { # import std::net::ip::Ipv6Address # # Ipv6Address.parse('::1').unwrap.v6? # => true - fn pub static parse(input: ref String) -> Option[Self] { + fn pub static parse(input: String) -> Option[Self] { let bytes = input.to_byte_array let mut cursor = 0 let max = bytes.length @@ -340,14 +340,14 @@ class pub Ipv6Address { if segments.length != IPV6_HEXTETS { return Option.None } Option.Some(Ipv6Address.new( - segments[0].clone, - segments[1].clone, - segments[2].clone, - segments[3].clone, - segments[4].clone, - segments[5].clone, - segments[6].clone, - segments[7].clone, + segments[0], + segments[1], + segments[2], + segments[3], + segments[4], + segments[5], + segments[6], + segments[7], )) } @@ -552,8 +552,8 @@ impl ToString for Ipv6Address { current_len += 1 if current_len > compression_len { - compression_len = current_len.clone - compression_start = current_at.clone + compression_len = current_len + compression_start = current_at } } else { current_at = 0 @@ -594,14 +594,14 @@ impl IntoString for Ipv6Address { impl Clone for Ipv6Address { fn pub clone -> Self { Self { - @a = @a.clone, - @b = @b.clone, - @c = @c.clone, - @d = @d.clone, - @e = @e.clone, - @f = @f.clone, - @g = @g.clone, - @h = @h.clone, + @a = @a, + @b = @b, + @c = @c, + @d = @d, + @e = @e, + @f = @f, + @g = @g, + @h = @h, } } } @@ -624,7 +624,7 @@ class pub Ipv4Address { # let addr = Ipv4Address.parse('1.2.3.4').unwrap # # addr.v4? # => true - fn pub static parse(input: ref String) -> Option[Self] { + fn pub static parse(input: String) -> Option[Self] { # No IPv4 address can be longer than 15 characters (255.255.255.255). if input.size > 15 { return Option.None } @@ -654,10 +654,10 @@ class pub Ipv4Address { if segments.length != IPV4_OCTETS { return Option.None } Option.Some(Ipv4Address { - @a = segments[0].clone, - @b = segments[1].clone, - @c = segments[2].clone, - @d = segments[3].clone + @a = segments[0], + @b = segments[1], + @c = segments[2], + @d = segments[3] }) } @@ -888,6 +888,6 @@ impl IntoString for Ipv4Address { impl Clone for Ipv4Address { fn pub clone -> Self { - Self { @a = @a.clone, @b = @b.clone, @c = @c.clone, @d = @d.clone } + Self { @a = @a, @b = @b, @c = @c, @d = @d } } } diff --git a/libstd/src/std/net/socket.inko b/libstd/src/std/net/socket.inko index 6df814894..e6c175250 100644 --- a/libstd/src/std/net/socket.inko +++ b/libstd/src/std/net/socket.inko @@ -332,7 +332,7 @@ class pub Socket { # port: 9999 # ) fn pub mut send_string_to( - string: ref String, + string: String, ip: ref ToString, port: Int ) !! Error -> Int { @@ -605,7 +605,7 @@ impl Write for Socket { } } - fn pub mut write_string(string: ref String) !! Error -> Int { + fn pub mut write_string(string: String) !! Error -> Int { try { _INKO.socket_write_string(@fd, string, @deadline) } else (err) { @@ -691,7 +691,7 @@ class pub UdpSocket { # port: 9999 # ) fn pub mut send_string_to( - string: ref String, + string: String, ip: ref ToString, port: Int ) !! Error -> Int { @@ -762,7 +762,7 @@ impl Write for UdpSocket { try @socket.write_bytes(bytes) } - fn pub mut write_string(string: ref String) !! Error -> Int { + fn pub mut write_string(string: String) !! Error -> Int { try @socket.write_string(string) } @@ -889,7 +889,7 @@ impl Write for TcpClient { try @socket.write_bytes(bytes) } - fn pub mut write_string(string: ref String) !! Error -> Int { + fn pub mut write_string(string: String) !! Error -> Int { try @socket.write_string(string) } @@ -1060,7 +1060,7 @@ impl Format for UnixAddress { } else if unnamed? { 'unnamed' } else { - @address.clone + @address } formatter.write(write) @@ -1095,7 +1095,7 @@ impl ToString for UnixAddress { # UnixAddress.new('/tmp/test.sock').to_string # => '/tmp/test.sock' # UnixAddress.new("\0example").to_string # => "\0example" fn pub to_string -> String { - @address.clone + @address } } @@ -1282,7 +1282,7 @@ class pub UnixSocket { # try! socket.bind('/tmp/test.sock') # try! socket.send_string_to(string: 'hello', address: '/tmp/test.sock') fn pub mut send_string_to( - string: ref String, + string: String, address: ref ToString ) !! Error -> Int { try { @@ -1444,7 +1444,7 @@ impl Write for UnixSocket { } } - fn pub mut write_string(string: ref String) !! Error -> Int { + fn pub mut write_string(string: String) !! Error -> Int { try { _INKO.socket_write_string(@fd, string, @deadline) } else (err) { @@ -1572,7 +1572,7 @@ impl Write for UnixDatagram { try @socket.write_bytes(bytes) } - fn pub mut write_string(string: ref String) !! Error -> Int { + fn pub mut write_string(string: String) !! Error -> Int { try @socket.write_string(string) } @@ -1686,7 +1686,7 @@ impl Write for UnixClient { try @socket.write_bytes(bytes) } - fn pub mut write_string(string: ref String) !! Error -> Int { + fn pub mut write_string(string: String) !! Error -> Int { try @socket.write_string(string) } diff --git a/libstd/src/std/process.inko b/libstd/src/std/process.inko index 8ade80df1..f0f8e309d 100644 --- a/libstd/src/std/process.inko +++ b/libstd/src/std/process.inko @@ -11,7 +11,7 @@ import std::time::Duration # # A panic is an unrecoverable error meant to guard against code bugs. For # runtime errors, use `try` and `throw` instead. -fn pub panic(message: ref String) -> Never { +fn pub panic(message: String) -> Never { _INKO.panic(message) } diff --git a/libstd/src/std/range.inko b/libstd/src/std/range.inko index 5da0b3745..993ba7e0e 100644 --- a/libstd/src/std/range.inko +++ b/libstd/src/std/range.inko @@ -31,7 +31,7 @@ class pub Range { # Returns `true` if the range is an inclusive range. fn pub inclusive? -> Bool { - @inclusive.clone + @inclusive } } diff --git a/libstd/src/std/stdio.inko b/libstd/src/std/stdio.inko index 01c6924d4..50b174070 100644 --- a/libstd/src/std/stdio.inko +++ b/libstd/src/std/stdio.inko @@ -31,7 +31,7 @@ impl Write for STDOUT { try _INKO.stdout_write_bytes(bytes) else 0 } - fn pub mut write_string(string: ref String) -> Int { + fn pub mut write_string(string: String) -> Int { try _INKO.stdout_write_string(string) else 0 } @@ -39,7 +39,7 @@ impl Write for STDOUT { try _INKO.stdout_flush else return } - fn pub mut print(string: ref String) -> Int { + fn pub mut print(string: String) -> Int { write_string(string) + write_string("\n") } } @@ -60,7 +60,7 @@ impl Write for STDERR { try _INKO.stderr_write_bytes(bytes) else 0 } - fn pub mut write_string(string: ref String) -> Int { + fn pub mut write_string(string: String) -> Int { try _INKO.stderr_write_string(string) else 0 } @@ -68,7 +68,7 @@ impl Write for STDERR { try _INKO.stderr_flush else return } - fn pub mut print(string: ref String) -> Int { + fn pub mut print(string: String) -> Int { write_string(string) + write_string("\n") } } diff --git a/libstd/src/std/string.inko b/libstd/src/std/string.inko index ce904f5f2..df3dafff2 100644 --- a/libstd/src/std/string.inko +++ b/libstd/src/std/string.inko @@ -60,7 +60,7 @@ fn padding(string: String, length: Int, pad_to: Int) -> String { let pad_size = pad_to - length let pad_buf = StringBuffer.new - pad_size.times fn (_) { pad_buf.push(string.clone) } + pad_size.times fn (_) { pad_buf.push(string) } let mut pad = pad_buf.into_string @@ -195,7 +195,7 @@ class builtin String { # 'hello'.byte_index(of: 'l', starting_at: 0) # => Option.Some(2) # 'hello'.byte_index(of: 'l', starting_at: 3) # => Option.Some(3) # 'hello'.byte_index(of: 'x', starting_at: 0) # => Option.None - fn pub byte_index(of: ref String, starting_at: Int) -> Option[Int] { + fn pub byte_index(of: String, starting_at: Int) -> Option[Int] { # This is a naive string searching algorithm (see # https://en.wikipedia.org/wiki/String-searching_algorithm) for more details # on the various algorithms. @@ -233,7 +233,7 @@ class builtin String { # # 'test_starts_with'.starts_with?('test_') # => true # 'hello'.starts_with?('test_') # => false - fn pub starts_with?(prefix: ref String) -> Bool { + fn pub starts_with?(prefix: String) -> Bool { match byte_index(of: prefix, starting_at: 0) { case Some(idx) -> idx == 0 case _ -> false @@ -248,7 +248,7 @@ class builtin String { # # 'hello_world'.ends_with?('world') # => true # 'hello'.ends_with?('world') # => false - fn pub ends_with?(suffix: ref String) -> Bool { + fn pub ends_with?(suffix: String) -> Bool { byte_index(of: suffix, starting_at: size - suffix.size).some? } @@ -275,11 +275,11 @@ class builtin String { # # iter.next # => Option.Some('foo') # iter.next # => Option.Some('bar') - fn pub split(separator: ref String) -> Enum[String, Never] { + fn pub split(separator: String) -> Enum[String, Never] { let mut offset = 0 Enum.new fn move { - match byte_index(of: separator, starting_at: offset.clone) { + match byte_index(of: separator, starting_at: offset) { case Some(at) -> { let start = offset := at + separator.size @@ -360,7 +360,7 @@ class builtin String { # # Examples # # 'xhellox'.strip_prefix('x') # => 'hellox' - fn pub strip_prefix(prefix: ref String) -> String { + fn pub strip_prefix(prefix: String) -> String { if starts_with?(prefix).false? { return clone } slice_bytes(start: prefix.size, length: size - prefix.size).into_string @@ -371,7 +371,7 @@ class builtin String { # # Examples # # 'xhellox'.strip_suffix('x') # => 'xhello' - fn pub strip_suffix(suffix: ref String) -> String { + fn pub strip_suffix(suffix: String) -> String { if ends_with?(suffix).false? { return clone } slice_bytes(start: 0, length: size - suffix.size).into_string @@ -462,7 +462,7 @@ class builtin String { case -1 -> buff.push(byte) case byte -> { buff.push(BSLASH) - buff.push(byte.clone) + buff.push(byte) } } } @@ -555,7 +555,7 @@ impl Add[String] for String { impl ToPath for String { fn pub to_path -> Path { - Path.new(self.clone) + Path.new(clone) } } @@ -584,7 +584,7 @@ class pub Characters { # # We need to maintain a reference to the String, otherwise the underlying # native iterator would be invalidated. - let @string: ref String + let @string: String # The native iterator provided by the VM. let @iter: Any @@ -610,7 +610,7 @@ impl Drop for Characters { # An iterator over the bytes in a `String`. class pub Bytes { - let @string: ref String + let @string: String let @index: Int } diff --git a/libstd/src/std/sys.inko b/libstd/src/std/sys.inko index 37ce42371..58c4972f5 100644 --- a/libstd/src/std/sys.inko +++ b/libstd/src/std/sys.inko @@ -187,7 +187,7 @@ class pub Command { # Returns the program to start. fn pub program -> String { - @program.clone + @program } # Sets the working directory of the command. @@ -340,7 +340,7 @@ class pub ExitStatus { impl ToInt for ExitStatus { fn pub to_int -> Int { - @code.clone + @code } } @@ -369,7 +369,7 @@ impl Write for Stdin { } } - fn pub mut write_string(string: ref String) !! Error -> Int { + fn pub mut write_string(string: String) !! Error -> Int { try { _INKO.child_process_stdin_write_string(@process.raw, string.to_string) } else (code) { diff --git a/libstd/src/std/time.inko b/libstd/src/std/time.inko index 68be5f637..f0fe3c820 100644 --- a/libstd/src/std/time.inko +++ b/libstd/src/std/time.inko @@ -125,7 +125,7 @@ class pub Duration { # # Duration.from_secs(5).to_nanos # => 5000000000 fn pub to_nanos -> Int { - @nanos.clone + @nanos } } @@ -152,7 +152,7 @@ impl ToInstant for Duration { impl Clone for Duration { fn pub clone -> Self { - Self { @nanos = @nanos.clone } + Self { @nanos = @nanos } } } @@ -365,7 +365,7 @@ class pub DateTime { # The returned `Int` is negative if `self` is before the Unix epoch, and # positive for a value that is on or after the Unix epoch. fn pub days_since_unix_epoch -> Int { - let year = if @month <= 2 { @year - 1 } else { @year.clone } + let year = if @month <= 2 { @year - 1 } else { @year } let month = @month let era = if year >= 0 { year } else { year - 399 } / 400 let yoe = year - (era * 400) @@ -398,14 +398,14 @@ class pub DateTime { impl Clone for DateTime { fn pub clone -> Self { Self { - @year = @year.clone, - @month = @month.clone, - @day = @day.clone, - @hour = @hour.clone, - @minute = @minute.clone, - @second = @second.clone, - @sub_second = @sub_second.clone, - @utc_offset = @utc_offset.clone + @year = @year, + @month = @month, + @day = @day, + @hour = @hour, + @minute = @minute, + @second = @second, + @sub_second = @sub_second, + @utc_offset = @utc_offset } } } @@ -465,7 +465,7 @@ impl Add[Duration] for DateTime { fn pub +(other: ref Duration) -> Self { let timestamp = to_float + other.to_secs - DateTime.from_timestamp(timestamp, utc_offset: @utc_offset.clone) + DateTime.from_timestamp(timestamp, utc_offset: @utc_offset) } } @@ -473,7 +473,7 @@ impl Subtract[Duration] for DateTime { fn pub -(other: ref Duration) -> Self { let timestamp = to_float - other.to_secs - DateTime.from_timestamp(timestamp, utc_offset: @utc_offset.clone) + DateTime.from_timestamp(timestamp, utc_offset: @utc_offset) } } @@ -570,7 +570,7 @@ impl ToInstant for Instant { impl Clone for Instant { fn pub clone -> Self { - Self { @nanos = @nanos.clone } + Self { @nanos = @nanos } } } diff --git a/libstd/test/helpers.inko b/libstd/test/helpers.inko index 8521837ce..772950b6c 100644 --- a/libstd/test/helpers.inko +++ b/libstd/test/helpers.inko @@ -29,7 +29,7 @@ class pub Script { let @stdin: Option[String] let @imports: Array[String] - fn pub static new(id: ref Int, code: String) -> Self { + fn pub static new(id: Int, code: String) -> Self { let path = env.temporary_directory.join("inko_test_script_{id}.inko") let exe = try! env.executable let cmd = Command.new(exe) @@ -43,7 +43,7 @@ class pub Script { cmd.stdout(Stream.Piped) Self { - @id = id.clone, + @id = id, @path = path, @code = code, @cmd = cmd, diff --git a/libstd/test/std/fs/test_file.inko b/libstd/test/std/fs/test_file.inko index b0a80d548..c13f07212 100644 --- a/libstd/test/std/fs/test_file.inko +++ b/libstd/test/std/fs/test_file.inko @@ -3,7 +3,7 @@ import std::fs::file::(self, ReadOnlyFile, ReadWriteFile, WriteOnlyFile) import std::fs::path::Path import std::test::Tests -fn write(string: ref String, to: ref Path) { +fn write(string: String, to: ref Path) { let file = try! WriteOnlyFile.new(to.clone) try! file.write_string(string) diff --git a/libstd/test/std/fs/test_path.inko b/libstd/test/std/fs/test_path.inko index eaee2d5b4..b90d298ac 100644 --- a/libstd/test/std/fs/test_path.inko +++ b/libstd/test/std/fs/test_path.inko @@ -20,7 +20,7 @@ fn accessed_at? -> Bool { true } -fn write(string: ref String, to: ref Path) { +fn write(string: String, to: ref Path) { let file = try! WriteOnlyFile.new(to.clone) try! file.write_string(string) diff --git a/libstd/test/std/net/test_socket.inko b/libstd/test/std/net/test_socket.inko index cee3e66d8..f6d3f317a 100644 --- a/libstd/test/std/net/test_socket.inko +++ b/libstd/test/std/net/test_socket.inko @@ -16,7 +16,7 @@ import std::time::(Duration, Instant) class SocketPath { let @path: Path - fn static pair(id: ref Int) -> (Self, Self) { + fn static pair(id: Int) -> (Self, Self) { let p1 = env.temporary_directory.join("inko-test-{id}-1.sock") let p2 = env.temporary_directory.join("inko-test-{id}-2.sock") @@ -26,7 +26,7 @@ class SocketPath { (Self { @path = p1 }, Self { @path = p2 }) } - fn static new(id: ref Int) -> Self { + fn static new(id: Int) -> Self { let path = env.temporary_directory.join("inko-test-{id}.sock") try file.remove(path) else nil diff --git a/libstd/test/std/test_io.inko b/libstd/test/std/test_io.inko index 229f34d4a..1853b9b63 100644 --- a/libstd/test/std/test_io.inko +++ b/libstd/test/std/test_io.inko @@ -42,7 +42,7 @@ impl Write for Writer { bytes.length } - fn pub mut write_string(string: ref String) -> Int { + fn pub mut write_string(string: String) -> Int { string.to_byte_array.iter.each fn (byte) { @buffer.push(byte) } string.size } diff --git a/libstd/test/std/test_test.inko b/libstd/test/std/test_test.inko index 0c8e880b4..ad3589bba 100644 --- a/libstd/test/std/test_test.inko +++ b/libstd/test/std/test_test.inko @@ -13,7 +13,7 @@ class Buffer { } impl Write for Buffer { - fn pub mut write_string(string: ref String) -> Int { + fn pub mut write_string(string: String) -> Int { let bytes = string.to_byte_array let size = bytes.length diff --git a/types/src/lib.rs b/types/src/lib.rs index ded8a2776..757a123af 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -3818,6 +3818,19 @@ impl TypeRef { self.as_ref(db) } else if other.is_mut(db) { self.as_mut(db) + } else if self.is_value_type(db) + && !self.is_owned_or_uni(db) + && other.is_owned_or_uni(db) + { + self.as_owned(db) + } else { + self + } + } + + pub fn value_type_as_owned(self, db: &Database) -> Self { + if self.is_value_type(db) { + self.as_owned(db) } else { self } @@ -3873,9 +3886,11 @@ impl TypeRef { pub fn as_uni(self, db: &Database) -> Self { match self { - TypeRef::Owned(id) | TypeRef::Infer(id) | TypeRef::Uni(id) => { - TypeRef::Uni(id) - } + TypeRef::Owned(id) + | TypeRef::Infer(id) + | TypeRef::Uni(id) + | TypeRef::Mut(id) + | TypeRef::Ref(id) => TypeRef::Uni(id), TypeRef::OwnedSelf | TypeRef::UniSelf => TypeRef::UniSelf, TypeRef::Placeholder(id) => { id.value(db).map_or(self, |v| v.as_uni(db)) @@ -4465,6 +4480,11 @@ impl TypeRef { TypeRef::Owned(their_id) | TypeRef::Infer(their_id) => { our_id.type_check(db, their_id, context, subtyping) } + TypeRef::Ref(their_id) | TypeRef::Mut(their_id) + if self.is_value_type(db) => + { + our_id.type_check(db, their_id, context, subtyping) + } TypeRef::Any | TypeRef::RefAny | TypeRef::Error => true, TypeRef::OwnedSelf => { our_id.type_check(db, context.self_type, context, subtyping) @@ -4501,6 +4521,11 @@ impl TypeRef { TypeRef::Ref(their_id) | TypeRef::Infer(their_id) => { our_id.type_check(db, their_id, context, subtyping) } + TypeRef::Owned(their_id) | TypeRef::Uni(their_id) + if self.is_value_type(db) => + { + our_id.type_check(db, their_id, context, subtyping) + } TypeRef::Error => true, TypeRef::RefSelf => { our_id.type_check(db, context.self_type, context, subtyping) @@ -4514,6 +4539,11 @@ impl TypeRef { TypeRef::Mut(their_id) => { our_id.type_check(db, their_id, context, false) } + TypeRef::Owned(their_id) | TypeRef::Uni(their_id) + if self.is_value_type(db) => + { + our_id.type_check(db, their_id, context, subtyping) + } TypeRef::Error => true, TypeRef::RefSelf => { our_id.type_check(db, context.self_type, context, subtyping)