From e605a21fd3a3707f63615b77d0256a7a3e490aeb Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 5 Sep 2025 23:30:35 +0200 Subject: [PATCH] Revert "Revert 0.7.6 code for 0.7.5 re-release" This reverts commit d1349c9cfbd3dab6d84dc4d9e425482637fc301f. --- lib/std/atomic_nolibc.c3 | 4 +- lib/std/collections/anylist.c3 | 548 ++---------------- lib/std/collections/interfacelist.c3 | 539 +++++++++++++++++ lib/std/core/allocators/dynamic_arena.c3 | 2 +- lib/std/core/allocators/tracking_allocator.c3 | 4 +- lib/std/core/array.c3 | 4 +- lib/std/core/builtin.c3 | 2 +- lib/std/core/types.c3 | 4 +- lib/std/crypto/ed25519.c3 | 10 +- lib/std/io/file_mmap.c3 | 2 +- lib/std/io/stream/bytebuffer.c3 | 4 +- lib/std/libc/libc.c3 | 2 +- lib/std/libc/os/posix.c3 | 2 +- lib/std/math/math.c3 | 4 +- lib/std/net/os/darwin.c3 | 4 +- lib/std/os/subprocess.c3 | 2 +- lib/std/os/win32/memoryapi.c3 | 2 +- lib/std/os/win32/types.c3 | 2 +- lib/std/sort/quicksort.c3 | 4 +- releasenotes.md | 29 +- resources/grammar/grammar.y | 98 ++-- resources/grammar/grammar_proposal.y | 8 +- src/build/build_options.c | 4 +- src/build/libraries.c | 2 +- src/build/project.c | 2 +- src/compiler/compiler.c | 3 +- src/compiler/compiler_internal.h | 42 +- src/compiler/copying.c | 8 + src/compiler/llvm_codegen.c | 2 +- src/compiler/llvm_codegen_expr.c | 2 +- src/compiler/llvm_codegen_stmt.c | 2 +- src/compiler/parse_expr.c | 4 +- src/compiler/parse_global.c | 6 +- src/compiler/parser_internal.h | 2 +- src/compiler/sema_asm.c | 12 +- src/compiler/sema_builtins.c | 2 +- src/compiler/sema_casts.c | 2 +- src/compiler/sema_const.c | 2 +- src/compiler/sema_decls.c | 20 +- src/compiler/sema_expr.c | 46 +- src/compiler/sema_stmts.c | 22 +- src/compiler/semantic_analyser.c | 11 + src/compiler_tests/tests.c | 226 ++++---- src/utils/lib.h | 2 +- src/utils/stringutils.c | 2 +- src/version.h | 4 +- test/src/test_suite_runner.c3 | 2 +- ...ay.c3 => cast_string_to_inferred_array.c3} | 0 .../defined_subscript_assign.c3 | 14 + ...k_implicit_conversion_signed_unsigned.c3t} | 0 .../expressions/fail_index_usize.c3 | 2 +- ...nts.c3t => deferred_default_arguments.c3t} | 0 test/test_suite/functions/slice_escape.c3 | 16 + .../test_suite/methods/subscript_set_error.c3 | 14 + .../{fallthough_do.c3t => fallthrough_do.c3t} | 0 test/test_suite/struct/member_expr.c3 | 4 +- test/test_suite/struct/struct_recursive.c3 | 4 +- .../switch/switch_in_defer_macro.c3t | 6 +- test/test_suite/variables/noinit_with_val.c3 | 10 + test/unit/stdlib/collections/anylist.c3 | 28 + test/unit/stdlib/collections/interfacelist.c3 | 126 ++++ test/unit/stdlib/core/string.c3 | 4 +- test/unit/stdlib/libc/libc.c3 | 4 +- 63 files changed, 1148 insertions(+), 796 deletions(-) create mode 100644 lib/std/collections/interfacelist.c3 rename test/test_suite/cast/{cast_string_to_infered_array.c3 => cast_string_to_inferred_array.c3} (100%) create mode 100644 test/test_suite/compile_time_introspection/defined_subscript_assign.c3 rename test/test_suite/expressions/{check_implict_conversion_signed_unsigned.c3t => check_implicit_conversion_signed_unsigned.c3t} (100%) rename test/test_suite/functions/{defered_default_arguments.c3t => deferred_default_arguments.c3t} (100%) create mode 100644 test/test_suite/functions/slice_escape.c3 create mode 100644 test/test_suite/methods/subscript_set_error.c3 rename test/test_suite/statements/{fallthough_do.c3t => fallthrough_do.c3t} (100%) create mode 100644 test/test_suite/variables/noinit_with_val.c3 create mode 100644 test/unit/stdlib/collections/anylist.c3 create mode 100644 test/unit/stdlib/collections/interfacelist.c3 diff --git a/lib/std/atomic_nolibc.c3 b/lib/std/atomic_nolibc.c3 index cdac3199e..5c0fde4ac 100644 --- a/lib/std/atomic_nolibc.c3 +++ b/lib/std/atomic_nolibc.c3 @@ -58,7 +58,7 @@ fn CInt __atomic_compare_exchange(CInt size, any ptr, any expected, any desired, nextcase; $endif default: - unreachable("Unsuported size (%d) for atomic_compare_exchange", size); + unreachable("Unsupported size (%d) for atomic_compare_exchange", size); } return 0; -} \ No newline at end of file +} diff --git a/lib/std/collections/anylist.c3 b/lib/std/collections/anylist.c3 index e929e07a6..f497cf59d 100644 --- a/lib/std/collections/anylist.c3 +++ b/lib/std/collections/anylist.c3 @@ -2,10 +2,10 @@ // Use of self source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. module std::collections::anylist; -import std::io,std::math; +import std::collections::interfacelist; -alias AnyPredicate = fn bool(any value); -alias AnyTest = fn bool(any type, any context); +alias AnyPredicate = InterfacePredicate {any}; +alias AnyTest = InterfaceTest {any}; <* The AnyList contains a heterogenous set of types. Anything placed in the @@ -18,282 +18,7 @@ alias AnyTest = fn bool(any type, any context); If we're not doing pop, then things are easier, since we can just hand over the existing any. *> -struct AnyList (Printable) -{ - usz size; - usz capacity; - Allocator allocator; - any* entries; -} - - -<* - Initialize the list. If not initialized then it will use the temp allocator - when something is pushed to it. - - @param [&inout] allocator : "The allocator to use" - @param initial_capacity : "The initial capacity to reserve, defaults to 16" -*> -fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16) -{ - self.allocator = allocator; - self.size = 0; - if (initial_capacity > 0) - { - initial_capacity = math::next_power_of_2(initial_capacity); - self.entries = allocator::alloc_array(allocator, any, initial_capacity); - } - else - { - self.entries = null; - } - self.capacity = initial_capacity; - return self; -} - -<* - Initialize the list using the temp allocator. - - @param initial_capacity : "The initial capacity to reserve" -*> -fn AnyList* AnyList.tinit(&self, usz initial_capacity = 16) -{ - return self.init(tmem, initial_capacity) @inline; -} - -fn bool AnyList.is_initialized(&self) @inline => self.allocator != null; - -<* - Push an element on the list by cloning it. -*> -macro void AnyList.push(&self, element) -{ - if (!self.allocator) self.allocator = tmem; - self._append(allocator::clone(self.allocator, element)); -} - -<* - Free a retained element removed using *_retained. -*> -fn void AnyList.free_element(&self, any element) @inline -{ - allocator::free(self.allocator, element.ptr); -} - -<* - Pop a value who's type is known. If the type is incorrect, this - will still pop the element. - - @param $Type : "The type we assume the value has" - @return "The last value as the type given" - @return? TYPE_MISMATCH, NO_MORE_ELEMENT -*> -macro AnyList.pop(&self, $Type) -{ - if (!self.size) return NO_MORE_ELEMENT?; - defer self.free_element(self.entries[self.size]); - return *anycast(self.entries[--self.size], $Type); -} - -<* - Copy the last value, pop it and return the copy of it. - - @param [&inout] allocator : "The allocator to use for copying" - @return "A copy of the last value if it exists" - @return? NO_MORE_ELEMENT -*> -fn any? AnyList.copy_pop(&self, Allocator allocator) -{ - if (!self.size) return NO_MORE_ELEMENT?; - defer self.free_element(self.entries[self.size]); - return allocator::clone_any(allocator, self.entries[--self.size]); -} - - -<* - Copy the last value, pop it and return the copy of it. - - @return "A temp copy of the last value if it exists" - @return? NO_MORE_ELEMENT -*> -fn any? AnyList.tcopy_pop(&self) => self.copy_pop(tmem); - - -<* - Pop the last value. It must later be released using `list.free_element()`. - - @return "The last value if it exists" - @return? NO_MORE_ELEMENT -*> -fn any? AnyList.pop_retained(&self) -{ - if (!self.size) return NO_MORE_ELEMENT?; - return self.entries[--self.size]; -} - -<* - Remove all elements in the list. -*> -fn void AnyList.clear(&self) -{ - for (usz i = 0; i < self.size; i++) - { - self.free_element(self.entries[i]); - } - self.size = 0; -} - -<* - Pop a value who's type is known. If the type is incorrect, this - will still pop the element. - - @param $Type : "The type we assume the value has" - @return "The first value as the type given" - @return? TYPE_MISMATCH, NO_MORE_ELEMENT -*> -macro AnyList.pop_first(&self, $Type) -{ - if (!self.size) return NO_MORE_ELEMENT?; - defer self.remove_at(0); - return *anycast(self.entries[0], $Type); -} - -<* - Pop the first value. It must later be released using `list.free_element()`. - - @return "The first value if it exists" - @return? NO_MORE_ELEMENT -*> -fn any? AnyList.pop_first_retained(&self) -{ - if (!self.size) return NO_MORE_ELEMENT?; - defer self.remove_at(0); - return self.entries[0]; -} - - -<* - Copy the first value, pop it and return the copy of it. - - @param [&inout] allocator : "The allocator to use for copying" - @return "A copy of the first value if it exists" - @return? NO_MORE_ELEMENT -*> -fn any? AnyList.copy_pop_first(&self, Allocator allocator) -{ - if (!self.size) return NO_MORE_ELEMENT?; - defer self.free_element(self.entries[self.size]); - defer self.remove_at(0); - return allocator::clone_any(allocator, self.entries[0]); -} - -<* - Copy the first value, pop it and return the temp copy of it. - - @return "A temp copy of the first value if it exists" - @return? NO_MORE_ELEMENT -*> -fn any? AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem); - -<* - Remove the element at the particular index. - - @param index : "The index of the element to remove" - @require index < self.size -*> -fn void AnyList.remove_at(&self, usz index) -{ - if (!--self.size || index == self.size) return; - self.free_element(self.entries[index]); - self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size]; -} - -<* - Add all the elements in another AnyList. - - @param [&in] other_list : "The list to add" -*> -fn void AnyList.add_all(&self, AnyList* other_list) -{ - if (!other_list.size) return; - self.reserve(other_list.size); - foreach (value : other_list) - { - self.entries[self.size++] = allocator::clone_any(self.allocator, value); - } -} - -<* - Reverse the order of the elements in the list. -*> -fn void AnyList.reverse(&self) -{ - if (self.size < 2) return; - usz half = self.size / 2U; - usz end = self.size - 1; - for (usz i = 0; i < half; i++) - { - self.swap(i, end - i); - } -} - -<* - Return a view of the data as a slice. - - @return "The slice view" -*> -fn any[] AnyList.array_view(&self) -{ - return self.entries[:self.size]; -} - -<* - Push an element to the front of the list. - - @param value : "The value to push to the list" -*> -macro void AnyList.push_front(&self, value) -{ - self.insert_at(0, value); -} - -<* - Insert an element at a particular index. - - @param index : "the index where the element should be inserted" - @param type : "the value to insert" - @require index <= self.size : "The index is out of bounds" -*> -macro void AnyList.insert_at(&self, usz index, type) -{ - if (index == self.size) - { - self.push(type); - return; - } - any value = allocator::copy(self.allocator, type); - self._insert_at(self, index, value); -} - -<* - Remove the last element in the list. The list may not be empty. - - @require self.size > 0 : "The list was already empty" -*> -fn void AnyList.remove_last(&self) -{ - self.free_element(self.entries[--self.size]); -} - -<* - Remove the first element in the list, the list may not be empty. - - @require self.size > 0 -*> -fn void AnyList.remove_first(&self) -{ - self.remove_at(0); -} +typedef AnyList = inline InterfaceList {any}; <* Return the first element by value, assuming it is the given type. @@ -313,10 +38,7 @@ macro AnyList.first(&self, $Type) @return "The first element" @return? NO_MORE_ELEMENT *> -fn any? AnyList.first_any(&self) @inline -{ - return self.size ? self.entries[0] : NO_MORE_ELEMENT?; -} +fn any? AnyList.first_any(&self) @inline => InterfaceList {any}.first(self); <* Return the last element by value, assuming it is the given type. @@ -336,29 +58,36 @@ macro AnyList.last(&self, $Type) @return "The last element" @return? NO_MORE_ELEMENT *> -fn any? AnyList.last_any(&self) @inline +fn any? AnyList.last_any(&self) @inline => InterfaceList {any}.last(self); + +<* + Pop a value who's type is known. If the type is incorrect, this + will still pop the element. + + @param $Type : "The type we assume the value has" + @return "The last value as the type given" + @return? TYPE_MISMATCH, NO_MORE_ELEMENT +*> +macro AnyList.pop(&self, $Type) { - return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?; + if (!self.size) return NO_MORE_ELEMENT?; + defer self.free_element(self.entries[self.size]); + return *anycast(self.entries[--self.size], $Type); } <* - Return whether the list is empty. + Pop a value who's type is known. If the type is incorrect, this + will still pop the element. - @return "True if the list is empty" + @param $Type : "The type we assume the value has" + @return "The first value as the type given" + @return? TYPE_MISMATCH, NO_MORE_ELEMENT *> -fn bool AnyList.is_empty(&self) @inline +macro AnyList.pop_first(&self, $Type) { - return !self.size; -} - -<* - Return the length of the list. - - @return "The number of elements in the list" -*> -fn usz AnyList.len(&self) @operator(len) @inline -{ - return self.size; + if (!self.size) return NO_MORE_ELEMENT?; + defer self.remove_at(0); + return *anycast(self.entries[0], $Type); } <* @@ -383,222 +112,11 @@ macro AnyList.get(&self, usz index, $Type) @return? TYPE_MISMATCH, NO_MORE_ELEMENT @require index < self.size : "Index out of range" *> -fn any AnyList.get_any(&self, usz index) @inline @operator([]) -{ - return self.entries[index]; -} +fn any AnyList.get_any(&self, usz index) @inline @operator([]) => InterfaceList {any}.get(self, index); <* - Completely free and clear a list. + Return the length of the list. + + @return "The number of elements in the list" *> -fn void AnyList.free(&self) -{ - if (!self.allocator) return; - self.clear(); - allocator::free(self.allocator, self.entries); - self.capacity = 0; - self.entries = null; -} - -<* - Swap two elements in a list. - - @param i : "Index of one of the elements" - @param j : "Index of the other element" - @require i < self.size : "The first index is out of range" - @require j < self.size : "The second index is out of range" -*> -fn void AnyList.swap(&self, usz i, usz j) -{ - any temp = self.entries[i]; - self.entries[i] = self.entries[j]; - self.entries[j] = temp; -} - -<* - Print the list to a formatter. -*> -fn usz? AnyList.to_format(&self, Formatter* formatter) @dynamic -{ - switch (self.size) - { - case 0: - return formatter.print("[]")!; - case 1: - return formatter.printf("[%s]", self.entries[0])!; - default: - usz n = formatter.print("[")!; - foreach (i, element : self.entries[:self.size]) - { - if (i != 0) formatter.print(", ")!; - n += formatter.printf("%s", element)!; - } - n += formatter.print("]")!; - return n; - } -} - -<* - Remove any elements matching the predicate. - - @param filter : "The function to determine if it should be removed or not" - @return "the number of deleted elements" -*> -fn usz AnyList.remove_if(&self, AnyPredicate filter) -{ - return self._remove_if(filter, false); -} - -<* - Retain the elements matching the predicate. - - @param selection : "The function to determine if it should be kept or not" - @return "the number of deleted elements" -*> -fn usz AnyList.retain_if(&self, AnyPredicate selection) -{ - return self._remove_if(selection, true); -} - -<* - Remove any elements matching the predicate. - - @param filter : "The function to determine if it should be removed or not" - @param context : "The context to the function" - @return "the number of deleted elements" -*> -fn usz AnyList.remove_using_test(&self, AnyTest filter, any context) -{ - return self._remove_using_test(filter, false, context); -} - -<* - Retain any elements matching the predicate. - - @param selection : "The function to determine if it should be retained or not" - @param context : "The context to the function" - @return "the number of deleted elements" -*> -fn usz AnyList.retain_using_test(&self, AnyTest selection, any context) -{ - return self._remove_using_test(selection, true, context); -} - - -<* - Reserve memory so that at least the `min_capacity` exists. - - @param min_capacity : "The min capacity to hold" -*> -fn void AnyList.reserve(&self, usz min_capacity) -{ - if (!min_capacity) return; - if (self.capacity >= min_capacity) return; - if (!self.allocator) self.allocator = tmem; - min_capacity = math::next_power_of_2(min_capacity); - self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity); - self.capacity = min_capacity; -} - -<* - Set the element at any index. - - @param index : "The index where to set the value." - @param value : "The value to set" - @require index <= self.size : "Index out of range" -*> -macro void AnyList.set(&self, usz index, value) -{ - if (index == self.size) - { - self.push(value); - return; - } - self.free_element(self.entries[index]); - self.entries[index] = allocator::copy(self.allocator, value); -} - -// -- private - -fn void AnyList.ensure_capacity(&self, usz added = 1) @inline @private -{ - usz new_size = self.size + added; - if (self.capacity >= new_size) return; - - assert(new_size < usz.max / 2U); - usz new_capacity = self.capacity ? 2U * self.capacity : 16U; - while (new_capacity < new_size) new_capacity *= 2U; - self.reserve(new_capacity); -} - -fn void AnyList._append(&self, any element) @local -{ - self.ensure_capacity(); - self.entries[self.size++] = element; -} - -<* - @require index < self.size -*> -fn void AnyList._insert_at(&self, usz index, any value) @local -{ - self.ensure_capacity(); - for (usz i = self.size; i > index; i--) - { - self.entries[i] = self.entries[i - 1]; - } - self.size++; - self.entries[index] = value; -} - -macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @local -{ - usz size = self.size; - for (usz i = size, usz k = size; k > 0; k = i) - { - // Find last index of item to be deleted. - $if $invert: - while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--; - $else - while (i > 0 && filter(&self.entries[i - 1], ctx)) i--; - $endif - // Remove the items from this index up to the one not to be deleted. - usz n = self.size - k; - for (usz j = i; j < k; j++) self.free_element(self.entries[j]); - self.entries[i:n] = self.entries[k:n]; - self.size -= k - i; - // Find last index of item not to be deleted. - $if $invert: - while (i > 0 && filter(&self.entries[i - 1], ctx)) i--; - $else - while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--; - $endif - } - return size - self.size; -} - -macro usz AnyList._remove_if(&self, AnyPredicate filter, bool $invert) @local -{ - usz size = self.size; - for (usz i = size, usz k = size; k > 0; k = i) - { - // Find last index of item to be deleted. - $if $invert: - while (i > 0 && !filter(&self.entries[i - 1])) i--; - $else - while (i > 0 && filter(&self.entries[i - 1])) i--; - $endif - // Remove the items from this index up to the one not to be deleted. - usz n = self.size - k; - for (usz j = i; j < k; j++) self.free_element(self.entries[j]); - self.entries[i:n] = self.entries[k:n]; - self.size -= k - i; - // Find last index of item not to be deleted. - $if $invert: - while (i > 0 && filter(&self.entries[i - 1])) i--; - $else - while (i > 0 && !filter(&self.entries[i - 1])) i--; - $endif - } - return size - self.size; -} +fn usz AnyList.len(&self) @operator(len) @inline => InterfaceList {any}.len(self); diff --git a/lib/std/collections/interfacelist.c3 b/lib/std/collections/interfacelist.c3 new file mode 100644 index 000000000..ffd51bd73 --- /dev/null +++ b/lib/std/collections/interfacelist.c3 @@ -0,0 +1,539 @@ +// Copyright (c) 2024-2025 Christoffer Lerno. All rights reserved. +// Use of self source code is governed by the MIT license +// a copy of which can be found in the LICENSE_STDLIB file. +<* + @require Type.kindof == INTERFACE || Type.kindof == ANY : "The kind of an interfacelist must be an interface or `any`" +*> +module std::collections::interfacelist {Type}; +import std::io,std::math; + +alias InterfacePredicate = fn bool(Type value); +alias InterfaceTest = fn bool(Type type, Type context); + +<* + The InterfaceList contains a heterogenous set of types implementing an interface. anything placed in the + list will shallowly copied in order to be stored as the interface. This means + that the list will copy and free its elements. + + However, because we're getting interface values back when we pop, those operations + need to take an allocator, as we can only copy then pop then return the copy. + + If we're not doing pop, then things are easier, since we can just hand over + the existing value. +*> +struct InterfaceList (Printable) +{ + usz size; + usz capacity; + Allocator allocator; + Type* entries; +} + + +<* + Initialize the list. If not initialized then it will use the temp allocator + when something is pushed to it. + + @param [&inout] allocator : "The allocator to use" + @param initial_capacity : "The initial capacity to reserve, defaults to 16" +*> +fn InterfaceList* InterfaceList.init(&self, Allocator allocator, usz initial_capacity = 16) +{ + self.allocator = allocator; + self.size = 0; + if (initial_capacity > 0) + { + initial_capacity = math::next_power_of_2(initial_capacity); + self.entries = allocator::alloc_array(allocator, Type, initial_capacity); + } + else + { + self.entries = null; + } + self.capacity = initial_capacity; + return self; +} + +<* + Initialize the list using the temp allocator. + + @param initial_capacity : "The initial capacity to reserve" +*> +fn InterfaceList* InterfaceList.tinit(&self, usz initial_capacity = 16) +{ + return self.init(tmem, initial_capacity) @inline; +} + +fn bool InterfaceList.is_initialized(&self) @inline => self.allocator != null; + +<* + Push an element on the list by cloning it. + @require $defined(Type t = &element) : "Element must implement the interface" +*> +macro void InterfaceList.push(&self, element) +{ + if (!self.allocator) self.allocator = tmem; + self._append(allocator::clone(self.allocator, element)); +} + +<* + Free a retained element removed using *_retained. +*> +fn void InterfaceList.free_element(&self, Type element) @inline +{ + allocator::free(self.allocator, element.ptr); +} + +<* + Copy the last value, pop it and return the copy of it. + + @param [&inout] allocator : "The allocator to use for copying" + @return "A copy of the last value if it exists" + @return? NO_MORE_ELEMENT +*> +fn Type? InterfaceList.copy_pop(&self, Allocator allocator) +{ + if (!self.size) return NO_MORE_ELEMENT?; + defer self.free_element(self.entries[self.size]); + return (Type)allocator::clone_any(allocator, self.entries[--self.size]); +} + +<* + Copy the last value, pop it and return the copy of it. + + @return "A temp copy of the last value if it exists" + @return? NO_MORE_ELEMENT +*> +fn Type? InterfaceList.tcopy_pop(&self) => self.copy_pop(tmem); + +<* + Pop the last value. It must later be released using `list.free_element()`. + + @return "The last value if it exists" + @return? NO_MORE_ELEMENT +*> +fn Type? InterfaceList.pop_retained(&self) +{ + if (!self.size) return NO_MORE_ELEMENT?; + return self.entries[--self.size]; +} + +<* + Remove all elements in the list. +*> +fn void InterfaceList.clear(&self) +{ + for (usz i = 0; i < self.size; i++) + { + self.free_element(self.entries[i]); + } + self.size = 0; +} + +<* + Pop the first value. It must later be released using `list.free_element()`. + + @return "The first value if it exists" + @return? NO_MORE_ELEMENT +*> +fn Type? InterfaceList.pop_first_retained(&self) +{ + if (!self.size) return NO_MORE_ELEMENT?; + defer self.remove_at(0); + return self.entries[0]; +} + +<* + Copy the first value, pop it and return the copy of it. + + @param [&inout] allocator : "The allocator to use for copying" + @return "A copy of the first value if it exists" + @return? NO_MORE_ELEMENT +*> +fn Type? InterfaceList.copy_pop_first(&self, Allocator allocator) +{ + if (!self.size) return NO_MORE_ELEMENT?; + defer self.free_element(self.entries[self.size]); + defer self.remove_at(0); + return (Type)allocator::clone_any(allocator, self.entries[0]); +} + +<* + Copy the first value, pop it and return the temp copy of it. + + @return "A temp copy of the first value if it exists" + @return? NO_MORE_ELEMENT +*> +fn Type? InterfaceList.tcopy_pop_first(&self) => self.copy_pop_first(tmem); + +<* + Remove the element at the particular index. + + @param index : "The index of the element to remove" + @require index < self.size +*> +fn void InterfaceList.remove_at(&self, usz index) +{ + if (!--self.size || index == self.size) return; + self.free_element(self.entries[index]); + self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size]; +} + +<* + Add all the elements in another InterfaceList. + + @param [&in] other_list : "The list to add" +*> +fn void InterfaceList.add_all(&self, InterfaceList* other_list) +{ + if (!other_list.size) return; + self.reserve(other_list.size); + foreach (value : other_list) + { + self.entries[self.size++] = (Type)allocator::clone_any(self.allocator, value); + } +} + +<* + Reverse the order of the elements in the list. +*> +fn void InterfaceList.reverse(&self) +{ + if (self.size < 2) return; + usz half = self.size / 2U; + usz end = self.size - 1; + for (usz i = 0; i < half; i++) + { + self.swap(i, end - i); + } +} + +<* + Return a view of the data as a slice. + + @return "The slice view" +*> +fn Type[] InterfaceList.array_view(&self) +{ + return self.entries[:self.size]; +} + +<* + Push an element to the front of the list. + + @param value : "The value to push to the list" + @require $defined(Type t = &value) : "Value must implement the interface" +*> +macro void InterfaceList.push_front(&self, value) +{ + self.insert_at(0, value); +} + +<* + Insert an element at a particular index. + + @param index : "the index where the element should be inserted" + @param type : "the value to insert" + @require index <= self.size : "The index is out of bounds" + @require $defined(Type t = &type) : "Type must implement the interface" +*> +macro void InterfaceList.insert_at(&self, usz index, type) +{ + if (index == self.size) + { + self.push(type); + return; + } + Type value = allocator::clone(self.allocator, type); + self._insert_at(self, index, value); +} + +<* + Remove the last element in the list. The list may not be empty. + + @require self.size > 0 : "The list was already empty" +*> +fn void InterfaceList.remove_last(&self) +{ + self.free_element(self.entries[--self.size]); +} + +<* + Remove the first element in the list, the list may not be empty. + + @require self.size > 0 +*> +fn void InterfaceList.remove_first(&self) +{ + self.remove_at(0); +} + +<* + Return the first element + + @return "The first element" + @return? NO_MORE_ELEMENT +*> +fn Type? InterfaceList.first(&self) @inline +{ + return self.size ? self.entries[0] : NO_MORE_ELEMENT?; +} + +<* + Return the last element + + @return "The last element" + @return? NO_MORE_ELEMENT +*> +fn Type? InterfaceList.last(&self) @inline +{ + return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?; +} + +<* + Return whether the list is empty. + + @return "True if the list is empty" +*> +fn bool InterfaceList.is_empty(&self) @inline +{ + return !self.size; +} + +<* + Return the length of the list. + + @return "The number of elements in the list" +*> +fn usz InterfaceList.len(&self) @operator(len) @inline +{ + return self.size; +} + +<* + Return an element in the list. + + @param index : "The index of the element to retrieve" + @return "The element at the index" + @return? TYPE_MISMATCH, NO_MORE_ELEMENT + @require index < self.size : "Index out of range" +*> +fn Type InterfaceList.get(&self, usz index) @inline @operator([]) +{ + return self.entries[index]; +} + +<* + Completely free and clear a list. +*> +fn void InterfaceList.free(&self) +{ + if (!self.allocator) return; + self.clear(); + allocator::free(self.allocator, self.entries); + self.capacity = 0; + self.entries = null; +} + +<* + Swap two elements in a list. + + @param i : "Index of one of the elements" + @param j : "Index of the other element" + @require i < self.size : "The first index is out of range" + @require j < self.size : "The second index is out of range" +*> +fn void InterfaceList.swap(&self, usz i, usz j) +{ + Type temp = self.entries[i]; + self.entries[i] = self.entries[j]; + self.entries[j] = temp; +} + +<* + Print the list to a formatter. +*> +fn usz? InterfaceList.to_format(&self, Formatter* formatter) @dynamic +{ + switch (self.size) + { + case 0: + return formatter.print("[]")!; + case 1: + return formatter.printf("[%s]", self.entries[0])!; + default: + usz n = formatter.print("[")!; + foreach (i, element : self.entries[:self.size]) + { + if (i != 0) formatter.print(", ")!; + n += formatter.printf("%s", element)!; + } + n += formatter.print("]")!; + return n; + } +} + +<* + Remove Type elements matching the predicate. + + @param filter : "The function to determine if it should be removed or not" + @return "the number of deleted elements" +*> +fn usz InterfaceList.remove_if(&self, InterfacePredicate filter) +{ + return self._remove_if(filter, false); +} + +<* + Retain the elements matching the predicate. + + @param selection : "The function to determine if it should be kept or not" + @return "the number of deleted elements" +*> +fn usz InterfaceList.retain_if(&self, InterfacePredicate selection) +{ + return self._remove_if(selection, true); +} + +<* + Remove Type elements matching the predicate. + + @param filter : "The function to determine if it should be removed or not" + @param context : "The context to the function" + @return "the number of deleted elements" +*> +fn usz InterfaceList.remove_using_test(&self, InterfaceTest filter, Type context) +{ + return self._remove_using_test(filter, false, context); +} + +<* + Retain Type elements matching the predicate. + + @param selection : "The function to determine if it should be retained or not" + @param context : "The context to the function" + @return "the number of deleted elements" +*> +fn usz InterfaceList.retain_using_test(&self, InterfaceTest selection, Type context) +{ + return self._remove_using_test(selection, true, context); +} + +<* + Reserve memory so that at least the `min_capacity` exists. + + @param min_capacity : "The min capacity to hold" +*> +fn void InterfaceList.reserve(&self, usz min_capacity) +{ + if (!min_capacity) return; + if (self.capacity >= min_capacity) return; + if (!self.allocator) self.allocator = tmem; + min_capacity = math::next_power_of_2(min_capacity); + self.entries = allocator::realloc(self.allocator, self.entries, Type.sizeof * min_capacity); + self.capacity = min_capacity; +} + +<* + Set the element at Type index. + + @param index : "The index where to set the value." + @param value : "The value to set" + @require index <= self.size : "Index out of range" + @require $defined(Type t = &value) : "Value must implement the interface" +*> +macro void InterfaceList.set(&self, usz index, value) +{ + if (index == self.size) + { + self.push(value); + return; + } + self.free_element(self.entries[index]); + self.entries[index] = allocator::clone(self.allocator, value); +} + +// -- private + +fn void InterfaceList.ensure_capacity(&self, usz added = 1) @inline @private +{ + usz new_size = self.size + added; + if (self.capacity >= new_size) return; + + assert(new_size < usz.max / 2U); + usz new_capacity = self.capacity ? 2U * self.capacity : 16U; + while (new_capacity < new_size) new_capacity *= 2U; + self.reserve(new_capacity); +} + +fn void InterfaceList._append(&self, Type element) @local +{ + self.ensure_capacity(); + self.entries[self.size++] = element; +} + +<* + @require index < self.size +*> +fn void InterfaceList._insert_at(&self, usz index, Type value) @local +{ + self.ensure_capacity(); + for (usz i = self.size; i > index; i--) + { + self.entries[i] = self.entries[i - 1]; + } + self.size++; + self.entries[index] = value; +} + +macro usz InterfaceList._remove_using_test(&self, InterfaceTest filter, bool $invert, ctx) @local +{ + usz size = self.size; + for (usz i = size, usz k = size; k > 0; k = i) + { + // Find last index of item to be deleted. + $if $invert: + while (i > 0 && !filter(self.entries[i - 1], ctx)) i--; + $else + while (i > 0 && filter(self.entries[i - 1], ctx)) i--; + $endif + // Remove the items from this index up to the one not to be deleted. + usz n = self.size - k; + for (usz j = i; j < k; j++) self.free_element(self.entries[j]); + self.entries[i:n] = self.entries[k:n]; + self.size -= k - i; + // Find last index of item not to be deleted. + $if $invert: + while (i > 0 && filter(self.entries[i - 1], ctx)) i--; + $else + while (i > 0 && !filter(self.entries[i - 1], ctx)) i--; + $endif + } + return size - self.size; +} + +macro usz InterfaceList._remove_if(&self, InterfacePredicate filter, bool $invert) @local +{ + usz size = self.size; + for (usz i = size, usz k = size; k > 0; k = i) + { + // Find last index of item to be deleted. + $if $invert: + while (i > 0 && !filter(self.entries[i - 1])) i--; + $else + while (i > 0 && filter(self.entries[i - 1])) i--; + $endif + // Remove the items from this index up to the one not to be deleted. + usz n = self.size - k; + for (usz j = i; j < k; j++) self.free_element(self.entries[j]); + self.entries[i:n] = self.entries[k:n]; + self.size -= k - i; + // Find last index of item not to be deleted. + $if $invert: + while (i > 0 && filter(self.entries[i - 1])) i--; + $else + while (i > 0 && !filter(self.entries[i - 1])) i--; + $endif + } + return size - self.size; +} diff --git a/lib/std/core/allocators/dynamic_arena.c3 b/lib/std/core/allocators/dynamic_arena.c3 index bb29a9631..5faf3f7a7 100644 --- a/lib/std/core/allocators/dynamic_arena.c3 +++ b/lib/std/core/allocators/dynamic_arena.c3 @@ -13,7 +13,7 @@ import std::math; The advantage over the BackedArenaAllocator, is that when allocating beyond the first "page", it will retain the characteristics of an arena allocator (allocating a large piece of memory then handing off - memory from that memory), wheras the BackedArenaAllocator will have heap allocator characteristics. + memory from that memory), whereas the BackedArenaAllocator will have heap allocator characteristics. *> struct DynamicArenaAllocator (Allocator) { diff --git a/lib/std/core/allocators/tracking_allocator.c3 b/lib/std/core/allocators/tracking_allocator.c3 index 3fe0cb77b..00ce4d90a 100644 --- a/lib/std/core/allocators/tracking_allocator.c3 +++ b/lib/std/core/allocators/tracking_allocator.c3 @@ -19,7 +19,7 @@ alias AllocMap = HashMap { uptr, Allocation }; // It tracks allocations using a hash map but // is not compatible with allocators that uses mark() // -// It is also embarassingly single-threaded, so +// It is also embarrassingly single-threaded, so // do not use it to track allocations that cross threads. struct TrackingAllocator (Allocator) @@ -216,4 +216,4 @@ fn void? TrackingAllocator.fprint_report(&self, OutStream out) => @pool() } } } -} \ No newline at end of file +} diff --git a/lib/std/core/array.c3 b/lib/std/core/array.c3 index fbd6cba0b..4412019bd 100644 --- a/lib/std/core/array.c3 +++ b/lib/std/core/array.c3 @@ -142,7 +142,7 @@ macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2); @param [in] array @param identity - @param #operation : "The reduction/folding labmda function or function pointer to apply." + @param #operation : "The reduction/folding lambda function or function pointer to apply." @require @is_valid_list(array) : "Expected a valid list" @require $defined($typefrom(@reduce_fn(array, identity)) $func = #operation) : "Invalid lambda or function pointer type" @@ -554,4 +554,4 @@ macro bool @is_valid_fill(left, right, fill_with = ...) $endif } -macro usz find_len(list) => $defined(list.len()) ??? list.len() : list.len; \ No newline at end of file +macro usz find_len(list) => $defined(list.len()) ??? list.len() : list.len; diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index 473c034f6..553e47ab5 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -7,7 +7,7 @@ import libc, std::hash, std::io, std::os::backtrace; <* EMPTY_MACRO_SLOT is a value used for implementing optional arguments for macros in an efficient - way. It relies on the fact that distinct types are not implicitly convertable. + way. It relies on the fact that distinct types are not implicitly convertible. You can use `@is_empty_macro_slot()` and `@is_valid_macro_slot()` to figure out whether the argument has been used or not. diff --git a/lib/std/core/types.c3 b/lib/std/core/types.c3 index 4fa7d595e..797cfc37f 100644 --- a/lib/std/core/types.c3 +++ b/lib/std/core/types.c3 @@ -115,7 +115,9 @@ fn bool TypeKind.is_int(kind) @inline return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT; } -macro bool is_slice_convertable($Type) +macro bool is_slice_convertable($Type) @deprecated("Use is_slice_convertible") => is_slice_convertible($Type); + +macro bool is_slice_convertible($Type) { $switch $Type.kindof: $case SLICE: diff --git a/lib/std/crypto/ed25519.c3 b/lib/std/crypto/ed25519.c3 index c4e9954b9..8af9093a5 100644 --- a/lib/std/crypto/ed25519.c3 +++ b/lib/std/crypto/ed25519.c3 @@ -365,7 +365,7 @@ fn void F25519Int.normalize(&s) { s.reduce_carry((*s)[^1] >> 7); - // Substract p + // Subtract p F25519Int sub @noinit; ushort c = 19; foreach (i, v : (*s)[:^1]) @@ -399,7 +399,7 @@ fn char eq(F25519Int* a, F25519Int* b) } <* - Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1. + Constant-time conditional selection. Result is undefined if condition is neither 0 nor 1. @param [&in] zero : "selected if condition is 0" @param [&in] one : "selected if condition is 1" @@ -441,7 +441,7 @@ fn F25519Int F25519Int.add(&s, F25519Int* n) @operator(+) macro F25519Int F25519Int.@sub(&s, F25519Int #n) @operator(-) => s.sub(@addr(#n)); <* - Substraction. + Subtraction. @param [&in] s @param [&in] n @@ -638,7 +638,7 @@ fn FBaseInt from_bytes(char[] bytes) } <* - Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1. + Constant-time conditional selection. Result is undefined if condition is neither 0 nor 1. @param [&in] zero : "selected if condition is 0" @param [&in] one : "selected if condition is 1" @@ -676,7 +676,7 @@ fn FBaseInt FBaseInt.add(&s, FBaseInt* n) @operator(+) } <* - Substraction if RHS is less than LHS else identity. + Subtraction if RHS is less than LHS else identity. @param [&in] s @param [&in] n diff --git a/lib/std/io/file_mmap.c3 b/lib/std/io/file_mmap.c3 index a50f346b0..630a4ebcc 100644 --- a/lib/std/io/file_mmap.c3 +++ b/lib/std/io/file_mmap.c3 @@ -20,7 +20,7 @@ fn char[] FileMmap.bytes(&self) } <* - Destroys the underlyng VirtualMemory object ie. calls munmap()" + Destroys the underlying VirtualMemory object ie. calls munmap()" *> fn void? FileMmap.destroy(&self) @maydiscard { diff --git a/lib/std/io/stream/bytebuffer.c3 b/lib/std/io/stream/bytebuffer.c3 index 3e086314d..8335e9eaa 100644 --- a/lib/std/io/stream/bytebuffer.c3 +++ b/lib/std/io/stream/bytebuffer.c3 @@ -13,7 +13,7 @@ struct ByteBuffer (InStream, OutStream) <* ByteBuffer provides a streamable read/write buffer. - max_read defines how many bytes might be kept before its internal buffer is shrinked. + max_read defines how many bytes might be kept before its internal buffer is shrunk. @require self.bytes.len == 0 : "Buffer already initialized." *> fn ByteBuffer* ByteBuffer.init(&self, Allocator allocator, usz max_read, usz initial_capacity = 16) @@ -145,4 +145,4 @@ macro ByteBuffer.shrink(&self) self.write_idx = 1 + readable; self.read_idx = 1; } -} \ No newline at end of file +} diff --git a/lib/std/libc/libc.c3 b/lib/std/libc/libc.c3 index 8ad3842e3..b4dfda6fc 100644 --- a/lib/std/libc/libc.c3 +++ b/lib/std/libc/libc.c3 @@ -438,7 +438,7 @@ alias TimeOffset @if(!env::WASI) = CLong ; const int TIME_UTC = 1; -// This is a best-effort aproximation, but the C standard does not enforce +// This is a best-effort approximation, but the C standard does not enforce // that this is a compile-time standard. const CLOCKS_PER_SEC @if(env::WIN32) = 1000; const CLOCKS_PER_SEC @if(!env::WIN32) = 1000000; diff --git a/lib/std/libc/os/posix.c3 b/lib/std/libc/os/posix.c3 index 31854a684..ef911768e 100644 --- a/lib/std/libc/os/posix.c3 +++ b/lib/std/libc/os/posix.c3 @@ -218,7 +218,7 @@ enum Speed : const CUInt MAX_BAUD = B4000000, } -enum Cc : const char +enum Cc : const inline char { VINTR = 0, VQUIT = 1, diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index 8ce850169..a6350cea3 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -252,8 +252,8 @@ macro @ceil($input) @const => $$ceil($input); @return "lower if x < lower, upper if x > upper, otherwise return x." @require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector` - @require $defined(x = lower) : `The lower bound must be convertable to the value type.` - @require $defined(x = upper) : `The upper bound must be convertable to the value type.` + @require $defined(x = lower) : `The lower bound must be convertible to the value type.` + @require $defined(x = upper) : `The upper bound must be convertible to the value type.` *> macro clamp(x, lower, upper) => $$max(($typeof(x))lower, $$min(x, ($typeof(x))upper)); diff --git a/lib/std/net/os/darwin.c3 b/lib/std/net/os/darwin.c3 index 9b74fb24b..3ef758836 100644 --- a/lib/std/net/os/darwin.c3 +++ b/lib/std/net/os/darwin.c3 @@ -67,7 +67,7 @@ const int SO_WANTMORE = 0x4000; // Apple: Give hint when more data re const int SO_WANTOOBFLAG = 0x8000; // Apple: Want OOB in MSG_FLAG on receive const int SO_SNDBUF = 0x1001; // Send buffer size -const int SO_RCVBUF = 0x1002; // Recieve buffer size +const int SO_RCVBUF = 0x1002; // Receive buffer size const int SO_SNDLOWAT = 0x1003; // Send low-water mark const int SO_RCVLOWAT = 0x1004; // Receive low-water mark const int SO_SNDTIMEO = 0x1005; // Send timeout @@ -94,4 +94,4 @@ const CShort POLLATTRIB = 0x0400; // file attributes may have changed const CShort POLLNLINK = 0x0800; // (un)link/rename may have happened const CShort POLLWRITE = 0x1000; // file's contents may have changed -const CInt MSG_PEEK = 0x0002; \ No newline at end of file +const CInt MSG_PEEK = 0x0002; diff --git a/lib/std/os/subprocess.c3 b/lib/std/os/subprocess.c3 index 774aa7855..a9be419c3 100644 --- a/lib/std/os/subprocess.c3 +++ b/lib/std/os/subprocess.c3 @@ -35,7 +35,7 @@ bitstruct SubProcessOptions : int { // Combine stdout and stderr to the same file bool combined_stdout_stderr; - // Child process should inhert env variables of parent process + // Child process should inherit env variables of parent process bool inherit_environment; // Enable async reading of stdout/stderr before completion bool read_async; diff --git a/lib/std/os/win32/memoryapi.c3 b/lib/std/os/win32/memoryapi.c3 index 8e5f9db04..176498844 100644 --- a/lib/std/os/win32/memoryapi.c3 +++ b/lib/std/os/win32/memoryapi.c3 @@ -36,7 +36,7 @@ enum Win32_FreeType : const Win32_DWORD MEM_COALESCE_PLACEHOLDERS = 0x00000001, MEM_PRESERVE_PLACEHOLDER = 0x00000002, } -extern fn Win32_LPVOID virtualAlloc(Win32_LPVOID lpAddres, Win32_SIZE_T dwSize, Win32_AllocationType flAllocationType, Win32_Protect flProtect) @extern("VirtualAlloc"); +extern fn Win32_LPVOID virtualAlloc(Win32_LPVOID lpAddress, Win32_SIZE_T dwSize, Win32_AllocationType flAllocationType, Win32_Protect flProtect) @extern("VirtualAlloc"); extern fn Win32_PVOID virtualAlloc2(Win32_HANDLE process, Win32_PVOID baseAddress, Win32_SIZE_T size, Win32_AllocationType allocationType, Win32_ULONG pageProtection, Win32_MEM_EXTENDED_PARAMETER* extendedParameters, Win32_ULONG parameterCount) @extern("VirtualAlloc2"); extern fn Win32_BOOL virtualFree(Win32_LPVOID lpAddress, Win32_SIZE_T dwSize, Win32_FreeType dwFreeType) @extern("VirtualFree"); extern fn Win32_BOOL virtualProtect(Win32_LPVOID lpAddress, Win32_SIZE_T dwSize, Win32_Protect flNewProtect, Win32_Protect* lpflOldProtect) @extern("VirtualProtect"); diff --git a/lib/std/os/win32/types.c3 b/lib/std/os/win32/types.c3 index 0fc54df29..595385ee3 100644 --- a/lib/std/os/win32/types.c3 +++ b/lib/std/os/win32/types.c3 @@ -432,7 +432,7 @@ struct Win32_XMM_SAVE_AREA32 Win32_UCHAR tagWord; Win32_UCHAR reserved1; Win32_USHORT errorOpcode; - Win32_ULONG errrorOffset; + Win32_ULONG errorOffset; Win32_USHORT errorSelector; Win32_USHORT reserved2; Win32_ULONG dataOffset; diff --git a/lib/std/sort/quicksort.c3 b/lib/std/sort/quicksort.c3 index d12e16440..3e326481e 100644 --- a/lib/std/sort/quicksort.c3 +++ b/lib/std/sort/quicksort.c3 @@ -82,8 +82,8 @@ fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context) } <* -@require low <= k : "kth smalles element is smaller than lower bounds" -@require k <= high : "kth smalles element is larger than upper bounds" +@require low <= k : "kth smallest element is smaller than lower bounds" +@require k <= high : "kth smallest element is larger than upper bounds" *> fn ElementType? qselect(Type list, isz low, isz high, isz k, CmpFn cmp, Context context) { diff --git a/releasenotes.md b/releasenotes.md index 6318ec34e..daee07764 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -1,5 +1,20 @@ # C3C Release Notes +## 0.7.6 Change list + +### Changes / improvements + +### Fixes +- Compiler assert with var x @noinit = 0 #2452 +- Confusing error message when type has [] overloaded but not []= #2453 +- $defined(x[0] = val) causes an error instead of returning false when a type does not have []= defined #2454 +- Returning pointer to index of slice stored in a struct from method taking self incorrectly detected as returning pointer to local variable #2455. +- Inlining location when accessing #foo symbols. +- Improve inlined-at when checking generic code. + +### Stdlib changes +- Added generic `InterfaceList` to store a list of values that implement a specific interface + ## 0.7.5 Change list ### Changes / improvements @@ -283,7 +298,7 @@ - Linker errors when shadowing @local with public function #2198 - Bug when offsetting pointers of large structs using ++ and --. - `x++` and `x--` works on pointer vectors #2222. -- `x += 1` and `x -= 1` works propertly on pointer vectors #2222. +- `x += 1` and `x -= 1` works properly on pointer vectors #2222. - Fixes to `x += { 1, 1 }` for enum and pointer vectors #2222. - Linking fails on operator method imported as `@public` #2224. - Lambda C-style vaargs were not properly rejected, leading to crash #2229. @@ -410,7 +425,7 @@ - Regression with invalid setup of the WASM temp allocator. - Correctly detect multiple overloads of the same type. - ABI bug on x64 Linux / MacOS when passing a union containing a struct of 3 floats. #2087 -- Bug with slice acces as inline struct member #2088. +- Bug with slice access as inline struct member #2088. - `@if` now does implicit conversion to bool like `$if`. #2086 - Fix broken enum inline -> bool conversions #2094. - `@if` was ignored on attrdef, regression 0.7 #2093. @@ -555,7 +570,7 @@ ### Changes / improvements - Contracts @require/@ensure are no longer treated as conditionals, but must be explicitly bool. - Add `win-debug` setting to be able to pick dwarf for output #1855. -- Error on switch case fallthough if there is more than one newline #1849. +- Error on switch case fallthrough if there is more than one newline #1849. - Added flags to `c3c project view` to filter displayed properties - Compile time array assignment #1806. - Allow `+++` to work on all types of arrays. @@ -582,7 +597,7 @@ - Fix issue with `@const` where the statement `$foo = 1;` was not considered constant. - Const strings and bytes were not properly converted to compile time bools. - Concatenating a const empty slice with another array caused a null pointer access. -- Fix `linux-crt` and `linux-crtbegin` not getting recognized as a project paramater +- Fix `linux-crt` and `linux-crtbegin` not getting recognized as a project parameter - Fix dues to crash when converting a const vector to another vector #1864. - Filter `$exec` output from `\r`, which otherwise would cause a compiler assert #1867. - Fixes to `"exec" use, including issue when compiling with MinGW. @@ -704,7 +719,7 @@ - Prohibit raw vaargs in regular functions with a function body. - Assert on certain slice to slice casts. #1768. - Fix vector float -> bool conversion. -- Fix `+a = 1` erronously being accepted. +- Fix `+a = 1` erroneously being accepted. - Fix not freeing a zero length String - Macros with trailing bodys aren't allowed as the single statement after a while loop with no body #1772. - Deref subscripts as needed for macro ref method arguments. #1789 @@ -1655,7 +1670,7 @@ - Allow any expression as default expression. - Allow using enums for indexing arrays. -- Added $convertable / $castable compile time functions. +- Added $convertible / $castable compile time functions. - Removed ´func´ deprecated keyword - Slicing a distinct type now returns the distinct type. - Renamed @autoimport -> @builtin @@ -1672,7 +1687,7 @@ - Add linker and linked dir arguments to build files. - Auto-import std::core. - LLVM 15 support. -- Beter native file handling for MSVC +- Better native file handling for MSVC - New import rules – recursive imports - Add lld linking for FreeBSD - User defined attributes. @Foo = @inline diff --git a/resources/grammar/grammar.y b/resources/grammar/grammar.y index c36677344..0222038aa 100644 --- a/resources/grammar/grammar.y +++ b/resources/grammar/grammar.y @@ -887,6 +887,7 @@ statement | asm_block_stmt | ct_echo_stmt | ct_assert_stmt + | ct_error_stmt | ct_if_stmt | ct_switch_stmt | ct_foreach_stmt @@ -927,14 +928,35 @@ optional_label | empty ; +ct_assert_expr_list + : ',' constant_expr + | ct_assert_expr_list ',' constant_expr + ; + ct_assert_stmt : CT_ASSERT constant_expr ':' constant_expr ';' + | CT_ASSERT constant_expr ':' constant_expr ct_assert_expr_list ';' | CT_ASSERT constant_expr ';' - | CT_ERROR constant_expr ';' + ; + +ct_error_stmt + : CT_ERROR constant_expr ';' + | CT_ERROR constant_expr ct_assert_expr_list ';' ; ct_include_stmt - : CT_INCLUDE string_expr ';' + : CT_INCLUDE constant_expr opt_attributes ';' + ; + +ct_exec_list + : constant_expr + | ct_exec_list ',' constant_expr + ; + +ct_exec_stmt + : CT_EXEC '(' string_expr ')' opt_attributes ';' + | CT_EXEC '(' string_expr ',' '{' ct_exec_list opt_comma '}' ')' opt_attributes ';' + | CT_EXEC '(' string_expr ',' '{' ct_exec_list opt_comma '}' ',' constant_expr ')' opt_attributes ';' ; ct_echo_stmt @@ -1077,7 +1099,7 @@ faults | faults ',' CONST_IDENT opt_attributes ; -fault_declaration +faultdef_declaration : FAULTDEF faults ';' ; @@ -1132,12 +1154,12 @@ parameter | CT_IDENT ELLIPSIS ; -func_defintion_decl +func_definition_decl : FN func_header fn_parameter_list opt_attributes ';' ; func_definition - : func_defintion_decl + : func_definition_decl | FN func_header fn_parameter_list opt_attributes macro_func_body ; @@ -1215,19 +1237,23 @@ opt_generic_parameters define_ident : IDENT opt_attributes '=' path_ident opt_generic_parameters + | IDENT opt_attributes '=' MODULE path_ident opt_generic_parameters | CONST_IDENT opt_attributes '=' path_const opt_generic_parameters | AT_IDENT opt_attributes '=' path_at_ident opt_generic_parameters ; -define_declaration +attrdef_declaration + : ATTRDEF define_attribute ';' + ; + +alias_declaration : ALIAS define_ident ';' - | ATTRDEF define_attribute ';' | ALIAS TYPE_IDENT opt_attributes '=' typedef_type opt_attributes ';' ; interface_body - : func_defintion_decl - | interface_body func_defintion_decl + : func_definition_decl + | interface_body func_definition_decl ; interface_declaration @@ -1245,7 +1271,7 @@ interface_declaration_name | TYPE_IDENT ':' interface_parents ; -distinct_declaration +typedef_declaration : TYPEDEF TYPE_IDENT opt_interface_impl opt_attributes '=' opt_inline type ';' ; @@ -1274,46 +1300,44 @@ import_decl ; translation_unit - : top_level_statements + : module top_level_after_module + | top_level_no_module | empty ; -top_level_statements - : top_level - | top_level_statements top_level - ; - -opt_extern - : EXTERN - | empty - ; - -exec_decl - : CT_EXEC '(' expr ')' opt_attributes ';' - | CT_EXEC '(' expr ',' initializer_list ')' opt_attributes ';' - | CT_EXEC '(' expr ',' initializer_list ',' expr ')' opt_attributes ';' - ; - -top_level - : module - | import_decl - | exec_decl - | opt_extern func_definition - | opt_extern const_declaration - | opt_extern global_declaration +top_level_decl + : import_decl + | func_definition + | EXTERN func_definition + | const_declaration + | EXTERN const_declaration + | global_declaration + | EXTERN global_declaration | ct_assert_stmt | ct_echo_stmt | ct_include_stmt + | ct_exec_stmt | struct_declaration - | fault_declaration + | faultdef_declaration | enum_declaration | macro_declaration - | define_declaration + | alias_declaration + | attrdef_declaration | bitstruct_declaration - | distinct_declaration + | typedef_declaration | interface_declaration ; +top_level_after_module + : top_level_decl + | top_level_after_module top_level_decl + ; + +top_level_no_module + : top_level_decl + | top_level_after_module top_level_decl + ; + %% void yyerror(YYLTYPE * yylloc_param , yyscan_t yyscanner, const char *yymsgp) diff --git a/resources/grammar/grammar_proposal.y b/resources/grammar/grammar_proposal.y index 9a5c73e90..25b694f36 100644 --- a/resources/grammar/grammar_proposal.y +++ b/resources/grammar/grammar_proposal.y @@ -1144,12 +1144,12 @@ parameter | CT_IDENT ELLIPSIS ; -func_defintion_decl +func_definition_decl : FN func_header fn_parameter_list opt_attributes ';' ; func_definition - : func_defintion_decl + : func_definition_decl | FN func_header fn_parameter_list opt_attributes macro_func_body ; @@ -1223,8 +1223,8 @@ define_declaration ; interface_body - : func_defintion_decl - | interface_body func_defintion_decl + : func_definition_decl + | interface_body func_definition_decl ; interface_declaration diff --git a/src/build/build_options.c b/src/build/build_options.c index 02314d09c..190b01a1d 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -249,7 +249,7 @@ static void project_view_usage() PRINTF("the results will be printed out like they are in the full view."); PRINTF("Otherwise the \"
: \" is left out."); PRINTF(""); - PRINTF("With flags on, each selected property will be seperated by an empty"); + PRINTF("With flags on, each selected property will be separated by an empty"); PRINTF("line, and properties with multiple values (like --authors) will have"); PRINTF("their values printed each on a new line."); PRINTF(""); @@ -1270,7 +1270,7 @@ static void parse_option(BuildOptions *options) name); } char *name_copy = strdup(name); - str_ellide_in_place(name_copy, 32); + str_elide_in_place(name_copy, 32); if (strchr(name, '/') != NULL || (PLATFORM_WINDOWS && strchr(name, '\\') != NULL)) { error_exit( diff --git a/src/build/libraries.c b/src/build/libraries.c index 469c2f0ce..d322ba283 100644 --- a/src/build/libraries.c +++ b/src/build/libraries.c @@ -103,7 +103,7 @@ static Library *add_library(JSONObject *json, const char *dir) if (!str_is_valid_lowercase_name(provides)) { char *res = strdup(provides); - str_ellide_in_place(res, 32); + str_elide_in_place(res, 32); error_exit("Invalid 'provides' module name in %s, was '%s', the name should only contain alphanumerical letters and '_'.", library->dir, res); } library->provides = provides; diff --git a/src/build/project.c b/src/build/project.c index d25b6e4c6..9bbfd92da 100644 --- a/src/build/project.c +++ b/src/build/project.c @@ -246,7 +246,7 @@ static void load_into_build_target(BuildParseContext context, JSONObject *json, if (!str_is_valid_lowercase_name(name)) { char *name_copy = strdup(name); - str_ellide_in_place(name_copy, 32); + str_elide_in_place(name_copy, 32); error_exit("Error reading %s: invalid library target name '%s' – it should only contain alphanumerical letters and '_'.", context.file, name_copy); } } diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 87fcb4be3..685539656 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -413,7 +413,7 @@ void compiler_parse(void) compiler_parsing_time = bench_mark(); } -bool compiler_should_ouput_file(const char *file) +bool compiler_should_output_file(const char *file) { if (!vec_size(compiler.build.emit_only)) return true; FOREACH(const char *, f, compiler.build.emit_only) @@ -1604,6 +1604,7 @@ Module *compiler_find_or_create_module(Path *module_name, const char **parameter // Set up the module. module = CALLOCS(Module); module->name = module_name; + module->inlined_at = (InliningSpan) { INVALID_SPAN, NULL }; size_t first = 0; for (size_t i = module_name->len; i > 0; i--) { diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 674bcef76..1d482179c 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -166,6 +166,25 @@ struct ConstInitializer_ }; }; +typedef union +{ + struct + { + FileId file_id; + unsigned char length; + unsigned char col; + uint32_t row; + }; + uint64_t a; +} SourceSpan; + +static_assert(sizeof(SourceSpan) == 8, "Expected 8 bytes"); + +typedef struct InliningSpan_ +{ + SourceSpan span; + struct InliningSpan_ *prev; +} InliningSpan; typedef struct { @@ -211,21 +230,8 @@ typedef struct const char *full_path; } File; -typedef union -{ - struct - { - FileId file_id; - unsigned char length; - unsigned char col; - uint32_t row; - }; - uint64_t a; -} SourceSpan; -static_assert(sizeof(SourceSpan) == 8, "Expected 8 bytes"); - typedef struct { const char *key; @@ -1607,6 +1613,7 @@ typedef struct Module_ Decl **tests; Decl **lambdas_to_evaluate; const char *generic_suffix; + InliningSpan inlined_at; } Module; @@ -1757,11 +1764,7 @@ typedef struct JumpTarget_ AstId defer; } JumpTarget; -typedef struct InliningSpan_ -{ - SourceSpan span; - struct InliningSpan_ *prev; -} InliningSpan; + struct SemaContext_ { @@ -2203,6 +2206,7 @@ Decl **copy_decl_list_macro(Decl **decl_list); Ast *copy_ast_macro(Ast *source_ast); Ast *copy_ast_defer(Ast *source_ast); TypeInfo *copy_type_info_single(TypeInfo *type_info); +InliningSpan *copy_inlining_span(InliningSpan *span); void init_asm(PlatformTarget *target); void print_asm_list(PlatformTarget *target); @@ -2478,7 +2482,7 @@ File *source_file_text_load(const char *filename, char *content); File *compile_and_invoke(const char *file, const char *args, const char *stdin_data, size_t limit); void compiler_parse(void); -bool compiler_should_ouput_file(const char *file); +bool compiler_should_output_file(const char *file); void emit_json(void); void stable_init(STable *table, uint32_t initial_size); diff --git a/src/compiler/copying.c b/src/compiler/copying.c index d2a1472a3..6d68e2fce 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -1159,3 +1159,11 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) return copy; } +InliningSpan *copy_inlining_span(InliningSpan *span) +{ + if (!span) return NULL; + InliningSpan *copy_span = MALLOCS(InliningSpan); + copy_span->span = span->span; + copy_span->prev = copy_inlining_span(span->prev); + return copy_span; +} \ No newline at end of file diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 32337a3c8..ca31074e7 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -1070,7 +1070,7 @@ static inline void llvm_optimize(GenContext *c) const char *llvm_codegen(void *context) { GenContext *c = context; - if (!compiler_should_ouput_file(c->base_name)) return NULL; + if (!compiler_should_output_file(c->base_name)) return NULL; llvm_optimize(c); // Serialize the LLVM IR, if requested, also verify the IR in this case diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index d0641c2de..12cf43fd6 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -4551,7 +4551,7 @@ static void llvm_emit_binary_expr(GenContext *c, BEValue *be_value, Expr *expr) // Operation + assign if (binary_op > BINARYOP_ASSIGN) { - // Finde the base op. + // Find the base op. BinaryOp base_op = binaryop_assign_base_op(binary_op); ASSERT(base_op != BINARYOP_ERROR); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 103881140..b61dd1714 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -589,7 +589,7 @@ void llvm_emit_for_stmt(GenContext *c, Ast *ast) llvm_emit_br(c, cond_block); break; case LOOP_INFINITE: - // We might have an infite loop + // We might have an infinite loop if (!loop_start_block) { SourceSpan loc = ast->span; diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 51888ddb8..8b0ed36f5 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -1179,7 +1179,7 @@ static Expr *parse_ct_defined(ParseContext *c, Expr *left, SourceSpan lhs_start /** * ct_sizeof ::= CT_SIZEOF '(' expr ')' * - * Note that this is tranformed to $typeof(expr).sizeof. + * Note that this is transformed to $typeof(expr).sizeof. */ static Expr *parse_ct_sizeof(ParseContext *c, Expr *left, SourceSpan lhs_start UNUSED) { @@ -1599,7 +1599,7 @@ EXIT: is_unsigned = false; if (i128_comp(i, INT128_MIN, type_u128) == CMP_GT) { - PRINT_ERROR_AT(expr_int, "The negated integer size would exeed an int128."); + PRINT_ERROR_AT(expr_int, "The negated integer size would exceed an int128."); return poisoned_expr; } if (negated) i = i128_neg(i); diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 6d7099fdb..b65a8ee2c 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -16,7 +16,7 @@ typedef enum FunctionParse_ static inline Decl *parse_func_definition(ParseContext *c, AstId contracts, FunctionParse parse_kind); static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl); static inline bool parse_enum_param_list(ParseContext *c, Decl*** parameters_ref, ArrayIndex *inline_index); -static Decl *parse_include(ParseContext *c); +static Decl *parse_ct_include(ParseContext *c); static Decl *parse_exec(ParseContext *c); static bool parse_attributes_for_global(ParseContext *c, Decl *decl); INLINE bool parse_decl_initializer(ParseContext *c, Decl *decl); @@ -3139,7 +3139,7 @@ static bool parse_contracts(ParseContext *c, AstId *contracts_ref) return true; } -static Decl *parse_include(ParseContext *c) +static Decl *parse_ct_include(ParseContext *c) { SourceSpan loc = c->span; Decl *decl = decl_new(DECL_CT_INCLUDE, NULL, loc); @@ -3323,7 +3323,7 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **context_out) return NULL; case TOKEN_CT_INCLUDE: if (contracts) goto CONTRACT_NOT_ALLOWED; - decl = parse_include(c); + decl = parse_ct_include(c); break; case TOKEN_CT_EXEC: if (contracts) goto CONTRACT_NOT_ALLOWED; diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index c09d6ba44..26b42ab60 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -41,7 +41,7 @@ Expr *parse_integer(ParseContext *c, Expr *left, SourceSpan lhs_start); Expr *parse_decl_or_expr(ParseContext *c); void recover_top_level(ParseContext *c); Expr *parse_cond(ParseContext *c); -Ast* parse_compound_stmt(ParseContext *c); +Ast *parse_compound_stmt(ParseContext *c); Ast *parse_short_body(ParseContext *c, TypeInfoId return_type, bool is_regular_fn); bool parse_attribute(ParseContext *c, Attr **attribute_ref, bool expect_eos); diff --git a/src/compiler/sema_asm.c b/src/compiler/sema_asm.c index ee21e7515..05e980b6c 100644 --- a/src/compiler/sema_asm.c +++ b/src/compiler/sema_asm.c @@ -82,7 +82,7 @@ static inline Decl *sema_resolve_external_symbol(SemaContext *context, Expr *exp } return decl; } -static inline bool sema_reg_int_suported_type(AsmArgType arg, Type *type) +static inline bool sema_reg_int_supported_type(AsmArgType arg, Type *type) { ASSERT(type_flatten(type) == type); unsigned bits = type_bit_size(type); @@ -104,7 +104,7 @@ INLINE bool sema_reg_is_valid_in_slot(AsmRegister *reg, AsmArgType arg_type) UNREACHABLE } -static inline bool sema_reg_float_suported_type(AsmArgType arg, Type *type) +static inline bool sema_reg_float_supported_type(AsmArgType arg, Type *type) { ASSERT(type_flatten(type) == type); if (!arg.float_bits) return false; @@ -415,7 +415,7 @@ static inline bool sema_check_asm_var(SemaContext *context, AsmInlineBlock *bloc SEMA_ERROR(expr, "An integer variable was not expected here."); return false; } - if (!sema_reg_int_suported_type(arg_type, type)) + if (!sema_reg_int_supported_type(arg_type, type)) { unsigned bits = arg_bits_max(arg_type.ireg_bits, 0); ASSERT(bits); @@ -437,7 +437,7 @@ static inline bool sema_check_asm_var(SemaContext *context, AsmInlineBlock *bloc SEMA_ERROR(expr, "A floating point variable was not expected here."); return false; } - if (!sema_reg_float_suported_type(arg_type, type)) + if (!sema_reg_float_supported_type(arg_type, type)) { SEMA_ERROR(expr, "%s is not supported in this position, convert it to a valid type.", type_quoted_error_string(decl->type)); @@ -500,7 +500,7 @@ static inline bool sema_check_asm_arg_value(SemaContext *context, AsmInlineBlock if (type_is_pointer_type(type)) type = type_uptr->canonical; if (type_is_integer(type)) { - if (!sema_reg_int_suported_type(arg_type, type)) + if (!sema_reg_int_supported_type(arg_type, type)) { SEMA_ERROR(expr, "%s is not valid for this slot.", type_quoted_error_string(inner->type)); return false; @@ -511,7 +511,7 @@ static inline bool sema_check_asm_arg_value(SemaContext *context, AsmInlineBlock } if (type_is_float(type)) { - if (!sema_reg_float_suported_type(arg_type, type)) + if (!sema_reg_float_supported_type(arg_type, type)) { SEMA_ERROR(expr, "%s is not valid for this slot.", type_quoted_error_string(inner->type)); return false; diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index f64104adc..88beba663 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -472,7 +472,7 @@ bool sema_expr_analyse_str_wide(SemaContext *context, Expr *expr, BuiltinFunctio data += increment - 1; if (!from_codepoint) { - RETURN_SEMA_ERROR(inner, "Unparseable codepoint in string."); + RETURN_SEMA_ERROR(inner, "Unparsable codepoint in string."); } if (type == type_ushort && from_codepoint & 0xFFFF0000) { diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 063fadba6..882d6698a 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -273,7 +273,7 @@ bool sema_error_failed_cast(SemaContext *context, Expr *expr, Type *from, Type * */ Type *type_infer_len_from_actual_type(Type *to_infer, Type *actual_type) { - // This may be called on types not inferrable, + // This may be called on types not inferable, // if so we assume the original type if (!type_len_is_inferred(to_infer)) return to_infer; diff --git a/src/compiler/sema_const.c b/src/compiler/sema_const.c index 3ed085605..3b1a3dfcb 100644 --- a/src/compiler/sema_const.c +++ b/src/compiler/sema_const.c @@ -297,7 +297,7 @@ static inline ConstInitializer *expr_const_initializer_from_expr(Expr *expr) * * 1. String/Bytes + ... => String/Bytes * 2. Vector/slice/array + Untyped list => Merged untyped list - * 3. Vector/slice/array + arraylike => vector/array iff canoncial match, otherwise Untyped list + * 3. Vector/slice/array + arraylike => vector/array iff canonical match, otherwise Untyped list * 4. Untyped list + Vector/array/slice => Merged untyped list * 5. Vector/array/slice + element => Vector/array/slice + 1 len iff canonical match, Untyped list otherwise * 6. Untyped list + element => Untyped list diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index abf68a5d1..4f6631ea5 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -48,7 +48,7 @@ static inline bool sema_analyse_distinct(SemaContext *context, Decl *decl, bool static CompilationUnit *unit_copy(Module *module, CompilationUnit *unit); -static Module *module_instantiate_generic(SemaContext *context, Module *module, Path *path, Expr **params); +static Module *module_instantiate_generic(SemaContext *context, Module *module, Path *path, Expr **params, SourceSpan from_span); static inline bool sema_analyse_enum_param(SemaContext *context, Decl *param); static inline bool sema_analyse_enum(SemaContext *context, Decl *decl, bool *erase_decl); @@ -541,7 +541,7 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl) AlignSize member_type_alignment; if (type_is_user_defined(member_type) && member_type->decl->resolve_status == RESOLVE_RUNNING) { - SEMA_ERROR(member, "Recursive defintion of %s.", type_quoted_error_string(member_type)); + SEMA_ERROR(member, "Recursive definition of %s.", type_quoted_error_string(member_type)); return decl_poison(decl); } if (!sema_set_abi_alignment(context, member->type, &member_type_alignment)) return decl_poison(decl); @@ -4626,6 +4626,11 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local, bool *c return decl_poison(decl); } + if (decl->var.no_init && decl->var.init_expr) + { + SEMA_ERROR(decl->var.init_expr, "'@noinit' variables may not have initializers."); + return decl_poison(decl); + } if (erase_decl) { decl->decl_kind = DECL_ERASED; @@ -4846,7 +4851,7 @@ static CompilationUnit *unit_copy(Module *module, CompilationUnit *unit) return copy; } -static Module *module_instantiate_generic(SemaContext *context, Module *module, Path *path, Expr **params) +static Module *module_instantiate_generic(SemaContext *context, Module *module, Path *path, Expr **params, SourceSpan from_span) { unsigned decls = 0; Decl* params_decls[MAX_PARAMS]; @@ -4897,6 +4902,7 @@ static Module *module_instantiate_generic(SemaContext *context, Module *module, new_module->contracts = astid(copy_ast_macro(astptr(module->contracts))); copy_end(); } + new_module->inlined_at = (InliningSpan) { .span = from_span, .prev = copy_inlining_span(context->inlined_at) }; return new_module; } @@ -5146,15 +5152,15 @@ Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, con AnalysisStage stage = c->unit->module->generic_module ? c->unit->module->stage : c->unit->module->stage - 1; - bool instatiation = false; + bool instantiation = false; if (!instantiated_module) { - instatiation = true; + instantiation = true; Path *path = CALLOCS(Path); path->module = path_string; path->span = module->name->span; path->len = scratch_buffer.len; - instantiated_module = module_instantiate_generic(c, module, path, params); + instantiated_module = module_instantiate_generic(c, module, path, params, invocation_span); if (!instantiated_module) return poisoned_decl; if (!sema_generate_parameterized_name_to_scratch(c, module, params, false, NULL)) return poisoned_decl; instantiated_module->generic_suffix = scratch_buffer_copy(); @@ -5167,7 +5173,7 @@ Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, con sema_error_at(c, span, "The generic module '%s' does not have '%s' for this parameterization.", module->name->module, name); return poisoned_decl; } - if (instatiation) + if (instantiation) { if (instantiated_module->contracts) { diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 4c01626a3..5c9c887dd 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -216,19 +216,22 @@ static Type *defer_iptr_cast(Expr *maybe_pointer); typedef struct { bool in_no_eval; + InliningSpan *old_inlining; } ContextSwitchState; static inline ContextSwitchState context_switch_state_push(SemaContext *context, SemaContext *new_context) { - ContextSwitchState state = { .in_no_eval = new_context->call_env.in_no_eval, }; + ContextSwitchState state = { .in_no_eval = new_context->call_env.in_no_eval, .old_inlining = new_context->inlined_at }; new_context->call_env.in_no_eval = context->call_env.in_no_eval; + new_context->inlined_at = context->inlined_at; return state; } static inline void context_switch_stat_pop(SemaContext *swapped, ContextSwitchState state) { swapped->call_env.in_no_eval = state.in_no_eval; + swapped->inlined_at = state.old_inlining; } Expr *sema_enter_inline_member(Expr *parent, CanonicalType *type) @@ -3807,11 +3810,15 @@ static inline bool sema_expr_resolve_subscript_index(SemaContext *context, Expr if (!subscript_type) { if (check_valid) return false; - if (overload_type == OVERLOAD_ELEMENT_REF) + switch (overload_type) { - RETURN_SEMA_ERROR(expr, "Getting a reference to a subscript of %s is not possible.", type_quoted_error_string(subscripted->type)); + case OVERLOAD_ELEMENT_REF: + RETURN_SEMA_ERROR(expr, "Getting a reference to a subscript of %s is not possible.", type_quoted_error_string(subscripted->type)); + case OVERLOAD_ELEMENT_SET: + RETURN_SEMA_ERROR(expr, "Assigning to a subscript of %s is not possible.", type_quoted_error_string(subscripted->type)); + default: + RETURN_SEMA_ERROR(expr, "Indexing a value of type %s is not possible.", type_quoted_error_string(subscripted->type)); } - RETURN_SEMA_ERROR(expr, "Indexing a value of type %s is not possible.", type_quoted_error_string(subscripted->type)); } if (!overload) current_type = type_flatten(current_expr->type); } @@ -3905,7 +3912,7 @@ SKIP: return true; } -static inline bool sema_expr_analyse_subscript_lvalue(SemaContext *context, Expr *expr, bool check_valid) +static inline bool sema_expr_analyse_subscript_lvalue(SemaContext *context, Expr *expr, bool *failed_ref) { // Evaluate the expression to index. Expr *subscripted = exprptr(expr->subscript_expr.expr); @@ -3944,12 +3951,11 @@ static inline bool sema_expr_analyse_subscript_lvalue(SemaContext *context, Expr Decl *overload; Type *subscript_type; int64_t index_value; - if (!sema_expr_resolve_subscript_index(context, expr, subscripted, index, ¤t_type, ¤t_expr, &subscript_type, &overload, &index_value, false, OVERLOAD_ELEMENT_SET, check_valid)) + if (!sema_expr_resolve_subscript_index(context, expr, subscripted, index, ¤t_type, ¤t_expr, &subscript_type, &overload, &index_value, false, OVERLOAD_ELEMENT_SET, failed_ref != NULL)) { - if (check_valid && expr_ok(index)) + if (failed_ref && expr_ok(index)) { - expr_poison(expr); - return true; + *failed_ref = true; } return false; } @@ -3959,7 +3965,7 @@ static inline bool sema_expr_analyse_subscript_lvalue(SemaContext *context, Expr { if (index_value == -1) { - if (check_valid) goto VALID_FAIL_POISON; + if (failed_ref) goto VALID_FAIL_POISON; RETURN_SEMA_ERROR(index, "Assigning to a compile time constant requires a constant index."); } expr->expr_kind = EXPR_CT_SUBSCRIPT; @@ -3978,7 +3984,7 @@ static inline bool sema_expr_analyse_subscript_lvalue(SemaContext *context, Expr Decl *len = sema_find_untyped_operator(current_expr->type, OVERLOAD_LEN, NULL); if (!len) { - if (check_valid) goto VALID_FAIL_POISON; + if (failed_ref) goto VALID_FAIL_POISON; RETURN_SEMA_ERROR(subscripted, "Cannot index '%s' from the end, since there is no 'len' overload.", type_to_error_string(subscripted->type)); } if (!sema_analyse_expr(context, current_expr)) return false; @@ -4012,9 +4018,9 @@ static inline bool sema_expr_analyse_subscript_lvalue(SemaContext *context, Expr // Check range bool remove_from_back = false; if (!sema_slice_index_is_in_range(context, current_type, index, false, start_from_end, &remove_from_back, - check_valid)) + failed_ref != NULL)) { - if (check_valid) goto VALID_FAIL_POISON; + if (failed_ref) goto VALID_FAIL_POISON; return false; } if (remove_from_back) @@ -4026,8 +4032,8 @@ static inline bool sema_expr_analyse_subscript_lvalue(SemaContext *context, Expr expr->type = type_add_optional(subscript_type, optional); return true; VALID_FAIL_POISON: - expr_poison(expr); - return true; + *failed_ref = true; + return false; } static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, CheckType check, bool check_valid) @@ -5959,7 +5965,7 @@ static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, E }; if (is_lvalue) { - if (!sema_expr_analyse_subscript_lvalue(context, expr, false)) return false; + if (!sema_expr_analyse_subscript_lvalue(context, expr, NULL)) return false; } else { @@ -9117,7 +9123,7 @@ static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr, EndJump active_scope_jump = context->active_scope.end_jump; - // First we analyse the "else" and try to implictly cast. + // First we analyse the "else" and try to implicitly cast. if (!sema_analyse_inferred_expr(context, infer_type, right, NULL)) return false; if (left->expr_kind == EXPR_OPTIONAL) @@ -9189,7 +9195,7 @@ static inline bool sema_expr_analyse_binary(SemaContext *context, Type *infer_ty { if (left->expr_kind != EXPR_TYPEINFO) { - if (!sema_analyse_expr_lvalue(context, left, NULL)) return false; + if (!sema_analyse_expr_lvalue(context, left, failed_ref)) return false; } } else @@ -10998,7 +11004,7 @@ static inline bool sema_expr_analyse_builtin(SemaContext *context, Expr *expr, b static inline bool sema_expr_analyse_compound_literal(SemaContext *context, Expr *expr, bool *no_match_ref) { TypeInfo *type_info = expr->expr_compound_literal.type_info; - // We allow infering the size of arrays. + // We allow inferring the size of arrays. if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_ALLOW_INFER)) return false; Type *type = type_info->type; if (type_is_optional(type)) @@ -11450,7 +11456,7 @@ RETRY: case EXPR_CT_IDENT: return sema_expr_resolve_ct_identifier(context, expr); case EXPR_SUBSCRIPT: - return sema_expr_analyse_subscript_lvalue(context, expr, false); + return sema_expr_analyse_subscript_lvalue(context, expr, failed_ref); case EXPR_OTHER_CONTEXT: { DEBUG_LOG("Switch context"); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index eece104fd..8b21eee99 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -327,6 +327,7 @@ static inline Expr *sema_dive_into_expression(Expr *expr) { case EXPR_RVALUE: case EXPR_RECAST: + case EXPR_PTR_ACCESS: expr = expr->inner_expr; continue; case EXPR_MAKE_SLICE: @@ -630,8 +631,18 @@ INLINE bool sema_check_not_stack_variable_escape(SemaContext *context, Expr *exp expr = expr->unary_expr.expr; CHECK_ACCESS: ASSERT_SPAN(expr, expr->resolve_status == RESOLVE_DONE); - while (expr->expr_kind == EXPR_ACCESS_RESOLVED) expr = expr->access_resolved_expr.parent; - + // &foo.bar.baz => foo + while (expr->expr_kind == EXPR_ACCESS_RESOLVED) + { + // If we indexed into something, like &foo.bar.baz[3] + if (allow_pointer) + { + // Then if foo.bar.baz was a pointer or slice, that's ok. + TypeKind kind = type_flatten(expr->type)->type_kind; + if (kind == TYPE_POINTER || kind == TYPE_SLICE) return true; + } + expr = expr->access_resolved_expr.parent; + } if (expr->expr_kind != EXPR_IDENTIFIER) return true; Decl *decl = expr->ident_expr; if (decl->decl_kind != DECL_VAR) return true; @@ -639,6 +650,8 @@ CHECK_ACCESS: { case VARDECL_LOCAL: if (decl->var.is_static) return true; + FALLTHROUGH; + case VARDECL_PARAM: switch (type_flatten(decl->type)->type_kind) { case TYPE_POINTER: @@ -649,15 +662,12 @@ CHECK_ACCESS: default: break; } - FALLTHROUGH; - case VARDECL_PARAM: break; default: return true; } - SEMA_ERROR(outer, "A pointer to a local variable will be invalid once the function returns. " + RETURN_SEMA_ERROR(outer, "A pointer to a local variable will be invalid once the function returns. " "Allocate the data on the heap or temp memory to return a pointer."); - return false; } /** diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 9fdfedd76..75bea07ba 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -592,6 +592,17 @@ void sema_print_inline(SemaContext *context, SourceSpan original) } inlined_at = inlined_at->prev; } + InliningSpan span = context->compilation_unit->module->inlined_at; + if (span.span.a == INVALID_SPAN.a) return; + inlined_at = &span; + while (inlined_at) + { + if (inlined_at->span.a != original.a) + { + sema_note_prev_at(inlined_at->span, "Inlined from here."); + } + inlined_at = inlined_at->prev; + } } void sema_error_at(SemaContext *context, SourceSpan span, const char *message, ...) diff --git a/src/compiler_tests/tests.c b/src/compiler_tests/tests.c index 9863ac6f9..0c9ef5433 100644 --- a/src/compiler_tests/tests.c +++ b/src/compiler_tests/tests.c @@ -19,146 +19,146 @@ void test_file(void) void test128() { printf("Begin i128 testing.\n"); - Int128 addres = i128_add(i128(0x123, 0x123), i128(0x222, 0x333)); - TEST_ASSERTF(addres.high == 0x345 && addres.low == 0x456, "i128 add failed with small numbers was %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - addres = i128_add(i128(0x123, UINT64_MAX), i128(0x222, 0x1)); - TEST_ASSERT(addres.high == 0x346 && addres.low == 0, "i128 add failed with simple overflow"); - addres = i128_add(i128(0x123, UINT64_MAX), i128(0x222, UINT64_MAX)); - TEST_ASSERT(addres.high == 0x346 && addres.low == UINT64_MAX - 1, "i128 add failed with simple overflow2"); - addres = i128_add(i128(UINT64_MAX, UINT64_MAX), i128(0x0, 0x1)); - TEST_ASSERT(addres.high == 0 && addres.low == 0, "i128 add failed with wrap"); - addres = i128_add(i128(UINT64_MAX, UINT64_MAX), i128(UINT64_MAX, UINT64_MAX)); - TEST_ASSERT(addres.high == UINT64_MAX && addres.low == UINT64_MAX - 1, "i128 add failed overflow with wrap"); + Int128 address = i128_add(i128(0x123, 0x123), i128(0x222, 0x333)); + TEST_ASSERTF(address.high == 0x345 && address.low == 0x456, "i128 add failed with small numbers was %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + address = i128_add(i128(0x123, UINT64_MAX), i128(0x222, 0x1)); + TEST_ASSERT(address.high == 0x346 && address.low == 0, "i128 add failed with simple overflow"); + address = i128_add(i128(0x123, UINT64_MAX), i128(0x222, UINT64_MAX)); + TEST_ASSERT(address.high == 0x346 && address.low == UINT64_MAX - 1, "i128 add failed with simple overflow2"); + address = i128_add(i128(UINT64_MAX, UINT64_MAX), i128(0x0, 0x1)); + TEST_ASSERT(address.high == 0 && address.low == 0, "i128 add failed with wrap"); + address = i128_add(i128(UINT64_MAX, UINT64_MAX), i128(UINT64_MAX, UINT64_MAX)); + TEST_ASSERT(address.high == UINT64_MAX && address.low == UINT64_MAX - 1, "i128 add failed overflow with wrap"); printf("-- i128 Add - Ok.\n"); - addres = i128_sub(i128(0x345, 0x457), i128(0x222, 0x333)); - TEST_ASSERTF(addres.high == 0x123 && addres.low == 0x124, "i128 sub failed with small numbers was %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - addres = i128_sub(i128(0x346, 0), i128(0x222, 0x1)); - TEST_ASSERTF(addres.high == 0x123 && addres.low == UINT64_MAX, "i128 sub failed with simple overflow %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - addres = i128_sub(i128(0x346, UINT64_MAX - 1), i128(0x222, UINT64_MAX)); - TEST_ASSERT(addres.high == 0x123 && addres.low == UINT64_MAX, "i128 sub failed with simple overflow2"); - addres = i128_sub(i128(0, 0), i128(0x0, 0x1)); - TEST_ASSERTF(addres.high == UINT64_MAX && addres.low == UINT64_MAX, "i128 sub failed with wrap %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - addres = i128_sub(i128(UINT64_MAX, UINT64_MAX - 1), i128(UINT64_MAX, UINT64_MAX)); - TEST_ASSERT(addres.high == UINT64_MAX && addres.low == UINT64_MAX, "i128 sub failed overflow with wrap"); + address = i128_sub(i128(0x345, 0x457), i128(0x222, 0x333)); + TEST_ASSERTF(address.high == 0x123 && address.low == 0x124, "i128 sub failed with small numbers was %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + address = i128_sub(i128(0x346, 0), i128(0x222, 0x1)); + TEST_ASSERTF(address.high == 0x123 && address.low == UINT64_MAX, "i128 sub failed with simple overflow %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + address = i128_sub(i128(0x346, UINT64_MAX - 1), i128(0x222, UINT64_MAX)); + TEST_ASSERT(address.high == 0x123 && address.low == UINT64_MAX, "i128 sub failed with simple overflow2"); + address = i128_sub(i128(0, 0), i128(0x0, 0x1)); + TEST_ASSERTF(address.high == UINT64_MAX && address.low == UINT64_MAX, "i128 sub failed with wrap %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + address = i128_sub(i128(UINT64_MAX, UINT64_MAX - 1), i128(UINT64_MAX, UINT64_MAX)); + TEST_ASSERT(address.high == UINT64_MAX && address.low == UINT64_MAX, "i128 sub failed overflow with wrap"); printf("-- i128 Sub - Ok.\n"); - addres = i128_and(i128(0x0, 0x0), i128(UINT64_MAX, UINT64_MAX)); - TEST_ASSERT(addres.high == 0 && addres.low == 0, "And failed"); - addres = i128_and(i128(0x123, 0x123456789abcdef1), i128(UINT64_MAX, UINT64_MAX)); - TEST_ASSERT(addres.high == 0x123 && addres.low == 0x123456789abcdef1, "And failed"); - addres = i128_and(i128(0xabcdef2233, 0x123456789A), i128(0x0F0F0F0F0F0F, 0xF0F0F0F0F0F0)); - TEST_ASSERT(addres.high == 0x0b0d0f0203 && addres.low == 0x1030507090, "And failed"); + address = i128_and(i128(0x0, 0x0), i128(UINT64_MAX, UINT64_MAX)); + TEST_ASSERT(address.high == 0 && address.low == 0, "And failed"); + address = i128_and(i128(0x123, 0x123456789abcdef1), i128(UINT64_MAX, UINT64_MAX)); + TEST_ASSERT(address.high == 0x123 && address.low == 0x123456789abcdef1, "And failed"); + address = i128_and(i128(0xabcdef2233, 0x123456789A), i128(0x0F0F0F0F0F0F, 0xF0F0F0F0F0F0)); + TEST_ASSERT(address.high == 0x0b0d0f0203 && address.low == 0x1030507090, "And failed"); printf("-- i128 And - Ok.\n"); - addres = i128_or(i128(0x0, 0x0), i128(UINT64_MAX, UINT64_MAX)); - TEST_ASSERT(addres.high == UINT64_MAX && addres.low == UINT64_MAX, "Or failed"); - addres = i128_or(i128(0x123, 0x123456789abcdef1), i128(0x123203, 0x0)); - TEST_ASSERT(addres.high == 0x123323 && addres.low == 0x123456789abcdef1, "Or failed"); - addres = i128_or(i128(0xabcdef2233, 0x123456789A), i128(0x0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0)); - TEST_ASSERTF(addres.high == 0x0FAFCFEF2F3F && addres.low == 0xF0F0F2F4F6F8FA, "Or failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + address = i128_or(i128(0x0, 0x0), i128(UINT64_MAX, UINT64_MAX)); + TEST_ASSERT(address.high == UINT64_MAX && address.low == UINT64_MAX, "Or failed"); + address = i128_or(i128(0x123, 0x123456789abcdef1), i128(0x123203, 0x0)); + TEST_ASSERT(address.high == 0x123323 && address.low == 0x123456789abcdef1, "Or failed"); + address = i128_or(i128(0xabcdef2233, 0x123456789A), i128(0x0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0)); + TEST_ASSERTF(address.high == 0x0FAFCFEF2F3F && address.low == 0xF0F0F2F4F6F8FA, "Or failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); printf("-- i128 Or - Ok.\n"); - addres = i128_xor(i128(0x0, 0x0), i128(UINT64_MAX, UINT64_MAX)); - TEST_ASSERT(addres.high == UINT64_MAX && addres.low == UINT64_MAX, "Xor failed"); - addres = i128_xor(i128(0x123, 0x123456789abcdef1), i128(0x123223, 0x0)); - TEST_ASSERT(addres.high == 0x123300 && addres.low == 0x123456789abcdef1, "Xor failed"); - addres = i128_xor(i128(0xabcdef2233, 0x123456789A), i128(0x0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0)); - TEST_ASSERTF(addres.high == 0x0FA4C2E02d3c && addres.low == 0xF0F0e2c4a6886A, "Xor failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + address = i128_xor(i128(0x0, 0x0), i128(UINT64_MAX, UINT64_MAX)); + TEST_ASSERT(address.high == UINT64_MAX && address.low == UINT64_MAX, "Xor failed"); + address = i128_xor(i128(0x123, 0x123456789abcdef1), i128(0x123223, 0x0)); + TEST_ASSERT(address.high == 0x123300 && address.low == 0x123456789abcdef1, "Xor failed"); + address = i128_xor(i128(0xabcdef2233, 0x123456789A), i128(0x0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0)); + TEST_ASSERTF(address.high == 0x0FA4C2E02d3c && address.low == 0xF0F0e2c4a6886A, "Xor failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); printf("-- i128 Xor - Ok.\n"); - addres = i128_neg(i128(0x0, 0x0)); - TEST_ASSERT(addres.high == 0 && addres.low == 0, "Neg failed"); - addres = i128_neg(i128(0x123, 0x123456789abcdef1)); - TEST_ASSERT(addres.high == ~((uint64_t)0x123) && addres.low == ~(uint64_t)0x123456789abcdef0, "Neg failed"); - addres = i128_neg(i128(0xabcdef2233, 0x123456789A)); - TEST_ASSERTF(addres.high == ~(uint64_t)0xabcdef2233 && addres.low == ~(uint64_t)0x1234567899, "Neg failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + address = i128_neg(i128(0x0, 0x0)); + TEST_ASSERT(address.high == 0 && address.low == 0, "Neg failed"); + address = i128_neg(i128(0x123, 0x123456789abcdef1)); + TEST_ASSERT(address.high == ~((uint64_t)0x123) && address.low == ~(uint64_t)0x123456789abcdef0, "Neg failed"); + address = i128_neg(i128(0xabcdef2233, 0x123456789A)); + TEST_ASSERTF(address.high == ~(uint64_t)0xabcdef2233 && address.low == ~(uint64_t)0x1234567899, "Neg failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); printf("-- i128 Neg - Ok.\n"); - addres = i128_from_str("1123"); - TEST_ASSERT(addres.high == 0 && addres.low == 1123, "Init failed"); - addres = i128_from_str("10000000000000000000012344434232"); - TEST_ASSERT(addres.high == 0x7e37be2022 && addres.low == 0xc0914b295fc91e38, "Init failed"); + address = i128_from_str("1123"); + TEST_ASSERT(address.high == 0 && address.low == 1123, "Init failed"); + address = i128_from_str("10000000000000000000012344434232"); + TEST_ASSERT(address.high == 0x7e37be2022 && address.low == 0xc0914b295fc91e38, "Init failed"); - addres = i128_mult(i128(0x111, 0x222), i128(0, 2)); - TEST_ASSERTF(addres.high == 0x222 && addres.low == 0x444, "Mult failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - addres = i128_mult(i128(0x111, 0x222), i128(2, 0)); - TEST_ASSERTF(addres.high == 0x444 && addres.low == 0, "Mult failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - addres = i128_mult(i128_from_str("523871293871232000123"), i128_from_str("283712312938293299")); + address = i128_mult(i128(0x111, 0x222), i128(0, 2)); + TEST_ASSERTF(address.high == 0x222 && address.low == 0x444, "Mult failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + address = i128_mult(i128(0x111, 0x222), i128(2, 0)); + TEST_ASSERTF(address.high == 0x444 && address.low == 0, "Mult failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + address = i128_mult(i128_from_str("523871293871232000123"), i128_from_str("283712312938293299")); - TEST_ASSERTF(i128_ucomp(i128_from_str("148628736466183585621117368965778075777"), addres) == CMP_EQ, "Mult failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_ucomp(i128_from_str("148628736466183585621117368965778075777"), address) == CMP_EQ, "Mult failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); printf("-- i128 Mult ok.\n"); - TEST_ASSERTF(i128_ucomp(i128_from_str("123"), i128_from_str("123")) == CMP_EQ, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_ucomp(i128_from_str("123"), i128_from_str("124")) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_ucomp(i128_from_str("123"), i128_from_str("121")) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_ucomp(i128(0x222, 0x111), i128(0x111, 0x222)) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_ucomp(i128(0x111, 0x222), i128(0x222, 0x111)) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_ucomp(i128(0x222, 0x111), i128(0x222, 0x111)) == CMP_EQ, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_ucomp(i128(UINT64_MAX, 0x111), i128(0x111, 0x222)) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_ucomp(i128(0x111, 0x222), i128(UINT64_MAX, 0x111)) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_ucomp(i128_from_str("123"), i128_from_str("123")) == CMP_EQ, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_ucomp(i128_from_str("123"), i128_from_str("124")) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_ucomp(i128_from_str("123"), i128_from_str("121")) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_ucomp(i128(0x222, 0x111), i128(0x111, 0x222)) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_ucomp(i128(0x111, 0x222), i128(0x222, 0x111)) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_ucomp(i128(0x222, 0x111), i128(0x222, 0x111)) == CMP_EQ, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_ucomp(i128(UINT64_MAX, 0x111), i128(0x111, 0x222)) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_ucomp(i128(0x111, 0x222), i128(UINT64_MAX, 0x111)) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); printf("-- i128 Ucomp ok.\n"); - TEST_ASSERTF(i128_scomp(i128_from_str("123"), i128_from_str("123")) == CMP_EQ, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_scomp(i128_from_str("123"), i128_from_str("124")) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_scomp(i128_from_str("123"), i128_from_str("121")) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_scomp(i128(0x222, 0x111), i128(0x111, 0x222)) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_scomp(i128(0x111, 0x222), i128(0x222, 0x111)) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_scomp(i128(0x222, 0x111), i128(0x222, 0x111)) == CMP_EQ, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_scomp(i128(UINT64_MAX, 0x111), i128(0x111, 0x222)) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - TEST_ASSERTF(i128_scomp(i128(0x111, 0x222), i128(UINT64_MAX, 0x111)) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(i128_scomp(i128_from_str("123"), i128_from_str("123")) == CMP_EQ, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_scomp(i128_from_str("123"), i128_from_str("124")) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_scomp(i128_from_str("123"), i128_from_str("121")) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_scomp(i128(0x222, 0x111), i128(0x111, 0x222)) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_scomp(i128(0x111, 0x222), i128(0x222, 0x111)) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_scomp(i128(0x222, 0x111), i128(0x222, 0x111)) == CMP_EQ, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_scomp(i128(UINT64_MAX, 0x111), i128(0x111, 0x222)) == CMP_LT, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + TEST_ASSERTF(i128_scomp(i128(0x111, 0x222), i128(UINT64_MAX, 0x111)) == CMP_GT, "Comp failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); printf("-- i128 Scomp ok.\n"); - addres = i128_shl(i128(0x234, 0x123456), i128(0, 0x4)); - TEST_ASSERT(addres.high == 0x2340 && addres.low == 0x1234560, "shl failed"); - addres = i128_shl(i128(0x234, 0x1234561), i128(0, 128)); - TEST_ASSERT(addres.high == 0 && addres.low == 0, "shl failed"); - addres = i128_shl(i128(0x234, 0x1234561), i128(1, 1)); - TEST_ASSERT(addres.high == 0 && addres.low == 0, "shl failed"); - addres = i128_shl(i128(0x234, 0x1234561), i128(0, 64)); - TEST_ASSERT(addres.high == 0x1234561 && addres.low == 0, "shl failed"); + address = i128_shl(i128(0x234, 0x123456), i128(0, 0x4)); + TEST_ASSERT(address.high == 0x2340 && address.low == 0x1234560, "shl failed"); + address = i128_shl(i128(0x234, 0x1234561), i128(0, 128)); + TEST_ASSERT(address.high == 0 && address.low == 0, "shl failed"); + address = i128_shl(i128(0x234, 0x1234561), i128(1, 1)); + TEST_ASSERT(address.high == 0 && address.low == 0, "shl failed"); + address = i128_shl(i128(0x234, 0x1234561), i128(0, 64)); + TEST_ASSERT(address.high == 0x1234561 && address.low == 0, "shl failed"); printf("-- i128 Shl ok.\n"); - addres = i128_lshr(i128(0x234, 0x123456), i128(0, 0x4)); - TEST_ASSERTF(addres.high == 0x23 && addres.low == 0x4000000000012345, "lshr failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - addres = i128_lshr(i128(0x234, 0x1234561), i128(0, 128)); - TEST_ASSERT(addres.high == 0 && addres.low == 0, "lshr failed"); - addres = i128_lshr(i128(0x234, 0x1234561), i128(1, 1)); - TEST_ASSERT(addres.high == 0 && addres.low == 0, "lshr failed"); - addres = i128_lshr(i128(0x234, 0x1234561), i128(0, 64)); - TEST_ASSERTF(addres.high == 0 && addres.low == 0x234, "lshr failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + address = i128_lshr(i128(0x234, 0x123456), i128(0, 0x4)); + TEST_ASSERTF(address.high == 0x23 && address.low == 0x4000000000012345, "lshr failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + address = i128_lshr(i128(0x234, 0x1234561), i128(0, 128)); + TEST_ASSERT(address.high == 0 && address.low == 0, "lshr failed"); + address = i128_lshr(i128(0x234, 0x1234561), i128(1, 1)); + TEST_ASSERT(address.high == 0 && address.low == 0, "lshr failed"); + address = i128_lshr(i128(0x234, 0x1234561), i128(0, 64)); + TEST_ASSERTF(address.high == 0 && address.low == 0x234, "lshr failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); printf("-- i128 Lshr ok.\n"); - addres = i128_ashr(i128(0x234, 0x123456), i128(0, 0x4)); - TEST_ASSERTF(addres.high == 0x23 && addres.low == 0x4000000000012345, "ashr failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - addres = i128_ashr(i128(0xF000000000000234, 0x123456), i128(0, 0x4)); - TEST_ASSERTF(addres.high == 0xFF00000000000023 && addres.low == 0x4000000000012345, "ashr failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - addres = i128_ashr(i128(0x234, 0x1234561), i128(0, 128)); - TEST_ASSERT(addres.high == 0 && addres.low == 0, "ashr failed"); - addres = i128_ashr(i128(0xF000000000000234, 0x1234561), i128(0, 128)); - TEST_ASSERT(addres.high == UINT64_MAX && addres.low == UINT64_MAX, "ashr failed"); - addres = i128_ashr(i128(0x234, 0x1234561), i128(1, 1)); - TEST_ASSERT(addres.high == 0 && addres.low == 0, "ashr failed"); - addres = i128_ashr(i128(0xF000000000000234, 0x1234561), i128(1, 1)); - TEST_ASSERT(addres.high == UINT64_MAX && addres.low == UINT64_MAX, "ashr failed"); - addres = i128_ashr(i128(0x234, 0x1234561), i128(0, 64)); - TEST_ASSERTF(addres.high == 0 && addres.low == 0x234, "ashr failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); - addres = i128_ashr(i128(0xF000000000000234, 0x1234561), i128(0, 64)); - TEST_ASSERTF(addres.high == UINT64_MAX && addres.low == 0xF000000000000234, "ashr failed %llx, %llx", (unsigned long long)addres.high, (unsigned long long)addres.low); + address = i128_ashr(i128(0x234, 0x123456), i128(0, 0x4)); + TEST_ASSERTF(address.high == 0x23 && address.low == 0x4000000000012345, "ashr failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + address = i128_ashr(i128(0xF000000000000234, 0x123456), i128(0, 0x4)); + TEST_ASSERTF(address.high == 0xFF00000000000023 && address.low == 0x4000000000012345, "ashr failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + address = i128_ashr(i128(0x234, 0x1234561), i128(0, 128)); + TEST_ASSERT(address.high == 0 && address.low == 0, "ashr failed"); + address = i128_ashr(i128(0xF000000000000234, 0x1234561), i128(0, 128)); + TEST_ASSERT(address.high == UINT64_MAX && address.low == UINT64_MAX, "ashr failed"); + address = i128_ashr(i128(0x234, 0x1234561), i128(1, 1)); + TEST_ASSERT(address.high == 0 && address.low == 0, "ashr failed"); + address = i128_ashr(i128(0xF000000000000234, 0x1234561), i128(1, 1)); + TEST_ASSERT(address.high == UINT64_MAX && address.low == UINT64_MAX, "ashr failed"); + address = i128_ashr(i128(0x234, 0x1234561), i128(0, 64)); + TEST_ASSERTF(address.high == 0 && address.low == 0x234, "ashr failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); + address = i128_ashr(i128(0xF000000000000234, 0x1234561), i128(0, 64)); + TEST_ASSERTF(address.high == UINT64_MAX && address.low == 0xF000000000000234, "ashr failed %llx, %llx", (unsigned long long)address.high, (unsigned long long)address.low); printf("-- i128 Ashr ok.\n"); TEST_ASSERT(i128_ucomp(i128_udiv(i128_from_str("123"), i128_from_str("123")), i128_from_str("1")) == CMP_EQ, "Div failed"); TEST_ASSERT(i128_ucomp(i128_udiv(i128_from_str("123"), i128_from_str("124")), i128_from_str("0")) == CMP_EQ, "Div failed"); TEST_ASSERT(i128_ucomp(i128_udiv(i128_from_str("245"), i128_from_str("123")), i128_from_str("1")) == CMP_EQ, "Div failed"); - addres = i128_udiv(i128(0x12345, UINT64_MAX), i128(1, 0)); - TEST_ASSERT(addres.low == 0x12345 && addres.high == 0, "Div failed"); - addres = i128_sdiv(i128(0x12345, UINT64_MAX), i128(1, 0)); - TEST_ASSERT(addres.low == 0x12345 && addres.high == 0, "Div failed"); - addres = i128_udiv(i128(UINT64_MAX, 0), i128(1, 0)); - TEST_ASSERT(addres.low == UINT64_MAX && addres.high == 0, "Div failed"); - addres = i128_sdiv(i128(UINT64_MAX - 1, UINT64_MAX - 1), i128(1, 0)); - TEST_ASSERTF(addres.low == UINT64_MAX && addres.high == UINT64_MAX, "Div failed %s", i128_to_string(addres, 10, + address = i128_udiv(i128(0x12345, UINT64_MAX), i128(1, 0)); + TEST_ASSERT(address.low == 0x12345 && address.high == 0, "Div failed"); + address = i128_sdiv(i128(0x12345, UINT64_MAX), i128(1, 0)); + TEST_ASSERT(address.low == 0x12345 && address.high == 0, "Div failed"); + address = i128_udiv(i128(UINT64_MAX, 0), i128(1, 0)); + TEST_ASSERT(address.low == UINT64_MAX && address.high == 0, "Div failed"); + address = i128_sdiv(i128(UINT64_MAX - 1, UINT64_MAX - 1), i128(1, 0)); + TEST_ASSERTF(address.low == UINT64_MAX && address.high == UINT64_MAX, "Div failed %s", i128_to_string(address, 10, true, false)); - addres = i128_sdiv(i128(2, 0), i128(UINT64_MAX - 1, UINT64_MAX - 1)); + address = i128_sdiv(i128(2, 0), i128(UINT64_MAX - 1, UINT64_MAX - 1)); printf("-- i128 Div okfefe %x.\n", (unsigned)-2); - TEST_ASSERTF(addres.low == UINT64_MAX && addres.high == UINT64_MAX, "Div failed: %s %llx, %llx", i128_to_string( - addres, 10, true, false), (unsigned long long)addres.high, (unsigned long long)addres.low); + TEST_ASSERTF(address.low == UINT64_MAX && address.high == UINT64_MAX, "Div failed: %s %llx, %llx", i128_to_string( + address, 10, true, false), (unsigned long long)address.high, (unsigned long long)address.low); printf("-- i128 Div ok.\n"); diff --git a/src/utils/lib.h b/src/utils/lib.h index 5d150067b..42eab7569 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -147,7 +147,7 @@ int str_findlist(const char *value, unsigned count, const char** elements); // Sprintf style, saved to an arena allocated string char *str_printf(const char *var, ...) __printflike(1, 2); char *str_vprintf(const char *var, va_list list); -void str_ellide_in_place(char *string, size_t max_size_shown); +void str_elide_in_place(char *string, size_t max_size_shown); bool str_is_valid_lowercase_name(const char *string); bool str_is_valid_constant(const char *string); const char *str_unescape(char *string); diff --git a/src/utils/stringutils.c b/src/utils/stringutils.c index ec39c0a82..1a6d65e47 100644 --- a/src/utils/stringutils.c +++ b/src/utils/stringutils.c @@ -168,7 +168,7 @@ bool str_is_valid_constant(const char *string) return true; } -void str_ellide_in_place(char *string, size_t max_size_shown) +void str_elide_in_place(char *string, size_t max_size_shown) { size_t len = strlen(string); if (max_size_shown > len) return; diff --git a/src/version.h b/src/version.h index 877a52b0b..d92f017ad 100644 --- a/src/version.h +++ b/src/version.h @@ -1,2 +1,2 @@ -#define COMPILER_VERSION "0.7.5" -#define PRERELEASE 0 +#define COMPILER_VERSION "0.7.6" +#define PRERELEASE 1 diff --git a/test/src/test_suite_runner.c3 b/test/src/test_suite_runner.c3 index 73098da91..5fe827a04 100644 --- a/test/src/test_suite_runner.c3 +++ b/test/src/test_suite_runner.c3 @@ -24,7 +24,7 @@ fn int main(String[] args) // Retain our current path. start_cwd = path::tcwd()!!; - // Create our test path, note that this prevents us from doing tests in parallell + // Create our test path, note that this prevents us from doing tests in parallel test_dir = start_cwd.tappend("_c3test_")!!; defer (void)path::rmtree(test_dir); diff --git a/test/test_suite/cast/cast_string_to_infered_array.c3 b/test/test_suite/cast/cast_string_to_inferred_array.c3 similarity index 100% rename from test/test_suite/cast/cast_string_to_infered_array.c3 rename to test/test_suite/cast/cast_string_to_inferred_array.c3 diff --git a/test/test_suite/compile_time_introspection/defined_subscript_assign.c3 b/test/test_suite/compile_time_introspection/defined_subscript_assign.c3 new file mode 100644 index 000000000..ee696e61a --- /dev/null +++ b/test/test_suite/compile_time_introspection/defined_subscript_assign.c3 @@ -0,0 +1,14 @@ +fn int main(String[] args) +{ + MyArray a = {{1, 4, 3, 6, 5}}; + $assert(!$defined(a[0] = 2)); + return 0; +} + +struct MyArray +{ + int[] val; +} + +fn int MyArray.get(self, usz idx) @operator([]) => self.val[idx]; +fn usz MyArray.len(self) @operator(len) => self.val.len; \ No newline at end of file diff --git a/test/test_suite/expressions/check_implict_conversion_signed_unsigned.c3t b/test/test_suite/expressions/check_implicit_conversion_signed_unsigned.c3t similarity index 100% rename from test/test_suite/expressions/check_implict_conversion_signed_unsigned.c3t rename to test/test_suite/expressions/check_implicit_conversion_signed_unsigned.c3t diff --git a/test/test_suite/expressions/fail_index_usize.c3 b/test/test_suite/expressions/fail_index_usize.c3 index 438d83cde..f1f113d5b 100644 --- a/test/test_suite/expressions/fail_index_usize.c3 +++ b/test/test_suite/expressions/fail_index_usize.c3 @@ -1,5 +1,5 @@ fn void test(int* array, usz n) { array[n] = 33; - n[array] = 33; // #error: Indexing a value of type + n[array] = 33; // #error: Assigning to a subscript of } diff --git a/test/test_suite/functions/defered_default_arguments.c3t b/test/test_suite/functions/deferred_default_arguments.c3t similarity index 100% rename from test/test_suite/functions/defered_default_arguments.c3t rename to test/test_suite/functions/deferred_default_arguments.c3t diff --git a/test/test_suite/functions/slice_escape.c3 b/test/test_suite/functions/slice_escape.c3 new file mode 100644 index 000000000..57475cb17 --- /dev/null +++ b/test/test_suite/functions/slice_escape.c3 @@ -0,0 +1,16 @@ +fn int* test() +{ + int[3] x = { 1, 2, 4 }; + return (int[])&x; // #error: A pointer to a local variable will be invalid once the function returns. Allocate the data on the heap or temp memory to return a pointer +} +fn int main(String[] args) +{ + MyArray a; + + return 0; +} +struct MyArray +{ + int[] val; +} +fn int* MyArray.get_ref(self, usz idx) => &self.val[idx]; \ No newline at end of file diff --git a/test/test_suite/methods/subscript_set_error.c3 b/test/test_suite/methods/subscript_set_error.c3 new file mode 100644 index 000000000..723c936e1 --- /dev/null +++ b/test/test_suite/methods/subscript_set_error.c3 @@ -0,0 +1,14 @@ +fn int main(String[] args) +{ + MyArray a = {{1, 4, 3, 6, 5}}; + a[0] = a[1]; // #error: Assigning to a subscript of 'MyArray' is not possible + return 0; +} + +struct MyArray +{ + int[] val; +} + +fn int MyArray.get(self, usz idx) @operator([]) => self.val[idx]; +fn usz MyArray.len(self) @operator(len) => self.val.len; \ No newline at end of file diff --git a/test/test_suite/statements/fallthough_do.c3t b/test/test_suite/statements/fallthrough_do.c3t similarity index 100% rename from test/test_suite/statements/fallthough_do.c3t rename to test/test_suite/statements/fallthrough_do.c3t diff --git a/test/test_suite/struct/member_expr.c3 b/test/test_suite/struct/member_expr.c3 index fc0cbbb8d..fac6c326e 100644 --- a/test/test_suite/struct/member_expr.c3 +++ b/test/test_suite/struct/member_expr.c3 @@ -20,12 +20,12 @@ fn void test_unknown_member() } -fn void test_nonstatic_stuct_func1() +fn void test_nonstatic_struct_func1() { Func2 a = &Foo.func2; } -fn void test_nonstatic_stuct_func2() +fn void test_nonstatic_struct_func2() { int b = Foo.func2(null, 2); } diff --git a/test/test_suite/struct/struct_recursive.c3 b/test/test_suite/struct/struct_recursive.c3 index 3e45e317c..ebbfb76c4 100644 --- a/test/test_suite/struct/struct_recursive.c3 +++ b/test/test_suite/struct/struct_recursive.c3 @@ -5,5 +5,5 @@ struct GlobalNode struct Ast { - GlobalNode global; // #error: Recursive defintion of 'GlobalNode' -} \ No newline at end of file + GlobalNode global; // #error: Recursive definition of 'GlobalNode' +} diff --git a/test/test_suite/switch/switch_in_defer_macro.c3t b/test/test_suite/switch/switch_in_defer_macro.c3t index 5f2433cb2..007b66ca0 100644 --- a/test/test_suite/switch/switch_in_defer_macro.c3t +++ b/test/test_suite/switch/switch_in_defer_macro.c3t @@ -409,7 +409,7 @@ alias Kind = Kind{Token, Comment}; enum Token : char (String token) { - KEYWORD1 = "keword1", + KEYWORD1 = "keyword1", KEYWORD2 = "keyword2", SINGLE = "//", MULTI = "/*", @@ -686,11 +686,11 @@ fn void test() @.enum.MULTI = internal constant [6 x i8] c"MULTI\00", align 1 @"$ct.char" = linkonce global %.introspect { i8 3, i64 0, ptr null, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 @"$ct.lexer_test.Token" = linkonce global { i8, i64, ptr, i64, i64, i64, [4 x %"char[]"] } { i8 8, i64 0, ptr null, i64 1, i64 ptrtoint (ptr @"$ct.char" to i64), i64 4, [4 x %"char[]"] [%"char[]" { ptr @.enum.KEYWORD1, i64 8 }, %"char[]" { ptr @.enum.KEYWORD2, i64 8 }, %"char[]" { ptr @.enum.SINGLE, i64 6 }, %"char[]" { ptr @.enum.MULTI, i64 5 }] }, align 8 -@.str = private unnamed_addr constant [8 x i8] c"keword1\00", align 1 +@.str = private unnamed_addr constant [9 x i8] c"keyword1\00", align 1 @.str.1 = private unnamed_addr constant [9 x i8] c"keyword2\00", align 1 @.str.2 = private unnamed_addr constant [3 x i8] c"//\00", align 1 @.str.3 = private unnamed_addr constant [3 x i8] c"/*\00", align 1 -@"lexer_test.Token$token" = linkonce constant [4 x %"char[]"] [%"char[]" { ptr @.str, i64 7 }, %"char[]" { ptr @.str.1, i64 8 }, %"char[]" { ptr @.str.2, i64 2 }, %"char[]" { ptr @.str.3, i64 2 }], align 8 +@"lexer_test.Token$token" = linkonce constant [4 x %"char[]"] [%"char[]" { ptr @.str, i64 8 }, %"char[]" { ptr @.str.1, i64 8 }, %"char[]" { ptr @.str.2, i64 2 }, %"char[]" { ptr @.str.3, i64 2 }], align 8 @"$ct.lexer_test.Comment" = linkonce global { i8, i64, ptr, i64, i64, i64, [2 x %"char[]"] } { i8 8, i64 0, ptr null, i64 1, i64 ptrtoint (ptr @"$ct.char" to i64), i64 2, [2 x %"char[]"] [%"char[]" { ptr @.enum.SINGLE, i64 6 }, %"char[]" { ptr @.enum.MULTI, i64 5 }] }, align 8 @"lexer_test.Comment$start" = linkonce constant [2 x i8] c"\02\03", align 1 @.str.4 = private unnamed_addr constant [2 x i8] c"\0A\00", align 1 diff --git a/test/test_suite/variables/noinit_with_val.c3 b/test/test_suite/variables/noinit_with_val.c3 new file mode 100644 index 000000000..09f114f03 --- /dev/null +++ b/test/test_suite/variables/noinit_with_val.c3 @@ -0,0 +1,10 @@ +fn int main() +{ + test(); + return 0; +} + +macro test() +{ + var x @noinit = 0; // #error: '@noinit' variables may not have initializers +} diff --git a/test/unit/stdlib/collections/anylist.c3 b/test/unit/stdlib/collections/anylist.c3 new file mode 100644 index 000000000..21c5186e0 --- /dev/null +++ b/test/unit/stdlib/collections/anylist.c3 @@ -0,0 +1,28 @@ +module anylist_test @test; +import std::collections::anylist; + +fn void pop() => @pool() +{ + AnyList l; + l.push(1.0); + l.push(1); + l.push("hello"); + assert(l.pop(String)!! == "hello"); + assert(l.pop(int)!! == 1); + assert(l.copy_pop(tmem)!!.type == double.typeid); +} + +fn void predicates() => @pool() +{ + AnyList l; + l.push(123u); + l.push(-1); + l.push("abc"); + l.push(5.0); + l.remove_using_test(fn (x, p) => x.type == p.type, &&456u); + assert(l[0].type == int.typeid); + assert(l.get(0, int)!! == -1); + l.retain_if(fn (x) => x.type == double.typeid); + assert(l.len() == 1); + assert(l.get(0, double)!! == 5.0); +} diff --git a/test/unit/stdlib/collections/interfacelist.c3 b/test/unit/stdlib/collections/interfacelist.c3 new file mode 100644 index 000000000..d00ba6032 --- /dev/null +++ b/test/unit/stdlib/collections/interfacelist.c3 @@ -0,0 +1,126 @@ +module interfacelist_test @test; +import std::collections::interfacelist; +interface Test +{ + fn int test(); +} + +alias TestL = InterfaceList {Test}; + +struct Test1 (Test) +{ + int a; +} +fn int Test1.test(&self) @dynamic => self.a; + +struct Test2 (Test) +{ + String b; +} +fn int Test2.test(&self) @dynamic => (int)self.b.len; + + +fn void initialized() => @pool() +{ + TestL l; + assert(!l.is_initialized()); + l.tinit(); + assert(l.is_initialized()); +} + +fn void basic_interation() => @pool() +{ + TestL l; + l.push((Test1){1}); + l.push((Test1){1234}); + assert(to_ints(l) == {1, 1234}); + assert(l.pop_retained().test()!! == 1234); + l.push((Test1){56789}); + assert(to_ints(l) == {1, 56789}); + l.set(2, (Test2){"abc"}); + assert(to_ints(l) == {1, 56789, 3}); +} + +fn void remove_at() => @pool() +{ + TestL l; + for (int i = 0; i < 5; i++) + { + l.push((Test1){i}); + } + assert(to_ints(l) == {0, 1, 2, 3, 4}); + l.remove_at(1); + assert(to_ints(l) == {0, 2, 3, 4}); + l.remove_at(3); + assert(to_ints(l) == {0, 2, 3}); +} + +fn void remove_with_predicate() => @pool() +{ + TestL l; + l.push((Test1){1}); + l.push((Test1){1234}); + l.push((Test2){"wefhewoifw"}); + l.push((Test1){-1290987}); + l.push((Test2){"abc"}); + assert(to_ints(l) == {1, 1234, 10, -1290987, 3}); + l.remove_if(fn (val) => val.test() < 5); + assert(to_ints(l) == {1234, 10}); + l.remove_if(fn (val) => val.type == Test2.typeid); + assert(to_ints(l) == {1234}); +} + +fn void retain_with_predicate() => @pool() +{ + TestL l; + l.push((Test1){1234}); + l.push((Test1){2345}); + l.push((Test1){3456}); + l.push((Test2){"abc"}); + l.push((Test2){"defg"}); + assert(to_ints(l) == {1234, 2345, 3456, 3, 4}); + l.retain_if(fn (val) => val.test() % 2 == 0); + assert(to_ints(l) == {1234, 3456, 4}); +} + +fn void remove_with_test() => @pool() +{ + TestL l; + l.push((Test1){532}); + l.push((Test2){"hello"}); + l.push((Test2){"abcdef"}); + l.push((Test1){765}); + assert(to_ints(l) == {532, 5, 6, 765}); + l.remove_using_test(fn (x, p) => x.type == p.type, &&(Test1){}); + assert(to_ints(l) == {5, 6}); + l.remove_using_test(fn (x, p) => x.test() == p.test(), &&(Test2){"abcdef"}); + assert(to_ints(l) == {5}); +} + +fn void retain_with_test() => @pool() +{ + TestL l; + l.push((Test1){345}); + l.push((Test1){3535}); + l.push((Test1){7654}); + l.push((Test2){"abdef"}); + l.push((Test1){6432}); + l.push((Test1){585868}); + assert(to_ints(l) == {345, 3535, 7654, 5, 6432, 585868}); + l.retain_using_test(fn (x, p) => x.test() < p.test(), &&(Test1){1000}); + assert(to_ints(l) == {345, 5}); + l.retain_using_test(fn (x, p) => x.type == p.type && x.test() == p.test(), &&(Test1){0}); + assert(to_ints(l) == {}); +} + +module interfacelist_test; + +fn int[] to_ints(TestL l) => @map(tmem, l.array_view(), fn int(Test x) => x.test()); + +import std::core::array @public; +macro @map(Allocator alloc, array, operation) +{ + var res = allocator::alloc_array(alloc, $typeof(operation).returns, array::find_len(array)); + foreach (i, val : array) res[i] = operation(val); + return res; +} diff --git a/test/unit/stdlib/core/string.c3 b/test/unit/stdlib/core/string.c3 index e84932fe7..4f3fb580e 100644 --- a/test/unit/stdlib/core/string.c3 +++ b/test/unit/stdlib/core/string.c3 @@ -273,7 +273,7 @@ fn void contains_char() assert(!test.contains_char('x')); } -fn void test_base_13_convesion() +fn void test_base_13_conversion() { assert("13".to_long(13)!! == 13 + 3); assert("1a".to_long(13)!! == 13 + 10); @@ -382,4 +382,4 @@ fn void test_snake_pascal_self_modify() s2.convert_snake_to_pascal(); test::eq(s2, s[1]); } -} \ No newline at end of file +} diff --git a/test/unit/stdlib/libc/libc.c3 b/test/unit/stdlib/libc/libc.c3 index aa0b88e78..9c55019ff 100644 --- a/test/unit/stdlib/libc/libc.c3 +++ b/test/unit/stdlib/libc/libc.c3 @@ -135,8 +135,8 @@ fn void bsearch() @test key = 6; found = (CInt*) libc::bsearch(&key, int_ar, 7, CInt.sizeof, &compare_cint); assert(*found == 6); - CInt non_existant_key = 12; - found = (CInt*) libc::bsearch(&non_existant_key, int_ar, 7, CInt.sizeof, &compare_cint); + CInt non_existent_key = 12; + found = (CInt*) libc::bsearch(&non_existent_key, int_ar, 7, CInt.sizeof, &compare_cint); assert(found == null); Event[] events = {