diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 799b23da1..99d355242 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -45,7 +45,7 @@ jobs: build-msys2-clang: runs-on: windows-latest - if: ${{ false }} + #if: ${{ false }} strategy: # Don't abort runners if a single one fails fail-fast: false diff --git a/README.md b/README.md index 29b12abaa..ced0d3ec3 100644 --- a/README.md +++ b/README.md @@ -129,13 +129,17 @@ fn void test() ### Current status +The current version of the compiler is alpha release 0.1.0. + It's possible to try out the current C3 compiler in the browser: https://ide.judge0.com/ – this is courtesy of the developer of Judge0. -Design work on C3 is mostly done, but there are some areas that are unfinished, such -as inline asm. Follow the issues [here](https://github.com/c3lang/c3c/issues). +Design work on C3 complete aside from fleshing out details, such as +inline asm. As the standard library work progresses, changes and improvements +to the language will happen continuously. +Follow the issues [here](https://github.com/c3lang/c3c/issues). -If you have any suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues) +If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues) or discuss C3 on its dedicated Discord: [https://discord.gg/qN76R87](https://discord.gg/qN76R87). The compiler should compile on Linux, Windows (under MSVC, Mingw or MSYS2) and MacOS, @@ -228,4 +232,4 @@ MIT licensed. #### Editor plugins -Editor plugins can be found at https://github.com/c3lang/editor-plugins \ No newline at end of file +Editor plugins can be found at https://github.com/c3lang/editor-plugins. \ No newline at end of file diff --git a/lib/std/builtin.c3 b/lib/std/builtin.c3 index 07ca25698..0e532c08e 100644 --- a/lib/std/builtin.c3 +++ b/lib/std/builtin.c3 @@ -3,7 +3,7 @@ // a copy of which can be found in the LICENSE_STDLIB file. module std::builtin; -optenum VarCastResult +fault VarCastResult { TYPE_MISMATCH } @@ -60,6 +60,7 @@ fn void panic(char* message, char *file, char *function, uint line) @autoimport while (stack) { libc::fprintf(@libc::stderr(), " at function %s (%s:%u)\n", stack.function, stack.file, stack.line); + if (stack == stack.prev) break; stack = stack.prev; } diff --git a/lib/std/io.c3 b/lib/std/io.c3 index e8c7b470b..44beb0e7c 100644 --- a/lib/std/io.c3 +++ b/lib/std/io.c3 @@ -67,7 +67,7 @@ enum Seek END = 2 } -optenum IoError +fault IoError { FILE_NOT_FOUND, FILE_NOT_SEEKABLE, diff --git a/lib/std/math.c3 b/lib/std/math.c3 index 96e3253b7..35173cd53 100644 --- a/lib/std/math.c3 +++ b/lib/std/math.c3 @@ -16,7 +16,7 @@ const DIV_PI = 0.318309886183790671537767526745028724; // 1 / pi const DIV_2_PI = 0.636619772367581343075535053490057448; // 2 / pi const DIV_2_SQRTPI = 1.12837916709551257389615890312154517; // 2/sqrt(pi) const SQRT2 = 1.41421356237309504880168872420969808; // sqrt(2) -const DIV_1_SQRT2 = 0.707106781186547524400844362104849039; // 1 / sqrt(2) +const double DIV_1_SQRT2 = 0.707106781186547524400844362104849039; // 1 / sqrt(2) const HALF_MAX = 6.5504e+4; const HALF_MIN = 6.103515625e-5; @@ -127,6 +127,13 @@ fn double ceil(double x) @inline return $$ceil(x); } +/** + * @checked x & 1 + */ +macro bool is_power_of_2(x) +{ + return x != 0 && (x & (x - 1)) == 0; +} diff --git a/lib/std/mem.c3 b/lib/std/mem.c3 index e202b6b3b..e2ea38c28 100644 --- a/lib/std/mem.c3 +++ b/lib/std/mem.c3 @@ -3,11 +3,6 @@ // a copy of which can be found in the LICENSE_STDLIB file. module std::mem; -extern fn void* _malloc(usize bytes) @extname("malloc"); -extern fn void* _realloc(void* ptr, usize bytes) @extname("realloc"); -extern fn void* _calloc(usize bytes, usize elements) @extname("calloc"); -extern fn void _free(void* ptr) @extname("free"); - macro volatile_load(&x) { return $$volatile_load(&x); @@ -18,186 +13,143 @@ macro volatile_store(&x, y) return $$volatile_store(&x, y); } +/** + * @require @math::is_power_of_2(alignment) + **/ +fn usize aligned_offset(usize offset, usize alignment) +{ + return alignment * ((offset + alignment - 1) / alignment); +} + + +/** + * @require @math::is_power_of_2(alignment) + **/ +fn bool ptr_is_aligned(void* ptr, usize alignment) @inline +{ + return (uptr)ptr & ((uptr)alignment - 1) == 0; +} + +fn void copy(char* dst, char* src, usize size) @inline +{ + @memcpy(dst, src, size); +} + +macro void memcpy(void* dst, void* src, usize size, bool $is_volatile = false, usize $dst_align = 0, usize $src_align = 0) +{ + $$memcpy(dst, src, size, $is_volatile, $dst_align, $src_align); +} + +fn void set(void* dst, char val, usize bytes) @inline +{ + @memset(dst, val, bytes); +} + +macro void memset(void* dst, char val, usize bytes, bool $is_volatile = false, usize $dst_align = 0) +{ + $$memset(dst, val, bytes, $is_volatile, $dst_align); +} + +macro bitcast(expr, $Type) +{ + var $size = (usize)($sizeof(expr)); + $assert($size == $Type.sizeof, "Cannot bitcast between types of different size."); + $Type x = void; + @memcpy(&x, &expr, $size, false, $alignof($Type), $alignof(expr)); + return x; +} + + enum AllocationKind { ALLOC, + CALLOC, REALLOC, FREE, } -optenum AllocationFailure +fault AllocationFailure { OUT_OF_MEMORY } -define AllocatorFunction = fn void!(void *data, void** pointer, usize bytes, usize alignment, AllocationKind kind); +private tlocal Allocator thread_allocator = { SYSTEM_ALLOCATOR, null }; struct Allocator { - AllocatorFunction allocation_function; + AllocatorFunction function; void *data; } -fn void copy(char* dst, char* src, usize size) -{ - for (usize i = 0; i < size; i++) dst[i] = src[i]; -} - -fn void! system_malloc_function(void *unused, void** pointer, usize bytes, usize alignment, AllocationKind kind) @inline -{ - switch (kind) - { - case ALLOC: - void* data = _malloc(bytes); - if (!data) return AllocationFailure.OUT_OF_MEMORY!; - *pointer = data; - return; - case REALLOC: - void* data = _realloc(*pointer, bytes); - if (!data) return AllocationFailure.OUT_OF_MEMORY!; - *pointer = data; - return; - case FREE: - _free(*pointer); - *pointer = null; - return; - } - @unreachable(); -} - - -struct SlotAllocator -{ - void* pages; - usize page_size; - usize page_count; - usize bitmask; - usize current_page; -} - -fn void*! SlotAllocator.alloc(SlotAllocator *allocator, usize size) -{ - void* active_page = (char*)(allocator.pages) + allocator.current_page * allocator.page_size; - void** page_pointer = (void**)(active_page); - if (*page_pointer) - { - mem::free(*page_pointer); - *page_pointer = null; - } - if (size > allocator.page_size - $sizeof(page_pointer)) - { - void* mem = mem::_malloc(size); - if (!mem) return AllocationFailure.OUT_OF_MEMORY!; - *page_pointer = mem; - allocator.current_page = (allocator.current_page + 1) & (allocator.bitmask); - return mem; - } - allocator.current_page = (allocator.current_page + 1) & (allocator.bitmask); - return &page_pointer[1]; -} - -struct RingAllocator -{ - char *data; - usize size; - usize offset; -} - - -fn void* RingAllocator.alloc(RingAllocator *allocator, usize size) -{ - if (size > allocator.size) return null; - // Wraparound? If so, start at the beginning. - if (allocator.offset + size > allocator.size) - { - allocator.offset = size; - return allocator.data; - } - void* data = allocator.offset + allocator.data; - allocator.offset = (allocator.offset + size) & allocator.size; - return data; -} - -fn void* RingAllocator.realloc(RingAllocator *allocator, void* ptr, usize size) -{ - if (size > allocator.size) return null; - assert(allocator.data >= ptr && ptr < allocator.data + size, "Realloc on other allocator."); - // 1. The pointer is before the allocator - if (allocator.data + allocator.offset > ptr) - { - if (allocator.data + allocator.size < ptr + size) - { - // 1a. There is not enough space, we need to copy to the start. - usize pointer_offset = ptr - allocator.data; - usize copy_len = pointer_offset + size > allocator.offset ? allocator.offset - pointer_offset : size; - //memcpy(allocator.data, ptr, copy_len); - allocator.offset = size; - return allocator.data; - } - // 1b. There is enough space, so we just change the offset: - allocator.offset = ptr - allocator.data + size; - return ptr; - } - // 2. The pointer is after the allocator - // 2a. Is there sufficient space? - if (ptr + size <= allocator.data + allocator.size) - { - // Good, if so we simply change the offset and return the pointer. - allocator.offset = ptr - allocator.data + size; - return ptr; - } - // 2b. Not sufficient space, we copy to the beginning. - usize pointer_offset = ptr - allocator.data; - usize copy_len = allocator.size - (ptr - allocator.data); - if (copy_len > size) copy_len = size; - //memcpy(allocator.data, ptr, copy_len); - allocator.offset = size; - return allocator.data; -} - -Allocator main_allocator = { &system_malloc_function, null }; - macro malloc($Type) { return ($Type*)(mem::alloc($Type.sizeof)); } -fn void* alloc(usize size, usize count = 1) @inline +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void* alloc(usize size, usize alignment = 0) { - return _malloc(size * count); + return thread_allocator.alloc(size, alignment)!!; } -fn void* calloc(usize size, usize elements = 1) @inline +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void*! alloc_checked(usize size, usize alignment = 0) { - return _calloc(size, elements); -} - -fn void* realloc(void *ptr, usize size) @inline -{ - return _realloc(ptr, size); -} - -fn void free(void* ptr) @inline -{ - _free(ptr); + return thread_allocator.alloc(size, alignment); } -const TEMP_BLOCK_SIZE = 1024; -const TEMP_PAGES = 64; +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void* calloc(usize size, usize alignment = 0) +{ + return thread_allocator.calloc(size, alignment)!!; +} -private char[TEMP_BLOCK_SIZE * TEMP_PAGES] allocator_static_storage; -private void*[TEMP_PAGES] allocator_static_page_storage; +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void*! calloc_checked(usize size, usize alignment = 0) +{ + return thread_allocator.calloc(size, alignment); +} -SlotAllocator default_allocator = { - .pages = &allocator_static_storage, - .page_size = TEMP_BLOCK_SIZE, - .page_count = TEMP_PAGES, - .bitmask = TEMP_PAGES - 1, - .current_page = 0, -}; +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void* realloc(void *ptr, usize new_size, usize alignment = 0) +{ + return thread_allocator.realloc(ptr, new_size, alignment)!!; +} + +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void*! realloc_checked(void *ptr, usize new_size, usize alignment = 0) +{ + return thread_allocator.realloc(ptr, new_size, alignment); +} + +fn void free(void* ptr) +{ + return thread_allocator.free(ptr)!!; +} + +macro void with_allocator(Allocator allocator; @body()) +{ + Allocator old_allocator = thread_allocator; + thread_allocator = allocator; + defer thread_allocator = old_allocator; + @body(); +} fn void*! talloc(usize size) { return default_allocator.alloc(size); -} \ No newline at end of file +} + diff --git a/lib/std/mem_allocator.c3 b/lib/std/mem_allocator.c3 new file mode 100644 index 000000000..faa02252d --- /dev/null +++ b/lib/std/mem_allocator.c3 @@ -0,0 +1,280 @@ +module std::mem; + +define AllocatorFunction = fn void*!(void *data, usize new_size, usize alignment, void* old_pointer, AllocationKind kind); + +const DEFAULT_MEM_ALIGNMENT = $alignof(void*) * 2; + +Allocator main_allocator = { SYSTEM_ALLOCATOR, null }; + +const AllocatorFunction NULL_ALLOCATOR = &null_allocator_fn; +const AllocatorFunction SYSTEM_ALLOCATOR = &libc_allocator_fn; + +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void*! Allocator.alloc(Allocator *allocator, usize size, usize alignment = 0) @inline +{ + return allocator.function(allocator.data, size, alignment, null, ALLOC); +} + +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void*! Allocator.realloc(Allocator *allocator, void* old_pointer, usize size, usize alignment = 0) @inline +{ + return allocator.function(allocator.data, size, alignment, old_pointer, REALLOC); +} + +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void*! Allocator.calloc(Allocator *allocator, usize size, usize alignment = 0) @inline +{ + return allocator.function(allocator.data, size, alignment, null, CALLOC); +} + +fn void! Allocator.free(Allocator *allocator, void* old_pointer) @inline +{ + allocator.function(allocator.data, 0, 0, old_pointer, FREE)?; +} + + +struct ArenaAllocator +{ + void* memory; + void* last_ptr; + usize total; + usize used; +} + +macro void*! allocator_to_function($Type, void* data, usize new_size, usize alignment, void* old_pointer, AllocationKind kind) +{ + $Type* allocator = data; + switch (kind) + { + case ALLOC: + return allocator.alloc(new_size, alignment) @inline; + case CALLOC: + return allocator.calloc(new_size, alignment) @inline; + case REALLOC: + return allocator.realloc(old_pointer, new_size, alignment) @inline; + case FREE: + allocator.free(old_pointer) @inline?; + return null; + } + @unreachable(); +} + +fn void*! arena_allocator_function(void* allocator, usize new_size, usize alignment, void* old_pointer, AllocationKind kind) +{ + return @allocator_to_function(ArenaAllocator, allocator, new_size, alignment, old_pointer, kind); +} + +fn Allocator ArenaAllocator.to_allocator(ArenaAllocator* allocator) @inline +{ + return { &arena_allocator_function, allocator }; +} + + +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void*! ArenaAllocator.alloc(ArenaAllocator* allocator, usize bytes, usize alignment = 0) +{ + if (!bytes) return null; + if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT; + iptr next = aligned_offset((iptr)allocator.memory + allocator.used, alignment); + usize next_after = next - (iptr)allocator.memory + bytes; + if (next_after > allocator.total) return AllocationFailure.OUT_OF_MEMORY!; + allocator.used = next_after; + return allocator.last_ptr = (void*)next; +} + +fn void*! ArenaAllocator.calloc(ArenaAllocator* allocator, usize bytes, usize alignment = 0) +{ + char* bits = allocator.alloc(bytes) @inline?; + mem::set(bits, 0, bytes); + return bits; +} + +/** + * @require ptr != null + * @require allocator != null + **/ +fn void*! ArenaAllocator.realloc(ArenaAllocator* allocator, void *ptr, usize bytes, usize alignment = 0) +{ + if (!ptr) return allocator.alloc(bytes, alignment); + if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT; + + // Is last allocation and alignment matches? + if (allocator.last_ptr == ptr && ptr_is_aligned(ptr, alignment)) + { + usize new_used = (usize)(ptr - allocator.memory) + bytes; + if (new_used > allocator.total) return AllocationFailure.OUT_OF_MEMORY!; + allocator.used = new_used; + return ptr; + } + + // Otherwise just allocate new memory. + void* new_mem = allocator.alloc(bytes, alignment)?; + // And copy too much probably! + copy(new_mem, ptr, (new_mem - ptr) > bytes ? bytes : (usize)(new_mem - ptr)); + return new_mem; +} + +fn void! ArenaAllocator.free(ArenaAllocator* allocator, void* ptr) +{ + if (!ptr) return; + if (ptr == allocator.last_ptr) + { + allocator.used = (usize)(ptr - allocator.memory); + allocator.last_ptr = null; + return; + } +} + +fn void! ArenaAllocator.init(ArenaAllocator* allocator, usize arena_size) +{ + allocator.memory = alloc_checked(arena_size)?; + allocator.total = arena_size; + allocator.used = 0; + allocator.last_ptr = null; +} + +fn void ArenaAllocator.reset(ArenaAllocator* allocator) +{ + allocator.used = 0; + allocator.last_ptr = null; +} + +fn void ArenaAllocator.destroy(ArenaAllocator* allocator) +{ + assert(allocator.memory); + free(allocator.memory); + allocator.total = allocator.used = 0; +} + + +private struct DynamicArenaPage +{ + void* memory; + void* prev_arena; + usize total; + usize used; +} + +struct DynamicArenaAllocator +{ + DynamicArenaPage* page; + usize total; + usize used; + usize page_size; + Allocator allocator; +} + +fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usize page_size, Allocator allocator = { null, null }) +{ + this.page = null; + this.used = this.total = 0; + this.page_size = page_size; + this.allocator = allocator.function ? allocator : thread_allocator; +} + +fn void! DynamicArenaAllocator.reset(DynamicArenaAllocator* this) +{ + DynamicArenaPage* page = this.page; + Allocator allocator = this.allocator; + while (page && page.prev_arena) + { + DynamicArenaPage* next_page = page.prev_arena; + void* mem = page.memory; + allocator.free(page)?; + allocator.free(mem)?; + page = next_page; + } + this.page = page; +} + +fn void*! dynamic_arena_allocator_function(void* allocator, usize new_size, usize alignment, void* old_pointer, AllocationKind kind) +{ + return @allocator_to_function(DynamicArenaAllocator, allocator, new_size, alignment, old_pointer, kind); +} + +fn Allocator DynamicArenaAllocator.to_allocator(DynamicArenaAllocator* this) +{ + return { &dynamic_arena_allocator_function, this }; +} + +fn void! DynamicArenaAllocator.destroy(DynamicArenaAllocator* this) +{ + this.reset(); + DynamicArenaPage* first_page = this.page; + if (!first_page) return; + void* mem = first_page.memory; + this.allocator.free(this.page)?; + this.page = null; + this.allocator.free(mem)?; +} + +fn void! DynamicArenaAllocator.free(DynamicArenaAllocator* allocator, void* ptr) +{ + // This can be made smarter. + return; +} + +/** + * @require @math::is_power_of_2(alignment) + */ +private fn void*! DynamicArenaAllocator.alloc_new(DynamicArenaAllocator* this, usize size, usize alignment) +{ + usize page_size = @max(this.page_size, size); + void* mem = this.allocator.alloc(page_size, alignment)?; + DynamicArenaPage*! page = this.allocator.alloc(DynamicArenaPage.sizeof); + if (catch err = page) + { + this.allocator.free(mem); + return err!; + } + page.memory = mem; + page.prev_arena = this.page; + page.total = page_size; + page.used = size; + this.page = page; + + return page.memory; +} + +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void*! DynamicArenaAllocator.calloc(DynamicArenaAllocator* allocator, usize size, usize alignment = 0) +{ + void* mem = allocator.alloc(size, alignment)?; + set(mem, 0, size); + return mem; +} + +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void*! DynamicArenaAllocator.realloc(DynamicArenaAllocator* allocator, void* ptr, usize size, usize alignment = 0) +{ + void* mem = allocator.alloc(size, alignment)?; + copy(mem, ptr, size); + return mem; +} + +/** + * @require !alignment || @math::is_power_of_2(alignment) + */ +fn void*! DynamicArenaAllocator.alloc(DynamicArenaAllocator* this, usize size, usize alignment) +{ + DynamicArenaPage *page = this.page; + if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT; + if (!page) return this.alloc_new(size, alignment); + usize start = aligned_offset((uptr)page.memory + page.used, alignment) - (usize)page.memory; + usize new_used = start + size; + if (new_used > page.total) return this.alloc_new(size, alignment); + page.used = new_used; + return page.memory + start; +} diff --git a/lib/std/mem_allocator_fn.c3 b/lib/std/mem_allocator_fn.c3 new file mode 100644 index 000000000..270a37000 --- /dev/null +++ b/lib/std/mem_allocator_fn.c3 @@ -0,0 +1,62 @@ +module std::mem; + +private fn void*! null_allocator_fn(void *data, usize bytes, usize alignment, void* old_pointer, AllocationKind kind) +{ + switch (kind) + { + case ALLOC: + case CALLOC: + case REALLOC: + return AllocationFailure.OUT_OF_MEMORY!; + default: + return null; + } +} + +fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* old_pointer, AllocationKind kind) @inline +{ + if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT; + assert(@math::is_power_of_2(alignment), "Alignment was not a power of 2"); + + void* data; + switch (kind) + { + case ALLOC: + if (alignment > DEFAULT_MEM_ALIGNMENT) + { + data = (void*)aligned_offset((iptr)libc::malloc(bytes + alignment), alignment); + } + else + { + data = libc::malloc(bytes); + } + if (!data) return AllocationFailure.OUT_OF_MEMORY!; + return data; + case CALLOC: + if (alignment > DEFAULT_MEM_ALIGNMENT) + { + data = (void*)aligned_offset((iptr)libc::calloc(bytes + alignment, 1), alignment); + } + else + { + data = libc::malloc(bytes); + } + if (!data) return AllocationFailure.OUT_OF_MEMORY!; + return data; + case REALLOC: + if (alignment > DEFAULT_MEM_ALIGNMENT) + { + data = (void*)aligned_offset((iptr)libc::realloc(old_pointer, bytes + alignment), alignment); + } + else + { + data = libc::realloc(old_pointer, bytes); + } + if (!data) return AllocationFailure.OUT_OF_MEMORY!; + return data; + case FREE: + libc::free(old_pointer); + return null; + } + @unreachable(); +} diff --git a/lib/std/mem_temp_allocator.c3 b/lib/std/mem_temp_allocator.c3 new file mode 100644 index 000000000..9fc258b70 --- /dev/null +++ b/lib/std/mem_temp_allocator.c3 @@ -0,0 +1,106 @@ +module std::mem; + +const TEMP_BLOCK_SIZE = 1024; +const TEMP_PAGES = 64; + +private char[TEMP_BLOCK_SIZE * TEMP_PAGES] allocator_static_storage; +private void*[TEMP_PAGES] allocator_static_page_storage; + +SlotAllocator default_allocator = { + .pages = &allocator_static_storage, + .page_size = TEMP_BLOCK_SIZE, + .page_count = TEMP_PAGES, + .bitmask = TEMP_PAGES - 1, + .current_page = 0, +}; + +struct SlotAllocator +{ + void* pages; + usize page_size; + usize page_count; + usize bitmask; + usize current_page; +} + +fn void*! SlotAllocator.alloc(SlotAllocator *allocator, usize size) +{ + void* active_page = (char*)(allocator.pages) + allocator.current_page * allocator.page_size; + void** page_pointer = (void**)(active_page); + if (*page_pointer) + { + // TODO fix + main_allocator.free(*page_pointer)?; + *page_pointer = null; + } + if (size > allocator.page_size - $sizeof(page_pointer)) + { + void* mem = main_allocator.alloc(size)?; + *page_pointer = mem; + allocator.current_page = (allocator.current_page + 1) & (allocator.bitmask); + return mem; + } + allocator.current_page = (allocator.current_page + 1) & (allocator.bitmask); + return &page_pointer[1]; +} + + +struct RingAllocator +{ + char *data; + usize size; + usize offset; +} + + +fn void* RingAllocator.alloc(RingAllocator *allocator, usize size) +{ + if (size > allocator.size) return null; + // Wraparound? If so, start at the beginning. + if (allocator.offset + size > allocator.size) + { + allocator.offset = size; + return allocator.data; + } + void* data = allocator.offset + allocator.data; + allocator.offset = (allocator.offset + size) & allocator.size; + return data; +} + +fn void* RingAllocator.realloc(RingAllocator *allocator, void* ptr, usize size) +{ + if (size > allocator.size) return null; + assert(allocator.data >= ptr && ptr < allocator.data + size, "Realloc on other allocator."); + // 1. The pointer is before the allocator + if (allocator.data + allocator.offset > ptr) + { + if (allocator.data + allocator.size < ptr + size) + { + // 1a. There is not enough space, we need to copy to the start. + usize pointer_offset = ptr - allocator.data; + usize copy_len = pointer_offset + size > allocator.offset ? allocator.offset - pointer_offset : size; + //memcpy(allocator.data, ptr, copy_len); + allocator.offset = size; + return allocator.data; + } + // 1b. There is enough space, so we just change the offset: + allocator.offset = ptr - allocator.data + size; + return ptr; + } + // 2. The pointer is after the allocator + // 2a. Is there sufficient space? + if (ptr + size <= allocator.data + allocator.size) + { + // Good, if so we simply change the offset and return the pointer. + allocator.offset = ptr - allocator.data + size; + return ptr; + } + // 2b. Not sufficient space, we copy to the beginning. + usize pointer_offset = ptr - allocator.data; + usize copy_len = allocator.size - (ptr - allocator.data); + if (copy_len > size) copy_len = size; + //memcpy(allocator.data, ptr, copy_len); + allocator.offset = size; + return allocator.data; +} + diff --git a/resources/examples/base64.c3 b/resources/examples/base64.c3 index cb47a4bc8..67cbddd87 100644 --- a/resources/examples/base64.c3 +++ b/resources/examples/base64.c3 @@ -2,7 +2,7 @@ module base64; // Based on the C2 version. -optenum DecodingError +fault DecodingError { INVALID_CHARACTER } diff --git a/resources/examples/fasta.c3 b/resources/examples/fasta.c3 index 0b3fc6faf..92e9ea2d4 100644 --- a/resources/examples/fasta.c3 +++ b/resources/examples/fasta.c3 @@ -8,10 +8,10 @@ const SEED = 42; uint seed = SEED; -fn float fasta_rand(float max) +fn float fasta_rand(float max_val) { seed = (seed * IA + IC) % IM; - return max * seed / IM; + return max_val * seed / IM; } private char[] alu = diff --git a/resources/examples/guess_number.c3 b/resources/examples/guess_number.c3 index c374b165e..f48637473 100644 --- a/resources/examples/guess_number.c3 +++ b/resources/examples/guess_number.c3 @@ -14,7 +14,7 @@ struct Game int high; } -optnum InputResult +fault InputResult { NOT_AN_INT, FAILED_TO_READ, diff --git a/resources/testfragments/demo_err.c3 b/resources/testfragments/demo_err.c3 new file mode 100644 index 000000000..c7714740a --- /dev/null +++ b/resources/testfragments/demo_err.c3 @@ -0,0 +1,152 @@ +module test; + +import libc; + +struct Doc { Head *head; } +struct Head { char[]* title; } + +struct Summary +{ + char[]* title; + bool ok; +} + +fn void Summary.print(Summary *s, CFile out) +{ + // We don't have a native printf in C3 yet, so use libc, + // which is not all that nice for the strings but... + char[] title = s.title ? *s.title : "missing"; + libc::fprintf(out, "Summary({ .title = %.*s, .ok = %s})", (int)title.len, title.ptr, s.ok ? "true" : "false"); +} + +fn bool contains(char[] haystack, char[] needle) +{ + usize len = haystack.len; + usize needle_len = needle.len; + if (len < needle_len) return false; + if (!needle_len) return true; + len -= needle_len - 1; + for (usize i = 0; i < len; i++) + { + if (libc::memcmp(&haystack[i], needle.ptr, needle_len) == 0) + { + return true; + } + } + return false; +} + +macro dupe(value) +{ + $typeof(&value) temp = mem::alloc_checked($sizeof(value))?; + *temp = value; + return temp; +} + +fault ReadError +{ + BAD_READ, +} + +fn Doc! readDoc(char[] url) +{ + if (contains(url, "fail")) return ReadError.BAD_READ!; + if (contains(url, "head-missing")) return { .head = null }; + if (contains(url, "title-missing")) return { @dupe(Head { .title = null })? }; + if (contains(url, "title-empty")) return { @dupe(Head { .title = @dupe((char[])"")? })? }; + // Not particularly elegant due to missing string functions. + int len = libc::snprintf(null, 0, "Title of %.*s", (int)url.len, url.ptr); + char* str = mem::alloc_checked(len + 1)?; + libc::snprintf(str, len + 1, "Title of %.*s", (int)url.len, url.ptr); + return { @dupe(Head { .title = @dupe(str[..len - 1])? })? }; +} + +fn Summary buildSummary(Doc doc) +{ + return Summary { + .title = doc.head ? doc.head.title : null, + .ok = true, + }; +} + +fn Summary readAndBuildSummary(char[] url) +{ + return buildSummary(readDoc(url)) ?? Summary { .title = null, .ok = false }; + /* + // or + Summary summary = buildSummary(readDoc(url)); + if (catch summary) return Summary { .title = null, .ok = false }; + return summary; + // or + Summary summary = buildSummary(readDoc(url)); + if (try summary) return summary; + return Summary { .title = null, .ok = false }; + */ +} + + +fault TitleResult +{ + TITLE_MISSING +} + +fn bool! isTitleNonEmpty(Doc doc) +{ + if (!doc.head) return TitleResult.TITLE_MISSING!; + char[]* head = doc.head.title; + if (!head) return TitleResult.TITLE_MISSING!; + return (*head).len > 0; +} + + +fn bool! readWhetherTitleNonEmpty(char[] url) +{ + return isTitleNonEmpty(readDoc(url)); +} + +fn char* bool_to_string(bool b) +{ + return b ? "true" : "false"; +} +fn char* nameFromError(anyerr e) +{ + switch (e) + { + case TitleResult.TITLE_MISSING: + return "no title"; + case ReadError.BAD_READ: + return "bad read"; + case AllocationFailure.OUT_OF_MEMORY: + return "out of memory"; + default: + return "unknown error"; + } +} + + +fn void main() +{ + const char[][] URLS = { "good", "title-empty", "title-missing", "head-missing", "fail" }; + DynamicArenaAllocator allocator; + allocator.init(1024); + foreach (char[] url : URLS) + { + @mem::with_allocator(allocator.to_allocator()) + { + // Yes, it's pretty onerous to print strings for the moment in C3 + libc::printf(`Checking "https://%.*s/":` "\n", (int)url.len, url.ptr); + Summary summary = readAndBuildSummary(url); + libc::printf(" Summary: "); + summary.print(@libc::stdout()); + libc::printf("\n"); + char[] title_sure = summary.title ? *summary.title : ""; + libc::printf(" Title: %.*s\n", (int)title_sure.len, title_sure.ptr); + bool! has_title = readWhetherTitleNonEmpty(url); + // This looks a bit less than elegant, but as you see it's mostly due to having to + // use printf here. + libc::printf(" Has title: %s vs %s\n", bool_to_string(has_title) ?? nameFromError(catch(has_title)), (has_title ?? false) ? "true" : "false"); + }; + allocator.reset(); + } + allocator.destroy(); +} diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 32d876e1d..f1026a7ac 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -68,10 +68,10 @@ const char *decl_to_name(Decl *decl) return "enum"; case DECL_ENUM_CONSTANT: return "enum value"; - case DECL_OPTVALUE: + case DECL_FAULTVALUE: return "err value"; - case DECL_OPTENUM: - return "optenum"; + case DECL_FAULT: + return "fault"; case DECL_FUNC: return "function"; case DECL_GENERIC: @@ -153,8 +153,8 @@ Decl *decl_new_with_type(const char *name, SourceSpan loc, DeclKind decl_type, V case DECL_STRUCT: kind = TYPE_STRUCT; break; - case DECL_OPTENUM: - kind = TYPE_ERRTYPE; + case DECL_FAULT: + kind = TYPE_FAULTTYPE; break; case DECL_ENUM: kind = TYPE_ENUM; @@ -171,7 +171,7 @@ Decl *decl_new_with_type(const char *name, SourceSpan loc, DeclKind decl_type, V case DECL_POISONED: case DECL_VAR: case DECL_ENUM_CONSTANT: - case DECL_OPTVALUE: + case DECL_FAULTVALUE: case DECL_IMPORT: case DECL_MACRO: case DECL_GENERIC: diff --git a/src/compiler/codegen_general.c b/src/compiler/codegen_general.c index 48d57beca..33f4d8fdd 100644 --- a/src/compiler/codegen_general.c +++ b/src/compiler/codegen_general.c @@ -187,7 +187,7 @@ bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements) *elements = 2; return true; case TYPE_ANYERR: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: type = type_iptr; goto RETRY; case TYPE_TYPEDEF: diff --git a/src/compiler/codegen_internal.h b/src/compiler/codegen_internal.h index 6643dd032..e73d138ff 100644 --- a/src/compiler/codegen_internal.h +++ b/src/compiler/codegen_internal.h @@ -12,7 +12,7 @@ static inline Type *type_lowering(Type *type) if (canonical->type_kind == TYPE_ENUM) return canonical->decl->enums.type_info->type->canonical; if (canonical->type_kind == TYPE_TYPEID) return type_iptr->canonical; if (canonical->type_kind == TYPE_ANYERR) return type_iptr->canonical; - if (canonical->type_kind == TYPE_ERRTYPE) return type_iptr->canonical; + if (canonical->type_kind == TYPE_FAULTTYPE) return type_iptr->canonical; if (canonical->type_kind == TYPE_BITSTRUCT) return type_lowering(canonical->decl->bitstruct.base_type->type); return canonical; } diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index b3764e559..7cd36dadc 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -419,6 +419,7 @@ typedef struct Expr *expr; Expr **args; uint64_t ordinal; + DeclId parent; } EnumConstantDecl; @@ -584,6 +585,7 @@ typedef struct Decl_ bool is_value : 1; bool is_autoimport : 1; bool has_extname : 1; + bool is_external_visible : 1; OperatorOverload operator : 3; union { @@ -617,7 +619,7 @@ typedef struct Decl_ Decl **methods; union { - // Unions, Optenum and Struct use strukt + // Unions, Fault and Struct use strukt StructDecl strukt; EnumDecl enums; DistinctDecl distinct_decl; @@ -1339,7 +1341,7 @@ typedef struct CompilationUnit_ Decl **types; Decl **functions; Decl **enums; - Decl **errtypes; + Decl **faulttypes; struct { // Not properly implemented @@ -1356,7 +1358,6 @@ typedef struct CompilationUnit_ Decl **global_decls; Decl *main_function; HTable local_symbols; - Decl **external_symbol_list; struct { void *debug_file; @@ -1951,6 +1952,7 @@ Decl *sema_resolve_normal_symbol(SemaContext *context, NameResolve *name_resolve Decl *sema_find_symbol(SemaContext *context, const char *symbol); Decl *sema_find_path_symbol(SemaContext *context, const char *symbol, Path *path); Decl *sema_resolve_symbol(SemaContext *context, const char *symbol, Path *path, SourceSpan span); +bool sema_symbol_is_defined_in_scope(SemaContext *c, const char *symbol); bool sema_resolve_type(SemaContext *context, Type *type); bool sema_resolve_array_like_len(SemaContext *context, TypeInfo *type_info, ArraySize *len_ref); @@ -2399,7 +2401,7 @@ static inline bool decl_is_struct_type(Decl *decl) static inline bool decl_is_enum_kind(Decl *decl) { DeclKind kind = decl->decl_kind; - return (kind == DECL_ENUM) | (kind == DECL_OPTENUM); + return (kind == DECL_ENUM) | (kind == DECL_FAULT); } static inline bool decl_is_user_defined_type(Decl *decl) @@ -2526,6 +2528,11 @@ static inline void ast_prepend(AstId *first, Ast *ast) *first = astid(ast); } +static inline bool visible_external(Visibility visibility) +{ + return visibility == VISIBLE_PUBLIC || visibility == VISIBLE_EXTERN; +} + static inline Ast *ast_next(AstId *current_ptr) { Ast *ast = astptr(*current_ptr); diff --git a/src/compiler/context.c b/src/compiler/context.c index 70642dcc1..e42cf88ed 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -123,11 +123,7 @@ bool context_set_module(ParseContext *context, Path *path, const char **generic_ void unit_register_external_symbol(CompilationUnit *unit, Decl *decl) { if (!decl->module || decl->module == unit->module || !decl->extname) return; - VECEACH(unit->external_symbol_list, i) - { - if (decl == unit->external_symbol_list[i]) return; - } - vec_add(unit->external_symbol_list, decl); + decl->is_external_visible = true; } @@ -144,7 +140,7 @@ void decl_register(Decl *decl) case DECL_CT_SWITCH: case DECL_CT_ASSERT: case DECL_ENUM_CONSTANT: - case DECL_OPTVALUE: + case DECL_FAULTVALUE: case DECL_IMPORT: case DECL_LABEL: case DECL_DECLARRAY: @@ -153,7 +149,7 @@ void decl_register(Decl *decl) case DECL_ATTRIBUTE: case DECL_BITSTRUCT: case DECL_DISTINCT: - case DECL_OPTENUM: + case DECL_FAULT: case DECL_ENUM: case DECL_STRUCT: case DECL_TYPEDEF: @@ -226,7 +222,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) case DECL_STRUCT: case DECL_UNION: case DECL_TYPEDEF: - case DECL_OPTENUM: + case DECL_FAULT: case DECL_BITSTRUCT: assert(decl->name); vec_add(unit->types, decl); @@ -245,7 +241,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) decl_set_external_name(decl); decl_register(decl); break; - case DECL_OPTVALUE: + case DECL_FAULTVALUE: case DECL_ENUM_CONSTANT: case DECL_IMPORT: case DECL_CT_ELSE: diff --git a/src/compiler/copying.c b/src/compiler/copying.c index c3a54a1f4..a92d2f56a 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -600,7 +600,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) case DECL_BITSTRUCT: UNREACHABLE case DECL_ENUM: - case DECL_OPTENUM: + case DECL_FAULT: copy_decl_type(copy); MACRO_COPY_DECL_LIST(copy->methods); MACRO_COPY_DECL_LIST(copy->enums.parameters); @@ -631,7 +631,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) MACRO_COPY_EXPR(copy->enum_constant.expr); MACRO_COPY_EXPR_LIST(copy->enum_constant.args); break; - case DECL_OPTVALUE: + case DECL_FAULTVALUE: MACRO_COPY_EXPR(copy->enum_constant.expr); MACRO_COPY_EXPR_LIST(copy->enum_constant.args); break; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index f940160be..436e2ce5e 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -132,8 +132,8 @@ typedef enum DECL_DISTINCT, DECL_ENUM, DECL_ENUM_CONSTANT, - DECL_OPTENUM, - DECL_OPTVALUE, + DECL_FAULT, + DECL_FAULTVALUE, DECL_FUNC, DECL_GENERIC, DECL_IMPORT, @@ -439,6 +439,7 @@ typedef enum TOKEN_ENUM, TOKEN_EXTERN, TOKEN_FALSE, + TOKEN_FAULT, TOKEN_FOR, TOKEN_FOREACH, TOKEN_FN, @@ -462,10 +463,6 @@ typedef enum TOKEN_VAR, TOKEN_WHILE, - TOKEN_ERRNUM, - TOKEN_OPTNUM, - TOKEN_OPTENUM, - TOKEN_CT_ALIGNOF, // $alignof TOKEN_CT_ASSERT, // $assert TOKEN_CT_CASE, // $case @@ -558,7 +555,7 @@ typedef enum TYPE_STRUCT, TYPE_UNION, TYPE_BITSTRUCT, - TYPE_ERRTYPE, + TYPE_FAULTTYPE, TYPE_TYPEDEF, TYPE_DISTINCT, TYPE_ARRAY, @@ -722,6 +719,8 @@ typedef enum BUILTIN_FMA, BUILTIN_VOLATILE_LOAD, BUILTIN_VOLATILE_STORE, + BUILTIN_MEMCOPY, + BUILTIN_MEMSET, BUILTIN_NONE, NUMBER_OF_BUILTINS = BUILTIN_NONE, } BuiltinFunction; diff --git a/src/compiler/headers.c b/src/compiler/headers.c index f7a452686..53de3e935 100644 --- a/src/compiler/headers.c +++ b/src/compiler/headers.c @@ -94,7 +94,7 @@ static void header_print_type(FILE *file, Type *type) OUTPUT("*"); return; case TYPE_ENUM: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: OUTPUT("enum %s__", type->decl->extname); return; case TYPE_FLEXIBLE_ARRAY: @@ -191,7 +191,7 @@ static void header_gen_decl(FILE *file, int indent, Decl *decl) { case NON_TYPE_DECLS: case DECL_ENUM_CONSTANT: - case DECL_OPTVALUE: + case DECL_FAULTVALUE: case DECL_POISONED: case DECL_VAR: case DECL_BODYPARAM: @@ -211,7 +211,7 @@ static void header_gen_decl(FILE *file, int indent, Decl *decl) case DECL_ENUM: header_gen_enum(file, indent, decl); return; - case DECL_OPTENUM: + case DECL_FAULT: header_gen_err(file, indent, decl); return; } diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index fc70a23b3..577c29b8d 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -7,6 +7,8 @@ const char* llvm_version = LLVM_VERSION_STRING; const char* llvm_target = LLVM_DEFAULT_TARGET_TRIPLE; +void llvm_emit_local_global_variable_definition(GenContext *c, Decl *decl); + static void diagnostics_handler(LLVMDiagnosticInfoRef ref, void *context) { char *message = LLVMGetDiagInfoDescription(ref); @@ -253,7 +255,7 @@ LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_ } -static void gencontext_emit_global_variable_definition(GenContext *c, Decl *decl) +void llvm_emit_local_global_variable_definition(GenContext *c, Decl *decl) { assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST); @@ -305,6 +307,23 @@ void llvm_emit_ptr_from_array(GenContext *c, BEValue *value) } } +void llvm_set_global_tls(Decl *decl) +{ + if (!decl->var.is_threadlocal) return; + LLVMThreadLocalMode thread_local_mode = LLVMGeneralDynamicTLSModel; + if (!decl->var.is_addr && (decl->visibility == VISIBLE_LOCAL || decl->visibility == VISIBLE_MODULE) && !decl->is_external_visible) + { + thread_local_mode = LLVMLocalDynamicTLSModel; + } + LLVMSetThreadLocal(decl->backend_ref, true); + LLVMSetThreadLocalMode(decl->backend_ref, thread_local_mode); + void *failable_ref = decl->var.failable_ref; + if (failable_ref) + { + LLVMSetThreadLocal(failable_ref, true); + LLVMSetThreadLocalMode(failable_ref, thread_local_mode); + } +} void llvm_set_internal_linkage(LLVMValueRef alloc) { LLVMSetLinkage(alloc, LLVMInternalLinkage); @@ -365,28 +384,27 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) // TODO fix name LLVMValueRef old = decl->backend_ref; LLVMValueRef global_ref = decl->backend_ref = LLVMAddGlobal(c->module, LLVMTypeOf(init_value), decl->extname); - LLVMSetThreadLocal(global_ref, decl->var.is_threadlocal); if (decl->var.is_addr) { LLVMSetUnnamedAddress(global_ref, LLVMNoUnnamedAddr); } else { - LLVMUnnamedAddr addr = LLVMLocalUnnamedAddr; - if (decl->visibility == VISIBLE_LOCAL || decl->visibility == VISIBLE_MODULE) addr = LLVMGlobalUnnamedAddr; + LLVMUnnamedAddr addr = LLVMGlobalUnnamedAddr; + if (decl->is_external_visible || visible_external(decl->visibility)) addr = LLVMLocalUnnamedAddr; LLVMSetUnnamedAddress(decl->backend_ref, addr); } if (decl->section) { LLVMSetSection(global_ref, decl->section); } + llvm_set_global_tls(decl); llvm_set_alignment(global_ref, alignment); LLVMValueRef failable_ref = decl->var.failable_ref; if (failable_ref) { llvm_set_alignment(failable_ref, type_alloca_alignment(type_anyerr)); - LLVMSetThreadLocal(failable_ref, decl->var.is_threadlocal); LLVMSetUnnamedAddress(failable_ref, LLVMGlobalUnnamedAddr); } if (init_expr && IS_FAILABLE(init_expr) && init_expr->expr_kind == EXPR_FAILABLE) @@ -404,7 +422,9 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) LLVMSetGlobalConstant(global_ref, decl->var.kind == VARDECL_CONST); - switch (decl->visibility) + Visibility visibility = decl->visibility; + if (decl->is_external_visible) visibility = VISIBLE_PUBLIC; + switch (visibility) { case VISIBLE_MODULE: LLVMSetVisibility(global_ref, LLVMProtectedVisibility); @@ -623,6 +643,9 @@ void llvm_codegen_setup() intrinsic_id.umax = lookup_intrinsic("llvm.umax"); intrinsic_id.umin = lookup_intrinsic("llvm.umin"); + intrinsic_id.memset = lookup_intrinsic("llvm.memset"); + intrinsic_id.memcpy = lookup_intrinsic("llvm.memcpy"); + attribute_id.noinline = lookup_attribute("noinline"); attribute_id.optnone = lookup_attribute("optnone"); attribute_id.alwaysinline = lookup_attribute("alwaysinline"); @@ -651,7 +674,9 @@ void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value) LLVMSetVisibility(value, LLVMDefaultVisibility); return; } - switch (decl->visibility) + Visibility visibility = decl->visibility; + if (decl->is_external_visible) visibility = VISIBLE_PUBLIC; + switch (visibility) { case VISIBLE_MODULE: case VISIBLE_PUBLIC: @@ -729,7 +754,7 @@ bool llvm_value_is_const(BEValue *value) } -void llvm_value_set_decl(BEValue *value, Decl *decl) +void llvm_value_set_decl(GenContext *c, BEValue *value, Decl *decl) { decl = decl_flatten(decl); if (decl->is_value) @@ -737,7 +762,7 @@ void llvm_value_set_decl(BEValue *value, Decl *decl) llvm_value_set(value, decl->backend_value, decl->type); return; } - llvm_value_set_decl_address(value, decl); + llvm_value_set_decl_address(c, value, decl); } @@ -772,13 +797,13 @@ static void llvm_emit_type_decls(GenContext *context, Decl *decl) // TODO break; case DECL_ENUM_CONSTANT: - case DECL_OPTVALUE: + case DECL_FAULTVALUE: // TODO break;; case DECL_STRUCT: case DECL_UNION: case DECL_ENUM: - case DECL_OPTENUM: + case DECL_FAULT: case DECL_BITSTRUCT: llvm_emit_introspection_type_from_decl(context, decl); break; @@ -849,6 +874,87 @@ const char *llvm_codegen(void *context) return object_name; } +void llvm_add_global(GenContext *c, Decl *decl) +{ + assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST); + + const char *name = decl->module == c->code_module ? "tempglobal" : decl_get_extname(decl); + decl->backend_ref = LLVMAddGlobal(c->module, llvm_get_type(c, type_lowering(type_no_fail(decl->type))), name); + if (IS_FAILABLE(decl)) + { + scratch_buffer_clear(); + scratch_buffer_append(decl_get_extname(decl)); + scratch_buffer_append(".f"); + decl->var.failable_ref = LLVMAddGlobal(c->module, llvm_get_type(c, type_anyerr), scratch_buffer_to_string()); + } + llvm_set_global_tls(decl); +} + +LLVMValueRef llvm_get_fault_ref(GenContext *c, Decl *decl) +{ + llvm_get_ref(c, decl); + decl = decl_flatten(decl); + if (decl->decl_kind != DECL_VAR) return NULL; + return decl->var.failable_ref; +} + +LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl) +{ + LLVMValueRef backend_ref = decl->backend_ref; + if (backend_ref) + { + if (!LLVMIsAGlobalValue(backend_ref) || LLVMGetGlobalParent(backend_ref) == c->module) return backend_ref; + } + switch (decl->decl_kind) + { + case DECL_VAR: + if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_UNWRAPPED) + { + return decl->backend_ref = llvm_get_ref(c, decl->var.alias); + } + assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST); + llvm_add_global(c, decl); + return decl->backend_ref; + case DECL_FUNC: + backend_ref = decl->backend_ref = LLVMAddFunction(c->module, decl_get_extname(decl), llvm_get_type(c, decl->type)); + if (decl->module == c->code_module && !decl->is_external_visible && !visible_external(decl->visibility)) + { + llvm_set_internal_linkage(backend_ref); + } + return backend_ref; + case DECL_DEFINE: + if (decl->define_decl.define_kind != DEFINE_TYPE_GENERIC) return llvm_get_ref(c, decl->define_decl.alias); + UNREACHABLE + case DECL_FAULTVALUE: + llvm_emit_introspection_type_from_decl(c, declptr(decl->enum_constant.parent)); + assert(decl->backend_ref); + return decl->backend_ref; + case DECL_POISONED: + case DECL_ATTRIBUTE: + case DECL_BITSTRUCT: + case DECL_CT_CASE: + case DECL_CT_ELIF: + case DECL_CT_ELSE: + case DECL_CT_IF: + case DECL_CT_SWITCH: + case DECL_CT_ASSERT: + case DECL_DISTINCT: + case DECL_ENUM: + case DECL_ENUM_CONSTANT: + case DECL_FAULT: + case DECL_GENERIC: + case DECL_IMPORT: + case DECL_LABEL: + case DECL_MACRO: + case DECL_STRUCT: + case DECL_TYPEDEF: + case DECL_UNION: + case DECL_DECLARRAY: + case DECL_BODYPARAM: + UNREACHABLE; + } + UNREACHABLE +} void *llvm_gen(Module *module) { if (!vec_size(module->units)) return NULL; @@ -857,12 +963,6 @@ void *llvm_gen(Module *module) gencontext_init(gen_context, module); gencontext_begin_module(gen_context); - // Declare the panic function implicitly - Decl *panicfn = gen_context->panicfn; - if (panicfn && panicfn->module != module) - { - llvm_emit_extern_decl(gen_context, panicfn); - } VECEACH(module->units, j) { @@ -871,15 +971,6 @@ void *llvm_gen(Module *module) gen_context->debug.compile_unit = unit->llvm.debug_compile_unit; gen_context->debug.file = unit->llvm.debug_file; - - VECEACH(unit->external_symbol_list, i) - { - Decl *d = unit->external_symbol_list[i]; - // Avoid duplicating symbol - if (d->module == unit->module) continue; - if (d == panicfn) continue; - llvm_emit_extern_decl(gen_context, unit->external_symbol_list[i]); - } VECEACH(unit->methods, i) { llvm_emit_function_decl(gen_context, unit->methods[i]); @@ -903,7 +994,7 @@ void *llvm_gen(Module *module) VECEACH(unit->vars, i) { - gencontext_emit_global_variable_definition(gen_context, unit->vars[i]); + llvm_get_ref(gen_context, unit->vars[i]); } VECEACH(unit->vars, i) { diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index 5b84f84a8..eb117f476 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -386,7 +386,7 @@ static void x64_classify(Type *type, ByteSize offset_base, X64Class *lo_class, X case TYPE_FUNC: case TYPE_DISTINCT: case TYPE_ANYERR: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: case TYPE_BITSTRUCT: case TYPE_FAILABLE: case TYPE_FAILABLE_ANY: @@ -578,7 +578,7 @@ AbiType x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_typ case TYPE_TYPEDEF: case TYPE_DISTINCT: case TYPE_ANYERR: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: case TYPE_BITSTRUCT: case TYPE_FAILABLE: case TYPE_FAILABLE_ANY: diff --git a/src/compiler/llvm_codegen_c_abi_x86.c b/src/compiler/llvm_codegen_c_abi_x86.c index d1a0377ea..8a94c4b67 100644 --- a/src/compiler/llvm_codegen_c_abi_x86.c +++ b/src/compiler/llvm_codegen_c_abi_x86.c @@ -114,7 +114,7 @@ static bool x86_should_return_type_in_reg(Type *type) case TYPE_TYPEDEF: case TYPE_DISTINCT: case TYPE_ENUM: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: case TYPE_TYPEID: case TYPE_ANYERR: case TYPE_BITSTRUCT: @@ -579,7 +579,7 @@ static ABIArgInfo *x86_classify_argument(CallABI call, Regs *regs, Type *type) case TYPE_VOID: case TYPE_ENUM: case TYPE_ANYERR: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: case TYPE_DISTINCT: case TYPE_FUNC: case TYPE_TYPEID: diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index 771694bd9..81ac154f2 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -550,7 +550,7 @@ static inline LLVMMetadataRef llvm_get_debug_type_internal(GenContext *c, Type * return type->backend_debug_type = llvm_debug_pointer_type(c, type); case TYPE_ENUM: return type->backend_debug_type = llvm_debug_enum_type(c, type, scope); - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: return type->backend_debug_type = llvm_debug_enum_type(c, type, scope); case TYPE_FUNC: return type->backend_debug_type = llvm_debug_func_type(c, type); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index a2ac72d0a..227d09145 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -3319,7 +3319,7 @@ void llvm_emit_derived_backend_type(GenContext *c, Type *type) case TYPE_STRUCT: case TYPE_UNION: case TYPE_BITSTRUCT: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: case TYPE_DISTINCT: origin = type->decl; continue; @@ -3991,7 +3991,7 @@ static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) LLVMValueRef value; if (decl) { - value = LLVMBuildPtrToInt(c->builder, decl->backend_ref, llvm_get_type(c, type_anyerr), ""); + value = LLVMBuildPtrToInt(c->builder, llvm_get_ref(c, decl), llvm_get_type(c, type_anyerr), ""); } else { @@ -4004,7 +4004,7 @@ static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) { Decl *decl = expr->const_expr.enum_val; return llvm_emit_const_expr(c, be_value, expr->const_expr.err_val->enum_constant.expr); - assert(decl->decl_kind == DECL_OPTVALUE); + assert(decl->decl_kind == DECL_FAULTVALUE); llvm_value_set(be_value, LLVMBuildPtrToInt(c->builder, decl->backend_ref, llvm_get_type(c, type_anyerr), ""), type_anyerr); @@ -4057,7 +4057,7 @@ static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVM case TYPE_FUNC: case TYPE_DISTINCT: case TYPE_ENUM: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: case TYPE_ANYERR: case TYPE_BITSTRUCT: case TYPE_FAILABLE: @@ -4229,17 +4229,54 @@ void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_poin static void llvm_emit_intrinsic_expr(GenContext *c, unsigned intrinsic, BEValue *be_value, Expr *expr) { unsigned arguments = vec_size(expr->call_expr.arguments); - assert(arguments < 5 && "Only has room for 4"); - LLVMValueRef arg_results[4]; + assert(arguments < 10 && "Only has room for 10"); + LLVMValueRef arg_results[10]; + if (intrinsic == intrinsic_id.memcpy) arguments -= 2; + if (intrinsic == intrinsic_id.memset) arguments--; + + Expr **args = expr->call_expr.arguments; for (unsigned i = 0; i < arguments; i++) { - llvm_emit_expr(c, be_value, expr->call_expr.arguments[i]); + llvm_emit_expr(c, be_value, args[i]); llvm_value_rvalue(c, be_value); arg_results[i] = be_value->value; } - LLVMTypeRef call_type = expr->type == type_void ? NULL : llvm_get_type(c, expr->type); - LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, &call_type, call_type ? 1 : 0, arg_results, arguments); + LLVMTypeRef call_type[3]; + int call_args = 0; + if (expr->type != type_void) + { + call_args = 1; + call_type[0] = llvm_get_type(c, expr->type); + } + else if (intrinsic == intrinsic_id.memcpy) + { + call_type[0] = call_type[1] = llvm_get_type(c, type_voidptr); + call_type[2] = llvm_get_type(c, type_usize); + call_args = 3; + } + else if (intrinsic == intrinsic_id.memset) + { + call_type[0] = llvm_get_type(c, type_voidptr); + call_type[1] = llvm_get_type(c, type_usize); + call_args = 2; + } + LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, call_type, call_args, arg_results, arguments); llvm_value_set(be_value, result, expr->type); + if (intrinsic == intrinsic_id.memcpy) + { + assert(args[4]->const_expr.const_kind == CONST_INTEGER); + assert(args[5]->const_expr.const_kind == CONST_INTEGER); + uint64_t dst_align = int_to_u64(args[4]->const_expr.ixx); + uint64_t src_align = int_to_u64(args[5]->const_expr.ixx); + if (dst_align > 0) llvm_attribute_add_call(c, result, attribute_id.align, 1, dst_align); + if (src_align > 0) llvm_attribute_add_call(c, result, attribute_id.align, 2, src_align); + } + else if (intrinsic == intrinsic_id.memset) + { + assert(args[4]->const_expr.const_kind == CONST_INTEGER); + uint64_t dst_align = int_to_u64(args[4]->const_expr.ixx); + if (dst_align > 0) llvm_attribute_add_call(c, result, attribute_id.align, 1, dst_align); + } } @@ -4430,6 +4467,10 @@ unsigned llvm_get_intrinsic(BuiltinFunction func) return intrinsic_id.pow; case BUILTIN_EXP: return intrinsic_id.exp; + case BUILTIN_MEMCOPY: + return intrinsic_id.memcpy; + case BUILTIN_MEMSET: + return intrinsic_id.memset; case BUILTIN_VOLATILE_STORE: case BUILTIN_VOLATILE_LOAD: UNREACHABLE @@ -4574,7 +4615,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 2b. Set signature, function and function type prototype = function_decl->type->func.prototype; - func = function_decl->backend_ref; + func = llvm_get_ref(c, function_decl); assert(func); func_type = llvm_get_type(c, function_decl->type); } @@ -4955,8 +4996,10 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 17h. Assign the return param to be_value. *result_value = synthetic_return_param; + if (c->debug.last_ptr) llvm_store(c, c->debug.last_ptr, c->debug.stack_slot, type_alloca_alignment(type_voidptr)); return; } + if (c->debug.last_ptr) llvm_store(c, c->debug.last_ptr, c->debug.stack_slot, type_alloca_alignment(type_voidptr)); // 17i. The simple case here is where there is a normal return. // In this case be_value already holds the result @@ -5294,7 +5337,7 @@ void llvm_emit_try_unwrap(GenContext *c, BEValue *value, Expr *expr) else { llvm_emit_local_decl(c, expr->try_unwrap_expr.decl); - llvm_value_set_decl_address(&addr, expr->try_unwrap_expr.decl); + llvm_value_set_decl_address(c, &addr, expr->try_unwrap_expr.decl); } assert(llvm_value_is_addr(&addr)); llvm_emit_try_assign_try_catch(c, true, value, &addr, NULL, expr->try_unwrap_expr.failable); @@ -5310,7 +5353,7 @@ void llvm_emit_catch_unwrap(GenContext *c, BEValue *value, Expr *expr) else if (expr->catch_unwrap_expr.decl) { llvm_emit_local_decl(c, expr->catch_unwrap_expr.decl); - llvm_value_set_decl_address(&addr, expr->catch_unwrap_expr.decl); + llvm_value_set_decl_address(c, &addr, expr->catch_unwrap_expr.decl); } else { @@ -5410,8 +5453,8 @@ static inline void llvm_emit_argv_to_subarray(GenContext *c, BEValue *value, Exp EMIT_LOC(c, expr); BEValue argc_value; BEValue argv_value; - llvm_value_set_decl(&argc_value, expr->argv_expr.argc); - llvm_value_set_decl(&argv_value, expr->argv_expr.argv); + llvm_value_set_decl(c, &argc_value, expr->argv_expr.argc); + llvm_value_set_decl(c, &argv_value, expr->argv_expr.argv); llvm_value_rvalue(c, &argc_value); llvm_value_rvalue(c, &argv_value); LLVMValueRef argv_ptr = argv_value.value; @@ -5522,7 +5565,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) UNREACHABLE; case EXPR_DECL: llvm_emit_local_decl(c, expr->decl_expr); - llvm_value_set_decl_address(value, expr->decl_expr); + llvm_value_set_decl_address(c, value, expr->decl_expr); return; case EXPR_SLICE_ASSIGN: llvm_emit_slice_assign(c, value, expr); @@ -5592,7 +5635,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) // These are folded in the semantic analysis step. UNREACHABLE case EXPR_IDENTIFIER: - llvm_value_set_decl(value, expr->identifier_expr.decl); + llvm_value_set_decl(c, value, expr->identifier_expr.decl); return; case EXPR_SUBSCRIPT: case EXPR_SUBSCRIPT_ADDR: diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 2ae2e5a1b..7422687a5 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -603,7 +603,8 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) { assert(decl->decl_kind == DECL_FUNC); // Resolve function backend type for function. - LLVMValueRef function = LLVMAddFunction(c->module, decl->extname, llvm_get_type(c, decl->type)); + + LLVMValueRef function = llvm_get_ref(c, decl); decl->backend_ref = function; FunctionPrototype *prototype = decl->type->func.prototype; @@ -645,7 +646,9 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) } LLVMSetFunctionCallConv(function, llvm_call_convention_from_call(prototype->call_abi)); - switch (decl->visibility) + Visibility visibility = decl->visibility; + if (decl->is_external_visible) visibility = VISIBLE_PUBLIC; + switch (visibility) { case VISIBLE_EXTERN: LLVMSetLinkage(function, decl->func_decl.attr_weak ? LLVMExternalWeakLinkage : LLVMExternalLinkage); @@ -682,42 +685,3 @@ void llvm_emit_methods(GenContext *c, Decl **methods) } } -void llvm_emit_extern_decl(GenContext *context, Decl *decl) -{ - const char *name; - switch (decl->decl_kind) - { - case DECL_POISONED: - UNREACHABLE; - case DECL_FUNC: - name = decl_get_extname(decl); - decl->backend_ref = LLVMAddFunction(context->module, name, - llvm_get_type(context, decl->type)); - LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); - break; - case DECL_VAR: - name = decl_get_extname(decl); - decl->backend_ref = LLVMAddGlobal(context->module, llvm_get_type(context, decl->type), name); - LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); - break; - case DECL_BITSTRUCT: - case DECL_STRUCT: - case DECL_UNION: - llvm_get_type(context, decl->type); - // TODO // Fix typeid - break; - case DECL_ENUM: - break; - case DECL_OPTENUM: - llvm_emit_introspection_type_from_decl(context, decl); - // TODO // Fix typeid - return; - case DECL_TYPEDEF: - case DECL_DISTINCT: - case NON_TYPE_DECLS: - case DECL_ENUM_CONSTANT: - case DECL_OPTVALUE: - case DECL_BODYPARAM: - return; - } -} diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 3c7291eab..d7ec01968 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -176,6 +176,8 @@ typedef struct unsigned convert_to_fp16; unsigned lifetime_start; unsigned lifetime_end; + unsigned memcpy; + unsigned memset; } LLVMIntrinsics; extern LLVMIntrinsics intrinsic_id; @@ -227,8 +229,8 @@ void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type); void llvm_value_set_int(GenContext *c, BEValue *value, Type *type, uint64_t i); void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type, AlignSize alignment); void llvm_value_set_address_abi_aligned(BEValue *value, LLVMValueRef llvm_value, Type *type); -void llvm_value_set_decl_address(BEValue *value, Decl *decl); -void llvm_value_set_decl(BEValue *value, Decl *decl); +void llvm_value_set_decl_address(GenContext *c, BEValue *value, Decl *decl); +void llvm_value_set_decl(GenContext *c, BEValue *value, Decl *decl); void llvm_value_fold_failable(GenContext *c, BEValue *value); void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_pointer, unsigned index); @@ -269,8 +271,10 @@ void llvm_emit_debug_location(GenContext *context, SourceSpan location); void llvm_emit_debug_parameter(GenContext *c, Decl *parameter, unsigned index); void llvm_emit_debug_local_var(GenContext *c, Decl *var); void llvm_emit_debug_global_var(GenContext *c, Decl *global); +void llvm_add_global(GenContext *c, Decl *decl); +LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl); +LLVMValueRef llvm_get_fault_ref(GenContext *c, Decl *decl); -void llvm_emit_extern_decl(GenContext *context, Decl *decl); LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_init); void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr); INLINE void llvm_emit_exprid(GenContext *c, BEValue *value, ExprId expr) @@ -278,11 +282,12 @@ INLINE void llvm_emit_exprid(GenContext *c, BEValue *value, ExprId expr) assert(expr); llvm_emit_expr(c, value, exprptr(expr)); } - +void llvm_emit_local_global_variable_definition(GenContext *c, Decl *decl); void llvm_emit_typeid(GenContext *c, BEValue *be_value, Type *type); void llvm_emit_global_variable_init(GenContext *c, Decl *decl); void llvm_set_private_linkage(LLVMValueRef alloc); void llvm_set_internal_linkage(LLVMValueRef alloc); +void llvm_set_global_tls(Decl *decl); void llvm_emit_initialize_reference_temporary_const(GenContext *c, BEValue *ref, Expr *expr); void llvm_emit_int_comp_zero(GenContext *c, BEValue *result, BEValue *lhs, BinaryOp binary_op); void llvm_emit_int_comparison(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs, BinaryOp binary_op); @@ -320,7 +325,7 @@ void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLV void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment); void llvm_emit_stmt(GenContext *c, Ast *ast); LLVMValueRef llvm_emit_zstring(GenContext *c, const char *str); -static inline LLVMValueRef llvm_emit_store(GenContext *context, Decl *decl, LLVMValueRef value); +static inline LLVMValueRef llvm_emit_store(GenContext *c, Decl *decl, LLVMValueRef value); void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name, SourceSpan loc); void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_name, SourceSpan loc); void llvm_emit_ptr_from_array(GenContext *c, BEValue *value); @@ -384,16 +389,11 @@ static inline LLVMValueRef decl_failable_ref(Decl *decl) return decl->var.failable_ref; } -static inline LLVMValueRef decl_ref(Decl *decl) -{ - if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_UNWRAPPED) return decl_ref(decl->var.alias); - assert(!decl->is_value); - return decl->backend_ref; -} -static inline LLVMValueRef llvm_emit_store(GenContext *context, Decl *decl, LLVMValueRef value) + +static inline LLVMValueRef llvm_emit_store(GenContext *c, Decl *decl, LLVMValueRef value) { - return LLVMBuildStore(context->builder, value, decl_ref(decl)); + return LLVMBuildStore(c->builder, value, llvm_get_ref(c, decl)); } static inline LLVMValueRef llvm_emit_bitcast(GenContext *context, LLVMValueRef value, Type *type) diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index a9a2b9898..c3450dfad 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -68,7 +68,7 @@ LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl) if (!decl->var.no_init) { BEValue value; - llvm_value_set_decl_address(&value, decl); + llvm_value_set_decl_address(c, &value, decl); value.kind = BE_ADDRESS; llvm_emit_assign_expr(c, &value, decl->var.init_expr, decl->var.failable_ref); } @@ -90,7 +90,7 @@ LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl) else { BEValue value; - llvm_value_set_decl_address(&value, decl); + llvm_value_set_decl_address(c, &value, decl); value.kind = BE_ADDRESS; llvm_store_zero(c, &value); } @@ -1088,7 +1088,7 @@ void llvm_emit_panic(GenContext *c, const char *message, const char *file, const llvm_const_int(c, type_uint, line) }; - LLVMBuildCall2(c->builder, llvm_get_type(c, panicfn->type), panicfn->backend_ref, args, 4, ""); + LLVMBuildCall2(c->builder, llvm_get_type(c, panicfn->type), llvm_get_ref(c, panicfn), args, 4, ""); } void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_name, SourceSpan loc) diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index b44d81f33..11e775841 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -11,7 +11,7 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl) { case DECL_VAR: case DECL_ENUM_CONSTANT: - case DECL_OPTVALUE: + case DECL_FAULTVALUE: case DECL_POISONED: case DECL_BODYPARAM: case NON_TYPE_DECLS: @@ -76,7 +76,7 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl) } case DECL_ENUM: return llvm_get_type(c, decl->type); - case DECL_OPTENUM: + case DECL_FAULT: return llvm_get_type(c, type_iptr); } UNREACHABLE @@ -130,7 +130,7 @@ static void param_expand(GenContext *context, LLVMTypeRef** params_ref, Type *ty } case TYPE_ENUM: case TYPE_ANYERR: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: param_expand(context, params_ref, type_lowering(type)); return; case TYPE_UNION: @@ -323,7 +323,7 @@ LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type) UNREACHABLE case TYPE_TYPEID: case TYPE_ANYERR: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: return any_type->backend_type = llvm_get_type(c, type_iptr->canonical); case TYPE_TYPEDEF: return any_type->backend_type = llvm_get_type(c, any_type->canonical); diff --git a/src/compiler/llvm_codegen_value.c b/src/compiler/llvm_codegen_value.c index d88b4cde5..6d9837621 100644 --- a/src/compiler/llvm_codegen_value.c +++ b/src/compiler/llvm_codegen_value.c @@ -97,14 +97,13 @@ void llvm_value_fold_failable(GenContext *c, BEValue *value) } } -void llvm_value_set_decl_address(BEValue *value, Decl *decl) +void llvm_value_set_decl_address(GenContext *c, BEValue *value, Decl *decl) { - decl = decl_flatten(decl); - llvm_value_set_address(value, decl_ref(decl), decl->type, decl->alignment); + LLVMValueRef backend_ref = llvm_get_ref(c, decl); + llvm_value_set_address(value, backend_ref, decl->type, decl->alignment); - if (decl->decl_kind == DECL_VAR && IS_FAILABLE(decl)) + if ((value->failable = llvm_get_fault_ref(c, decl))) { value->kind = BE_ADDRESS_FAILABLE; - value->failable = decl->var.failable_ref; } } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 41bafebb3..328d3d2cc 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -38,9 +38,7 @@ void recover_top_level(ParseContext *c) case TOKEN_ENUM: case TOKEN_GENERIC: case TOKEN_DEFINE: - case TOKEN_OPTENUM: - case TOKEN_OPTNUM: - case TOKEN_ERRNUM: + case TOKEN_FAULT: return; case TOKEN_IDENT: // Incr arrays only case TOKEN_CONST: @@ -1786,44 +1784,48 @@ static inline Decl *parse_macro_declaration(ParseContext *c, Visibility visibili /** * error_declaration - * : OPTENUM TYPE_IDENT ';' - * | OPTENUM TYPE_IDENT '{' error_data '}' + * : FAULT TYPE_IDENT ';' + * | FAULT TYPE_IDENT '{' error_data '}' * ; */ -static inline Decl *parse_optenum_declaration(ParseContext *c, Visibility visibility) +static inline Decl *parse_fault_declaration(ParseContext *c, Visibility visibility) { advance(c); // advance_and_verify(context, TOKEN_ERRTYPE); - Decl *decl = decl_new_with_type(symstr(c), c->span, DECL_OPTENUM, visibility); + Decl *decl = decl_new_with_type(symstr(c), c->span, DECL_FAULT, visibility); - if (!consume_type_name(c, "optenum")) return poisoned_decl; + if (!consume_type_name(c, "fault")) return poisoned_decl; TypeInfo *type = NULL; CONSUME_OR_RET(TOKEN_LBRACE, poisoned_decl); decl->enums.type_info = type_info_new_base(type_iptr->canonical, decl->span); + uint64_t ordinal = 0; while (!try_consume(c, TOKEN_RBRACE)) { - Decl *opt_const = decl_new(DECL_OPTVALUE, symstr(c), c->span, decl->visibility); - if (!consume_const_name(c, "optional value")) + Decl *fault_const = decl_new(DECL_FAULTVALUE, symstr(c), c->span, decl->visibility); + if (!consume_const_name(c, "fault value")) { return poisoned_decl; } - const char *name = opt_const->name; + const char *name = fault_const->name; + fault_const->enum_constant.parent = declid(decl); + fault_const->enum_constant.ordinal = ordinal; + ordinal++; VECEACH(decl->enums.values, i) { Decl *other_constant = decl->enums.values[i]; if (other_constant->name == name) { - SEMA_ERROR(opt_const, "This opt constant is declared twice."); + SEMA_ERROR(fault_const, "This fault value was declared twice."); SEMA_PREV(other_constant, "The previous declaration was here."); - decl_poison(opt_const); + decl_poison(fault_const); break; } } - vec_add(decl->enums.values, opt_const); + vec_add(decl->enums.values, fault_const); // Allow trailing ',' if (!try_consume(c, TOKEN_COMMA)) { @@ -2201,13 +2203,13 @@ static inline bool parse_doc_errors(ParseContext *c, AstId **docs_ref) ASSIGN_TYPE_OR_RET(ret.type, parse_base_type(c), false); if (ret.type->kind != TYPE_INFO_IDENTIFIER) { - SEMA_ERROR(ret.type, "Expected an optenum type."); + SEMA_ERROR(ret.type, "Expected a fault type."); return false; } if (try_consume(c, TOKEN_DOT)) { ret.ident = c->data.string; - TRY_CONSUME_OR_RET(TOKEN_CONST_IDENT, "Expected an optenum value.", false); + TRY_CONSUME_OR_RET(TOKEN_CONST_IDENT, "Expected a fault value.", false); } ret.span = extend_span_with_token(ret.span, c->prev_span); vec_add(returns, ret); @@ -2404,11 +2406,9 @@ Decl *parse_top_level_statement(ParseContext *c) ASSIGN_DECL_OR_RET(decl, parse_enum_declaration(c, visibility), poisoned_decl); break; } - case TOKEN_OPTENUM: - case TOKEN_OPTNUM: - case TOKEN_ERRNUM: + case TOKEN_FAULT: { - ASSIGN_DECL_OR_RET(decl, parse_optenum_declaration(c, visibility), poisoned_decl); + ASSIGN_DECL_OR_RET(decl, parse_fault_declaration(c, visibility), poisoned_decl); break; } case TOKEN_IDENT: diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 34838d45c..9b85999f2 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -990,9 +990,7 @@ Ast *parse_stmt(ParseContext *c) case TOKEN_MODULE: case TOKEN_EXTERN: case TOKEN_STRUCT: - case TOKEN_OPTENUM: - case TOKEN_OPTNUM: - case TOKEN_ERRNUM: + case TOKEN_FAULT: case TOKEN_UNION: case TOKEN_DEFINE: case TOKEN_DOCS_START: diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 52cf4045f..88d1dedf3 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -373,7 +373,7 @@ CastKind cast_to_bool_kind(Type *type) return CAST_FPBOOL; case TYPE_POINTER: return CAST_PTRBOOL; - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: return CAST_ERBOOL; case TYPE_POISONED: case TYPE_VOID: @@ -405,7 +405,7 @@ bool cast_may_explicit(Type *from_type, Type *to_type, bool ignore_failability, if (from_type->failable == type_void || !from_type->failable) { // void! x; anyerr y = (anyerr)(x); - if (to_type->type_kind == TYPE_ERRTYPE || to_type->type_kind == TYPE_ANYERR) return true; + if (to_type->type_kind == TYPE_FAULTTYPE || to_type->type_kind == TYPE_ANYERR) return true; } if (!ignore_failability) return false; } @@ -457,7 +457,7 @@ bool cast_may_explicit(Type *from_type, Type *to_type, bool ignore_failability, return to_type->type_kind == TYPE_ARRAY && type_is_integer(to_type->array.base); case TYPE_ANYERR: // May convert to a bool, an error type or an integer - return to_type == type_bool || to_kind == TYPE_ERRTYPE || type_is_integer(to_type); + return to_type == type_bool || to_kind == TYPE_FAULTTYPE || type_is_integer(to_type); case ALL_SIGNED_INTS: case ALL_UNSIGNED_INTS: // We don't have to match pointer size if it's a constant. @@ -480,7 +480,7 @@ bool cast_may_explicit(Type *from_type, Type *to_type, bool ignore_failability, return false; case TYPE_ANY: return to_kind == TYPE_POINTER; - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: // Allow MyError.A -> error, to an integer or to bool return to_type->type_kind == TYPE_ANYERR || type_is_integer(to_type) || to_type == type_bool; case TYPE_FLEXIBLE_ARRAY: @@ -556,7 +556,7 @@ bool cast_may_implicit(Type *from_type, Type *to_type, bool is_simple_expr, bool return false; } - if (to == type_anyerr && from->type_kind == TYPE_ERRTYPE) return true; + if (to == type_anyerr && from->type_kind == TYPE_FAULTTYPE) return true; // 3. Handle ints if (type_is_integer(to)) @@ -1210,7 +1210,7 @@ static bool cast_inner(Expr *expr, Type *from_type, Type *to, Type *to_type) break; case TYPE_ANYERR: if (to->type_kind == TYPE_BOOL) return insert_cast(expr, CAST_EUBOOL, to_type); - if (to->type_kind == TYPE_ERRTYPE) return insert_cast(expr, CAST_EUER, to_type); + if (to->type_kind == TYPE_FAULTTYPE) return insert_cast(expr, CAST_EUER, to_type); if (type_is_integer(to)) return insert_cast(expr, CAST_EUINT, to_type); break; case ALL_SIGNED_INTS: @@ -1249,7 +1249,7 @@ static bool cast_inner(Expr *expr, Type *from_type, Type *to, Type *to_type) if (to == type_bool) return enum_to_bool(expr, from_type, to_type); if (to->type_kind == TYPE_POINTER) return enum_to_pointer(expr, from_type, to_type); break; - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: if (to->type_kind == TYPE_ANYERR) return err_to_anyerr(expr, to_type); if (to == type_bool) return err_to_bool(expr, to_type); if (type_is_integer(to)) return insert_cast(expr, CAST_ERINT, to_type); @@ -1309,7 +1309,7 @@ bool cast(Expr *expr, Type *to_type) Type *to = type_flatten(to_type); // Special case *! => error - if (to == type_anyerr || to->type_kind == TYPE_ERRTYPE) + if (to == type_anyerr || to->type_kind == TYPE_FAULTTYPE) { if (type_is_failable(from_type)) return voidfail_to_error(expr, to_type); } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index bda36685a..7df1c936e 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -360,7 +360,7 @@ static bool sema_analyse_struct_union(SemaContext *context, Decl *decl) case DECL_UNION: domain = ATTR_UNION; break; - case DECL_OPTENUM: + case DECL_FAULT: domain = ATTR_ERROR; break; default: @@ -735,7 +735,7 @@ static inline bool sema_analyse_distinct(SemaContext *context, Decl *decl) case TYPE_FAILABLE: SEMA_ERROR(decl, "You cannot create a distinct type from a failable."); return false; - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: SEMA_ERROR(decl, "You cannot create a distinct type from an error type."); return false; case TYPE_ANYERR: @@ -863,7 +863,7 @@ static inline bool sema_analyse_error(SemaContext *context, Decl *decl) enum_value->enum_constant.ordinal = i; DEBUG_LOG("* Ordinal: %d", i); assert(enum_value->resolve_status == RESOLVE_NOT_DONE); - assert(enum_value->decl_kind == DECL_OPTVALUE); + assert(enum_value->decl_kind == DECL_FAULTVALUE); // Start evaluating the constant enum_value->resolve_status = RESOLVE_DONE; @@ -2071,7 +2071,7 @@ static CompilationUnit *unit_copy(Module *module, CompilationUnit *unit) copy->imports = decl_copy_list(unit->imports); copy->global_decls = decl_copy_list(unit->global_decls); copy->module = module; - assert(!unit->functions && !unit->macro_methods && !unit->methods && !unit->enums && !unit->ct_ifs && !unit->types && !unit->external_symbol_list); + assert(!unit->functions && !unit->macro_methods && !unit->methods && !unit->enums && !unit->ct_ifs && !unit->types); return copy; } @@ -2266,7 +2266,7 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) if (!sema_analyse_enum(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; - case DECL_OPTENUM: + case DECL_FAULT: if (!sema_analyse_error(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; @@ -2285,7 +2285,7 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) case DECL_CT_CASE: case DECL_CT_IF: case DECL_CT_ASSERT: - case DECL_OPTVALUE: + case DECL_FAULTVALUE: case DECL_DECLARRAY: case DECL_BODYPARAM: UNREACHABLE diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index db789dfe6..679fa940f 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -618,7 +618,7 @@ static inline bool sema_cast_ident_rvalue(SemaContext *context, Expr *expr) case DECL_GENERIC: SEMA_ERROR(expr, "Expected generic function followed by (...)."); return expr_poison(expr); - case DECL_OPTVALUE: + case DECL_FAULTVALUE: SEMA_ERROR(expr, "Did you forget a '!' after '%s'?", decl->name); return expr_poison(expr); case DECL_ENUM_CONSTANT: @@ -648,8 +648,8 @@ static inline bool sema_cast_ident_rvalue(SemaContext *context, Expr *expr) case DECL_ENUM: SEMA_ERROR(expr, "Expected enum name followed by '.' and an enum value."); return expr_poison(expr); - case DECL_OPTENUM: - SEMA_ERROR(expr, "Expected optenum name followed by '.' and an error value."); + case DECL_FAULT: + SEMA_ERROR(expr, "Expected fault name followed by '.' and a fault value."); return expr_poison(expr); case DECL_IMPORT: case DECL_CT_IF: @@ -860,12 +860,12 @@ static inline bool sema_expr_analyse_enum_constant(Expr *expr, const char *name, static inline bool find_possible_inferred_identifier(Type *to, Expr *expr) { - if (to->canonical->type_kind != TYPE_ENUM && to->canonical->type_kind != TYPE_ERRTYPE) return false; + if (to->canonical->type_kind != TYPE_ENUM && to->canonical->type_kind != TYPE_FAULTTYPE) return false; Decl *parent_decl = to->canonical->decl; switch (parent_decl->decl_kind) { case DECL_ENUM: - case DECL_OPTENUM: + case DECL_FAULT: return sema_expr_analyse_enum_constant(expr, expr->identifier_expr.ident, parent_decl); case DECL_UNION: case DECL_STRUCT: @@ -902,13 +902,16 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, return false; } - if (decl->decl_kind == DECL_FUNC || decl->decl_kind == DECL_MACRO || decl->decl_kind == DECL_GENERIC) + if (decl->decl_kind == DECL_VAR || decl->decl_kind == DECL_FUNC || decl->decl_kind == DECL_MACRO || decl->decl_kind == DECL_GENERIC) { if (decl->module != context->unit->module && !decl->is_autoimport && !expr->identifier_expr.path) { const char *message; switch (decl->decl_kind) { + case DECL_VAR: + message = "Globals from other modules must be prefixed with the module name."; + break; case DECL_FUNC: message = "Functions from other modules must be prefixed with the module name."; break; @@ -1379,7 +1382,7 @@ static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl * FAIL_MISSING: // 17c. Vararg not set? That's fine. - if (params[i]->var.vararg) continue; + if (params && params[i]->var.vararg) continue; // 17d. Argument missing, that's bad. if (is_func_ptr) @@ -1535,6 +1538,7 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr case VARDECL_PARAM_REF: // &foo if (!sema_analyse_expr_lvalue(context, arg)) return false; + if (!sema_expr_check_assign(context, arg)) return false; if (type && type->canonical != arg->type->canonical) { SEMA_ERROR(arg, "'%s' cannot be implicitly cast to '%s'.", type_to_error_string(arg->type), type_to_error_string(type)); @@ -1542,7 +1546,7 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr } if (param && !param->alignment) { - if (arg->expr_kind == EXPR_IDENTIFIER || arg->expr_kind == EXPR_CONST) + if (arg->expr_kind == EXPR_IDENTIFIER) { param->alignment = arg->identifier_expr.decl->alignment; } @@ -1843,6 +1847,18 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s macro_context.current_function = context->current_function; rtype = decl->macro_decl.rtype ? type_infoptr(decl->macro_decl.rtype)->type : NULL; macro_context.expected_block_type = rtype; + bool may_failable = true; + if (rtype) + { + if (type_is_failable(rtype)) + { + rtype = type_no_fail(rtype); + } + else + { + may_failable = false; + } + } context_change_scope_with_flags(¯o_context, SCOPE_MACRO); @@ -1867,7 +1883,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s if (!vec_size(macro_context.returns)) { - if (rtype && type_no_fail(rtype) != type_void) + if (rtype && rtype != type_void) { SEMA_ERROR(decl, "Missing return in macro that should evaluate to %s.", @@ -1886,6 +1902,11 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s VECEACH(macro_context.returns, i) { Ast *return_stmt = macro_context.returns[i]; + if (!return_stmt) + { + assert(may_failable); + continue; + } Expr *ret_expr = return_stmt->return_stmt.expr; if (!ret_expr) { @@ -1894,7 +1915,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s return SCOPE_POP_ERROR(); } Type *type = ret_expr->type; - if (!cast_may_implicit(type, rtype, true, true)) + if (!cast_may_implicit(type, rtype, true, may_failable)) { SEMA_ERROR(ret_expr, "Expected %s, not %s.", type_quoted_error_string(rtype), type_quoted_error_string(type)); @@ -1902,6 +1923,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s } bool success = cast_implicit(ret_expr, rtype); assert(success); + if (may_failable) ret_expr->type = type_get_opt_fail(ret_expr->type, may_failable); } call_expr->type = type_get_opt_fail(rtype, failable); } @@ -2193,11 +2215,98 @@ static inline unsigned builtin_expected_args(BuiltinFunction func) return 2; case BUILTIN_FMA: return 3; + case BUILTIN_MEMSET: + return 5; + case BUILTIN_MEMCOPY: + return 6; case BUILTIN_NONE: UNREACHABLE } UNREACHABLE } + +typedef enum +{ + BA_POINTER, + BA_SIZE, + BA_BOOL, + BA_CHAR, + BA_FLOATLIKE, +} BuiltinArg; + +static bool sema_check_builtin_args_match(Expr **args, size_t arg_len) +{ + Type *first = args[0]->type->canonical; + for (size_t i = 1; i < arg_len; i++) + { + if (first != args[i]->type->canonical) + { + SEMA_ERROR(args[i], "Expected an expression of type %s.", type_quoted_error_string(args[0]->type)); + return false; + } + } + return true; +} + +static bool sema_check_builtin_args_const(Expr **args, size_t arg_len) +{ + for (size_t i = 0; i < arg_len; i++) + { + if (!expr_is_const(args[i])) + { + SEMA_ERROR(args[i], "Expected a compile time constant value for this argument."); + return false; + } + } + return true; +} +static bool sema_check_builtin_args(Expr **args, BuiltinArg *arg_type, size_t arg_len) +{ + for (size_t i = 0; i < arg_len; i++) + { + Type *type = args[i]->type->canonical; + switch (arg_type[i]) + { + case BA_POINTER: + if (!type_is_pointer(type)) + { + SEMA_ERROR(args[i], "Expected a pointer."); + return false; + } + break; + case BA_CHAR: + if (type != type_char && type != type_ichar) + { + SEMA_ERROR(args[i], "Expected a char or ichar."); + return false; + } + break; + case BA_SIZE: + if (!type_is_integer(type) || type_size(type) != type_size(type_usize)) + { + SEMA_ERROR(args[i], "Expected an usize or isize value."); + return false; + } + break; + case BA_BOOL: + if (type != type_bool) + { + SEMA_ERROR(args[i], "Expected a bool."); + return false; + } + break; + case BA_FLOATLIKE: + if (!type_is_float_or_float_vector(type)) + { + SEMA_ERROR(args[i], "Expected a floating point or floating point array."); + return false; + } + break; + } + } + return true; +} + static inline bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) { expr->call_expr.is_builtin = true; @@ -2240,6 +2349,20 @@ static inline bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *ex case BUILTIN_TRAP: rtype = type_void; break; + case BUILTIN_MEMCOPY: + if (!sema_check_builtin_args(args, + (BuiltinArg[]) { BA_POINTER, BA_POINTER, BA_SIZE, BA_BOOL, BA_SIZE, BA_SIZE }, + arg_count)) return false; + if (!sema_check_builtin_args_const(&args[3], 3)) return false; + rtype = type_void; + break; + case BUILTIN_MEMSET: + if (!sema_check_builtin_args(args, + (BuiltinArg[]) { BA_POINTER, BA_CHAR, BA_SIZE, BA_BOOL, BA_SIZE }, + arg_count)) return false; + if (!sema_check_builtin_args_const(&args[3], 2)) return false; + rtype = type_void; + break; case BUILTIN_STACKTRACE: rtype = type_voidptr; break; @@ -2248,86 +2371,42 @@ static inline bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *ex case BUILTIN_SQRT: case BUILTIN_COS: case BUILTIN_SIN: - case BUILTIN_POW: case BUILTIN_EXP: case BUILTIN_FABS: case BUILTIN_LOG: case BUILTIN_LOG2: case BUILTIN_LOG10: - if (type_is_float_or_float_vector(args[0]->type)) - { - rtype = args[0]->type; - break; - } - SEMA_ERROR(args[0], "Expected a floating point or floating point array."); - return false; + if (!sema_check_builtin_args(args, + (BuiltinArg[]) { BA_FLOATLIKE }, + arg_count)) return false; + rtype = args[0]->type; + break; + case BUILTIN_POW: case BUILTIN_MAX: case BUILTIN_MIN: - if (!type_is_float_or_float_vector(args[0]->type)) - { - SEMA_ERROR(args[0], "Expected a floating point or floating point array."); - return false; - } - if (!type_is_float_or_float_vector(args[1]->type)) - { - SEMA_ERROR(args[1], "Expected a floating point or floating point array."); - return false; - } - if (type_flatten(args[0]->type) != type_flatten(args[1]->type)) - { - SEMA_ERROR(args[1], "Expected an expression of type %s.", type_quoted_error_string(args[0]->type)); - return false; - } + if (!sema_check_builtin_args(args, + (BuiltinArg[]) { BA_FLOATLIKE, BA_FLOATLIKE }, + arg_count)) return false; + if (!sema_check_builtin_args_match(args, arg_count)) return false; rtype = args[0]->type; break; case BUILTIN_FMA: - if (!type_is_float_or_float_vector(args[0]->type)) - { - SEMA_ERROR(args[0], "Expected a floating point or floating point array."); - return false; - } - if (!type_is_float_or_float_vector(args[1]->type)) - { - SEMA_ERROR(args[1], "Expected a floating point or floating point array."); - return false; - } - if (!type_is_float_or_float_vector(args[2]->type)) - { - SEMA_ERROR(args[2], "Expected a floating point or floating point array."); - return false; - } - if (type_flatten(args[0]->type) != type_flatten(args[1]->type)) - { - SEMA_ERROR(args[1], "Expected an expression of type %s.", type_quoted_error_string(args[0]->type)); - return false; - } - if (type_flatten(args[0]->type) != type_flatten(args[2]->type)) - { - SEMA_ERROR(args[2], "Expected an expression of type %s.", type_quoted_error_string(args[0]->type)); - return false; - } + if (!sema_check_builtin_args(args, + (BuiltinArg[]) { BA_FLOATLIKE, BA_FLOATLIKE, BA_FLOATLIKE }, + arg_count)) return false; + if (!sema_check_builtin_args_match(args, arg_count)) return false; rtype = args[0]->type; break; case BUILTIN_VOLATILE_LOAD: { - Type *type = type_flatten(args[0]->type); - if (!type_is_pointer(type)) - { - SEMA_ERROR(args[0], "Expected a pointer."); - return false; - } - rtype = type->pointer; + if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_POINTER }, 1)) return false; + rtype = args[0]->type->canonical->pointer; break; } case BUILTIN_VOLATILE_STORE: { - Type *type = type_flatten(args[0]->type); - if (!type_is_pointer(type)) - { - SEMA_ERROR(args[0], "Expected a pointer."); - return false; - } - rtype = type->pointer; + if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_POINTER }, 1)) return false; + rtype = args[0]->type->canonical->pointer; if (!cast_implicit(args[1], rtype)) return false; break; } @@ -3078,7 +3157,7 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp return true; } break; - case DECL_OPTENUM: + case DECL_FAULT: unit_register_external_symbol(context->compilation_unit, decl); if (is_const) @@ -6191,7 +6270,7 @@ static inline bool sema_expr_analyse_failable(SemaContext *context, Expr *expr) } Type *type = inner->type->canonical; - if (type->type_kind != TYPE_ERRTYPE && type->type_kind != TYPE_ANYERR) + if (type->type_kind != TYPE_FAULTTYPE && type->type_kind != TYPE_ANYERR) { SEMA_ERROR(inner, "You cannot use the '!' operator on expressions of type %s", type_quoted_error_string(type)); return false; diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 7846c743a..558d8b16f 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -433,6 +433,23 @@ Decl *sema_find_symbol(SemaContext *context, const char *symbol) return sema_resolve_symbol_common(context, &resolve); } +bool sema_symbol_is_defined_in_scope(SemaContext *c, const char *symbol) +{ + NameResolve resolve = { + .suppress_error = true, + .symbol = symbol, + }; + Decl *decl = sema_resolve_symbol_common(c, &resolve); + // Unknown symbol => not defined + if (!decl) return false; + // Defined in the same module => defined + if (decl->module == c->unit->module) return true; + // Not a variable or function => defined + if (decl->decl_kind != DECL_VAR && decl->decl_kind != DECL_FUNC) return true; + // Otherwise defined only if autoimport. + return decl->is_autoimport; +} + Decl *sema_find_path_symbol(SemaContext *context, const char *symbol, Path *path) { NameResolve resolve = { @@ -492,7 +509,7 @@ bool sema_add_local(SemaContext *context, Decl *decl) if (decl->decl_kind == DECL_VAR && decl->var.shadow) goto ADD_VAR; Decl *other = sema_find_symbol(context, decl->name); assert(!other || other->module); - if (other && other->module == current_module) + if (other && (other->module == current_module || other->is_autoimport)) { sema_shadow_error(decl, other); decl_poison(decl); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 9467fe6fb..c9db83bef 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -261,7 +261,7 @@ static inline bool sema_analyse_try_unwrap(SemaContext *context, Expr *expr) // 1. Check if we are doing an implicit declaration. if (!var_type && ident->expr_kind == EXPR_IDENTIFIER) { - implicit_declaration = !sema_find_symbol(context, ident->identifier_expr.ident); + implicit_declaration = !sema_symbol_is_defined_in_scope(context, ident->identifier_expr.ident); } // 2. If we have a type for the variable, resolve it. @@ -377,7 +377,6 @@ static inline bool sema_analyse_try_unwrap_chain(SemaContext *context, Expr *exp assert(expr->expr_kind == EXPR_TRY_UNWRAP_CHAIN); Expr **chain = expr->try_unwrap_chain_expr; - unsigned elements = vec_size(chain); VECEACH(expr->try_unwrap_chain_expr, i) { @@ -408,7 +407,7 @@ static inline bool sema_analyse_catch_unwrap(SemaContext *context, Expr *expr) } if (!type && ident->expr_kind == EXPR_IDENTIFIER) { - implicit_declaration = !sema_find_symbol(context, ident->identifier_expr.ident); + implicit_declaration = !sema_symbol_is_defined_in_scope(context, ident->identifier_expr.ident); } if (!type && !implicit_declaration) @@ -540,8 +539,7 @@ static inline bool sema_analyse_last_cond(SemaContext *context, Expr *expr, Cond // Does the identifier exist in the parent scope? // then again it can't be a variant unwrap. - Decl *decl_for_ident = sema_find_symbol(context, left->identifier_expr.ident); - if (decl_for_ident) goto NORMAL_EXPR; + if (sema_symbol_is_defined_in_scope(context, left->identifier_expr.ident)) goto NORMAL_EXPR; Expr *right = exprptr(expr->binary_expr.right); bool is_deref = right->expr_kind == EXPR_UNARY && right->unary_expr.operator == UNARYOP_DEREF; @@ -2393,14 +2391,14 @@ static bool sema_analyse_errors(SemaContext *context, Ast *directive) const char *ident = returns[i].ident; if (type_info->kind != TYPE_INFO_IDENTIFIER) { - SEMA_ERROR(type_info, "Expected an optenum name here."); + SEMA_ERROR(type_info, "Expected a fault name here."); return false; } if (!sema_resolve_type_info(context, type_info)) return false; Type *type = type_info->type; - if (type->type_kind != TYPE_ERRTYPE) + if (type->type_kind != TYPE_FAULTTYPE) { - SEMA_ERROR(type_info, "An optenum type is required."); + SEMA_ERROR(type_info, "A fault type is required."); return false; } if (!ident) @@ -2419,7 +2417,7 @@ static bool sema_analyse_errors(SemaContext *context, Ast *directive) goto NEXT; } } - sema_error_at(returns[i].span, "No optenum value '%s' found.", ident); + sema_error_at(returns[i].span, "No fault value '%s' found.", ident); return false; NEXT:; } diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index eb7bc18a3..86116e246 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -135,7 +135,7 @@ static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_in case DECL_STRUCT: case DECL_BITSTRUCT: case DECL_UNION: - case DECL_OPTENUM: + case DECL_FAULT: case DECL_ENUM: type_info->type = decl->type; type_info->resolve_status = RESOLVE_DONE; @@ -165,7 +165,7 @@ static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_in } FALLTHROUGH; case DECL_FUNC: - case DECL_OPTVALUE: + case DECL_FAULTVALUE: case DECL_ENUM_CONSTANT: case DECL_IMPORT: case DECL_MACRO: @@ -213,7 +213,7 @@ bool sema_resolve_type(SemaContext *context, Type *type) case TYPE_BITSTRUCT: case TYPE_DISTINCT: case TYPE_ENUM: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: case TYPE_FUNC: case TYPE_STRUCT: case TYPE_UNION: diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 963e916a4..26e4ab68b 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -176,7 +176,7 @@ static void register_generic_decls(Module *module, Decl **decls) { case DECL_POISONED: case DECL_ENUM_CONSTANT: - case DECL_OPTVALUE: + case DECL_FAULTVALUE: case DECL_IMPORT: case DECL_LABEL: case DECL_CT_ASSERT: @@ -204,7 +204,7 @@ static void register_generic_decls(Module *module, Decl **decls) case DECL_DISTINCT: case DECL_ENUM: case DECL_GENERIC: - case DECL_OPTENUM: + case DECL_FAULT: case DECL_FUNC: case DECL_STRUCT: case DECL_TYPEDEF: diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 3dcd96593..175dab85f 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -173,6 +173,8 @@ void symtab_init(uint32_t capacity) builtin_list[BUILTIN_FABS] = KW_DEF("fabs"); builtin_list[BUILTIN_VOLATILE_STORE] = KW_DEF("volatile_store"); builtin_list[BUILTIN_VOLATILE_LOAD] = KW_DEF("volatile_load"); + builtin_list[BUILTIN_MEMCOPY] = KW_DEF("memcpy"); + builtin_list[BUILTIN_MEMSET] = KW_DEF("memset"); for (unsigned i = 0; i < NUMBER_OF_BUILTINS; i++) { diff --git a/src/compiler/tb_codegen.c b/src/compiler/tb_codegen.c index 3af0c0bff..61359e35d 100644 --- a/src/compiler/tb_codegen.c +++ b/src/compiler/tb_codegen.c @@ -134,7 +134,7 @@ static void param_expand(TB_DataType **params_ref, Type *type) } case TYPE_ENUM: case TYPE_ANYERR: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: param_expand(params_ref, type_lowering(type)); return; case TYPE_UNION: @@ -873,7 +873,6 @@ void *tinybackend_gen(Module *module) { CompilationUnit *unit = module->units[j]; - printf(" External symbols: %d\n", vec_size(unit->external_symbol_list)); printf(" Functions: %d\n", vec_size(unit->functions)); } diff --git a/src/compiler/tilde_codegen.c b/src/compiler/tilde_codegen.c index ed88c9e29..c90056385 100644 --- a/src/compiler/tilde_codegen.c +++ b/src/compiler/tilde_codegen.c @@ -890,7 +890,7 @@ static void llvm_emit_type_decls(GenContext *context, Decl *decl) case DECL_STRUCT: case DECL_UNION: case DECL_ENUM: - case DECL_ERRTYPE: + case DECL_FAULTTYPE: case DECL_BITSTRUCT: llvm_emit_introspection_type_from_decl(context, decl); break; @@ -975,13 +975,6 @@ void *llvm_gen(Module *module) gen_context->debug.compile_unit = unit->llvm.debug_compile_unit; gen_context->debug.file = unit->llvm.debug_file; - VECEACH(unit->external_symbol_list, i) - { - Decl *d = unit->external_symbol_list[i]; - // Avoid duplicating symbol - if (d->module == unit->module) continue; - llvm_emit_extern_decl(gen_context, unit->external_symbol_list[i]); - } VECEACH(unit->methods, i) { llvm_emit_function_decl(gen_context, unit->methods[i]); diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 9adcb75a8..ed00bd103 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -218,6 +218,8 @@ const char *token_type_to_string(TokenType type) return "extern"; case TOKEN_FALSE: return "false"; + case TOKEN_FAULT: + return "fault"; case TOKEN_FOR: return "for"; case TOKEN_FOREACH: @@ -264,13 +266,6 @@ const char *token_type_to_string(TokenType type) return "var"; case TOKEN_WHILE: return "while"; - // Aliases to test out - case TOKEN_OPTENUM: - return "optenum"; - case TOKEN_OPTNUM: - return "optnum"; - case TOKEN_ERRNUM: - return "errnum"; // Named types case TOKEN_VOID: diff --git a/src/compiler/types.c b/src/compiler/types.c index 2cf7c2bd9..886bad107 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -111,7 +111,7 @@ static void type_append_name_to_scratch(Type *type) case TYPE_POISONED: case TYPE_TYPEDEF: UNREACHABLE; - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: case TYPE_ENUM: case TYPE_STRUCT: case TYPE_UNION: @@ -214,7 +214,7 @@ const char *type_to_error_string(Type *type) case TYPE_POISONED: return "poisoned"; case TYPE_ENUM: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: case TYPE_TYPEDEF: case TYPE_STRUCT: case TYPE_VOID: @@ -299,7 +299,7 @@ RETRY: case TYPE_TYPEDEF: type = type->canonical; goto RETRY; - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: type = type_iptr->canonical; goto RETRY; case TYPE_ENUM: @@ -384,7 +384,7 @@ bool type_is_abi_aggregate(Type *type) case TYPE_FUNC: case TYPE_VECTOR: case TYPE_ANYERR: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: return false; case TYPE_STRUCT: case TYPE_UNION: @@ -510,7 +510,7 @@ AlignSize type_abi_alignment(Type *type) goto RETRY; case TYPE_ENUM: return type->decl->enums.type_info->type->canonical->builtin.abi_alignment; - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: return t.iptr.canonical->builtin.abi_alignment; case TYPE_STRUCT: case TYPE_UNION: @@ -811,7 +811,7 @@ bool type_is_user_defined(Type *type) case TYPE_FUNC: case TYPE_STRUCT: case TYPE_UNION: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: case TYPE_TYPEDEF: case TYPE_DISTINCT: return true; @@ -1234,7 +1234,7 @@ bool type_is_scalar(Type *type) case TYPE_TYPEID: case TYPE_POINTER: case TYPE_ENUM: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: case TYPE_ANYERR: return true; case TYPE_BITSTRUCT: @@ -1346,7 +1346,7 @@ bool type_may_have_sub_elements(Type *type) case TYPE_UNION: case TYPE_STRUCT: case TYPE_ENUM: - case TYPE_ERRTYPE: + case TYPE_FAULTTYPE: case TYPE_BITSTRUCT: return true; default: @@ -1541,8 +1541,8 @@ Type *type_find_max_type(Type *type, Type *other) // IMPROVE: should there be implicit conversion between one enum and the other in // some way? return NULL; - case TYPE_ERRTYPE: - if (other->type_kind == TYPE_ERRTYPE) return type_anyerr; + case TYPE_FAULTTYPE: + if (other->type_kind == TYPE_FAULTTYPE) return type_anyerr; return NULL; case TYPE_ANYERR: return type_anyerr; diff --git a/src/version.h b/src/version.h index 0ed34da97..4cc1500e2 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "PRE.29" \ No newline at end of file +#define COMPILER_VERSION "0.1.0" \ No newline at end of file diff --git a/test/test_suite/cast/cast_to_failable.c3 b/test/test_suite/cast/cast_to_failable.c3 index fc2d16a19..e47ee8206 100644 --- a/test/test_suite/cast/cast_to_failable.c3 +++ b/test/test_suite/cast/cast_to_failable.c3 @@ -1,4 +1,4 @@ -optenum MyErr +fault MyErr { FOO } diff --git a/test/test_suite/distinct/distinct_invalid.c3 b/test/test_suite/distinct/distinct_invalid.c3 index e21cfb375..44c3af955 100644 --- a/test/test_suite/distinct/distinct_invalid.c3 +++ b/test/test_suite/distinct/distinct_invalid.c3 @@ -1,4 +1,4 @@ -optenum Error +fault Error {} define Foo1 = distinct Error; // #error: You cannot create a distinct type from an error diff --git a/test/test_suite/errors/anyerr_void.c3t b/test/test_suite/errors/anyerr_void.c3t index 1534ad044..c50dd9656 100644 --- a/test/test_suite/errors/anyerr_void.c3t +++ b/test/test_suite/errors/anyerr_void.c3t @@ -1,4 +1,4 @@ -optenum MyError +fault MyError { FOO, BAR diff --git a/test/test_suite/errors/error_decl_ok.c3 b/test/test_suite/errors/error_decl_ok.c3 index bfaac243d..a17338068 100644 --- a/test/test_suite/errors/error_decl_ok.c3 +++ b/test/test_suite/errors/error_decl_ok.c3 @@ -1,17 +1,17 @@ module errors; -optenum TheError +fault TheError { A } -optenum TheError2 +fault TheError2 { A, B } -optenum TheError3 +fault TheError3 { C, D } diff --git a/test/test_suite/errors/error_regression_2.c3t b/test/test_suite/errors/error_regression_2.c3t index 35bb4318c..eb89b35a0 100644 --- a/test/test_suite/errors/error_regression_2.c3t +++ b/test/test_suite/errors/error_regression_2.c3t @@ -45,7 +45,7 @@ macro dupe(value) return temp; } -optenum ReadError +fault ReadError { BAD_READ, OUT_OF_MEMORY @@ -89,7 +89,7 @@ fn Summary readAndBuildSummary(char[] url) } -optenum TitleResult +fault TitleResult { TITLE_MISSING } @@ -366,7 +366,7 @@ if.then15: ; preds = %if.exit9 store %"char[]"* null, %"char[]"** %33, align 8 %34 = load %Head, %Head* %literal18, align 8 store %Head %34, %Head* %value, align 8 - %35 = call i8* @"std::mem.alloc"(i64 8, i64 1) #2 + %35 = call i8* @"std::mem.alloc"(i64 8, i64 0) %ptrptr = bitcast i8* %35 to %Head* store %Head* %ptrptr, %Head** %temp, align 8 %36 = load %Head*, %Head** %temp, align 8 @@ -418,7 +418,7 @@ if.then27: ; preds = %if.exit21 store %"char[]"* null, %"char[]"** %53, align 8 %54 = getelementptr inbounds %Head, %Head* %literal32, i32 0, i32 0 store %"char[]" { i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.4, i32 0, i32 0), i64 0 }, %"char[]"* %value34, align 8 - %55 = call i8* @"std::mem.alloc"(i64 16, i64 1) #2 + %55 = call i8* @"std::mem.alloc"(i64 16, i64 0) %ptrptr36 = bitcast i8* %55 to %"char[]"* store %"char[]"* %ptrptr36, %"char[]"** %temp35, align 8 %56 = load %"char[]"*, %"char[]"** %temp35, align 8 @@ -445,7 +445,7 @@ noerr_block41: ; preds = %if.exit39 store %"char[]"* %61, %"char[]"** %54, align 8 %62 = load %Head, %Head* %literal32, align 8 store %Head %62, %Head* %value31, align 8 - %63 = call i8* @"std::mem.alloc"(i64 8, i64 1) #2 + %63 = call i8* @"std::mem.alloc"(i64 8, i64 0) %ptrptr43 = bitcast i8* %63 to %Head* store %Head* %ptrptr43, %Head** %temp42, align 8 %64 = load %Head*, %Head** %temp42, align 8 @@ -486,7 +486,7 @@ if.exit49: ; preds = %if.exit21 %77 = load i32, i32* %len, align 4 %siuiext = sext i32 %77 to i64 %add = add i64 %siuiext, 1 - %78 = call i8* @"std::mem.alloc"(i64 %add, i64 1) #2 + %78 = call i8* @"std::mem.alloc"(i64 %add, i64 0) store i8* %78, i8** %str, align 8 %79 = load i8*, i8** %str, align 8 %not50 = icmp eq i8* %79, null @@ -520,7 +520,7 @@ if.exit52: ; preds = %if.exit49 %93 = insertvalue %"char[]" undef, i8* %ptroffset, 0 %94 = insertvalue %"char[]" %93, i64 %size, 1 store %"char[]" %94, %"char[]"* %value62, align 8 - %95 = call i8* @"std::mem.alloc"(i64 16, i64 1) #2 + %95 = call i8* @"std::mem.alloc"(i64 16, i64 0) %ptrptr64 = bitcast i8* %95 to %"char[]"* store %"char[]"* %ptrptr64, %"char[]"** %temp63, align 8 %96 = load %"char[]"*, %"char[]"** %temp63, align 8 @@ -547,7 +547,7 @@ noerr_block69: ; preds = %if.exit67 store %"char[]"* %101, %"char[]"** %89, align 8 %102 = load %Head, %Head* %literal60, align 8 store %Head %102, %Head* %value59, align 8 - %103 = call i8* @"std::mem.alloc"(i64 8, i64 1) #2 + %103 = call i8* @"std::mem.alloc"(i64 8, i64 0) %ptrptr71 = bitcast i8* %103 to %Head* store %Head* %ptrptr71, %Head** %temp70, align 8 %104 = load %Head*, %Head** %temp70, align 8 diff --git a/test/test_suite/errors/error_semantic_fails.c3 b/test/test_suite/errors/error_semantic_fails.c3 index 051e1890d..bfafa510f 100644 --- a/test/test_suite/errors/error_semantic_fails.c3 +++ b/test/test_suite/errors/error_semantic_fails.c3 @@ -1,5 +1,5 @@ -optenum Repeater +fault Repeater { A, - A // #error: This opt constant is declared twice. + A // #error: This fault value was declared twice. } diff --git a/test/test_suite/errors/error_throw.c3 b/test/test_suite/errors/error_throw.c3 index 1ebe2953f..dacd9276b 100644 --- a/test/test_suite/errors/error_throw.c3 +++ b/test/test_suite/errors/error_throw.c3 @@ -1,6 +1,6 @@ module foo; -optenum Blurg +fault Blurg { Z } diff --git a/test/test_suite/errors/error_union.c3 b/test/test_suite/errors/error_union.c3 index eb5f61b13..b76a694c9 100644 --- a/test/test_suite/errors/error_union.c3 +++ b/test/test_suite/errors/error_union.c3 @@ -1,6 +1,6 @@ module foo; -optenum Blurg +fault Blurg { X, Y, Z } diff --git a/test/test_suite/errors/failable_inits.c3t b/test/test_suite/errors/failable_inits.c3t index a6450d78a..19378878a 100644 --- a/test/test_suite/errors/failable_inits.c3t +++ b/test/test_suite/errors/failable_inits.c3t @@ -2,7 +2,7 @@ module test; import std::io; -optenum Foo +fault Foo { MY_VAL1, MY_VAL2, diff --git a/test/test_suite/errors/failable_taddr_and_access.c3t b/test/test_suite/errors/failable_taddr_and_access.c3t index c1e3e454f..cb5b6541b 100644 --- a/test/test_suite/errors/failable_taddr_and_access.c3t +++ b/test/test_suite/errors/failable_taddr_and_access.c3t @@ -6,7 +6,7 @@ struct Foo int x, y; } -optenum MyErr +fault MyErr { FOO } diff --git a/test/test_suite/errors/failable_untyped_list.c3 b/test/test_suite/errors/failable_untyped_list.c3 index c11826b37..3af5e5a82 100644 --- a/test/test_suite/errors/failable_untyped_list.c3 +++ b/test/test_suite/errors/failable_untyped_list.c3 @@ -4,7 +4,7 @@ struct Foo int x, y; } -optenum MyErr +fault MyErr { FOO } diff --git a/test/test_suite/errors/general_error_regression.c3t b/test/test_suite/errors/general_error_regression.c3t index 9157ffad7..0e8395633 100644 --- a/test/test_suite/errors/general_error_regression.c3t +++ b/test/test_suite/errors/general_error_regression.c3t @@ -2,7 +2,7 @@ module foo; import std::io; -optenum Foo +fault Foo { X, Y, @@ -13,7 +13,7 @@ optenum Foo } -optenum Foob +fault Foob { X1, Y2 diff --git a/test/test_suite/errors/macro_err2.c3t b/test/test_suite/errors/macro_err2.c3t index fec44cd1a..2e91e16dc 100644 --- a/test/test_suite/errors/macro_err2.c3t +++ b/test/test_suite/errors/macro_err2.c3t @@ -1,6 +1,6 @@ // #target: x64-darwin module test; -optenum Tester { FOO } +fault Tester { FOO } fn int! abc() { diff --git a/test/test_suite/errors/macro_err3.c3t b/test/test_suite/errors/macro_err3.c3t index 7d880f362..176f48081 100644 --- a/test/test_suite/errors/macro_err3.c3t +++ b/test/test_suite/errors/macro_err3.c3t @@ -1,6 +1,6 @@ // #target: x64-darwin module test; -optenum Tester { FOO } +fault Tester { FOO } macro test() diff --git a/test/test_suite/errors/simple_static_failable.c3t b/test/test_suite/errors/simple_static_failable.c3t index 8c9a099c4..82a956111 100644 --- a/test/test_suite/errors/simple_static_failable.c3t +++ b/test/test_suite/errors/simple_static_failable.c3t @@ -2,7 +2,7 @@ module foo; -optenum Blurg +fault Blurg { Y } diff --git a/test/test_suite/errors/try_assign.c3t b/test/test_suite/errors/try_assign.c3t index ed7365b62..cd1e5ade1 100644 --- a/test/test_suite/errors/try_assign.c3t +++ b/test/test_suite/errors/try_assign.c3t @@ -1,7 +1,7 @@ // #target: x64-darwin extern fn int! err(); -optenum FooErr { QBERT } +fault FooErr { QBERT } extern fn int printf(char* fmt, ...); fn void main() diff --git a/test/test_suite/errors/try_catch_if.c3t b/test/test_suite/errors/try_catch_if.c3t index 052009f07..e9914e812 100644 --- a/test/test_suite/errors/try_catch_if.c3t +++ b/test/test_suite/errors/try_catch_if.c3t @@ -13,7 +13,7 @@ fn int! tester() return 222; } -optenum Foo +fault Foo { A } diff --git a/test/test_suite/errors/try_catch_unwrapping_while_if.c3 b/test/test_suite/errors/try_catch_unwrapping_while_if.c3 index 64f804b2d..faaf287d7 100644 --- a/test/test_suite/errors/try_catch_unwrapping_while_if.c3 +++ b/test/test_suite/errors/try_catch_unwrapping_while_if.c3 @@ -1,7 +1,7 @@ extern fn int printf(char* fmt, ...); extern fn int! err(); -optenum FooErr +fault FooErr { X } diff --git a/test/test_suite/errors/try_with_unwrapper.c3t b/test/test_suite/errors/try_with_unwrapper.c3t index 09f17d763..e166cbf69 100644 --- a/test/test_suite/errors/try_with_unwrapper.c3t +++ b/test/test_suite/errors/try_with_unwrapper.c3t @@ -13,7 +13,7 @@ fn int! tester() return 222; } -optenum Foo +fault Foo { A } diff --git a/test/test_suite/expressions/addr_of_fails.c3 b/test/test_suite/expressions/addr_of_fails.c3 index 90e3be3d2..0fe763de3 100644 --- a/test/test_suite/expressions/addr_of_fails.c3 +++ b/test/test_suite/expressions/addr_of_fails.c3 @@ -62,7 +62,7 @@ fn void test6() define Baz = Foo; define Bar = distinct int; -optenum Err { FOO } +fault Err { FOO } union Un { int x; } enum MyEnum { BAR } diff --git a/test/test_suite/expressions/casts/cast_failable.c3 b/test/test_suite/expressions/casts/cast_failable.c3 index 7dc829abd..3fc1b685e 100644 --- a/test/test_suite/expressions/casts/cast_failable.c3 +++ b/test/test_suite/expressions/casts/cast_failable.c3 @@ -1,4 +1,4 @@ -optenum MyErr +fault MyErr { FOO } diff --git a/test/test_suite/failable_catch.c3t b/test/test_suite/failable_catch.c3t index 000c4174c..4fb9c1db4 100644 --- a/test/test_suite/failable_catch.c3t +++ b/test/test_suite/failable_catch.c3t @@ -1,6 +1,6 @@ // #target: x64-darwin -errnum MyErr +fault MyErr { TEST } diff --git a/test/test_suite/from_docs/examples_if_catch.c3t b/test/test_suite/from_docs/examples_if_catch.c3t index 359022259..e85d752ec 100644 --- a/test/test_suite/from_docs/examples_if_catch.c3t +++ b/test/test_suite/from_docs/examples_if_catch.c3t @@ -2,7 +2,7 @@ module demo; import std::io; -optenum MathError +fault MathError { DIVISION_BY_ZERO } diff --git a/test/test_suite/functions/default_param_fail.c3 b/test/test_suite/functions/default_param_fail.c3 index e3328c40a..95d284f8c 100644 --- a/test/test_suite/functions/default_param_fail.c3 +++ b/test/test_suite/functions/default_param_fail.c3 @@ -1,7 +1,7 @@ int z; -optenum MyError +fault MyError { FOO, } diff --git a/test/test_suite/functions/static_vars.c3t b/test/test_suite/functions/static_vars.c3t index 33819fa34..0d4e78da8 100644 --- a/test/test_suite/functions/static_vars.c3t +++ b/test/test_suite/functions/static_vars.c3t @@ -11,7 +11,7 @@ fn int test() // #expect: foo.ll @test.x = internal unnamed_addr global i32 1, align 4 -@test.y = internal thread_local unnamed_addr global i32 2, align 4 +@test.y = internal thread_local(localdynamic) unnamed_addr global i32 2, align 4 define i32 @foo.test() diff --git a/test/test_suite/functions/test_regression.c3t b/test/test_suite/functions/test_regression.c3t index 906b6aa73..d533501a3 100644 --- a/test/test_suite/functions/test_regression.c3t +++ b/test/test_suite/functions/test_regression.c3t @@ -131,8 +131,8 @@ fn void main() printf("Min %d Max %d Elements: %d\n", MyEnum.min, MyEnum.max, (int)(MyEnum.elements)); - int max = MyEnum.max; - int min = MyEnum.min; + int maxv = MyEnum.max; + int minv = MyEnum.min; int elements = MyEnum.elements; printf("Hello\n"); IntArray array; @@ -203,7 +203,7 @@ fn Type getMult(Type a) } Type argh = 234; -optenum MyErr +fault MyErr { X, Y @@ -236,14 +236,14 @@ fn Type getValue(Blob blob) /* #expect: test.ll -%Blob = type { i32 } -%Blob.0 = type { double } -%LinkedList = type { i64, %Node*, %Node* } -%Node = type { %Node*, %Node*, i32 } -%List = type { i64, i64, i32* } +%Blob.0 = type { i32 } +%Blob.1 = type { double } %Foo2 = type { i32 } %Bobo = type { i16, float, i16, i16, float, i16 } %"int[]" = type { i32*, i64 } +%LinkedList = type { i64, %Node*, %Node* } +%Node = type { %Node*, %Node*, i32 } +%List = type { i64, i64, i32* } %Foo = type { i32, i32 } define void @test.Foo2__printme(%Foo2* %0) #0 { @@ -431,13 +431,13 @@ define void @test.main() #0 { entry: %list = alloca %LinkedList, align 8 %i = alloca i32, align 4 - %max = alloca i32, align 4 - %min = alloca i32, align 4 + %maxv = alloca i32, align 4 + %minv = alloca i32, align 4 %elements = alloca i32, align 4 %array = alloca %List, align 8 %i1 = alloca i32, align 4 - %a = alloca %Blob, align 4 - %b = alloca %Blob.0, align 8 + %a = alloca %Blob.0, align 4 + %b = alloca %Blob.1, align 8 %tempcoerce = alloca double, align 8 %ddx = alloca %Foo, align 4 %fro = alloca i32, align 4 @@ -487,8 +487,8 @@ loop.body: ; preds = %loop.cond loop.exit: ; preds = %loop.cond call void @"std::array::linkedlist.int.LinkedList__free"(%LinkedList* %list) %11 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([28 x i8], [28 x i8]* @.str.3, i32 0, i32 0), i32 -5, i32 14, i32 3) - store i32 14, i32* %max, align 4 - store i32 -5, i32* %min, align 4 + store i32 14, i32* %maxv, align 4 + store i32 -5, i32* %minv, align 4 store i32 3, i32* %elements, align 4 %12 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.4, i32 0, i32 0)) %13 = bitcast %List* %array to i8* @@ -521,15 +521,15 @@ loop.body5: ; preds = %loop.cond2 loop.exit8: ; preds = %loop.cond2 call void @"std::array::list.int.List__free"(%List* %array) - %21 = bitcast %Blob* %a to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %21, i8* align 4 bitcast (%Blob* @.__const.6 to i8*), i32 4, i1 false) - %22 = bitcast %Blob.0* %b to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %22, i8* align 8 bitcast (%Blob.0* @.__const.7 to i8*), i32 8, i1 false) - %23 = getelementptr inbounds %Blob, %Blob* %a, i32 0, i32 0 + %21 = bitcast %Blob.0* %a to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %21, i8* align 4 bitcast (%Blob.0* @.__const.6 to i8*), i32 4, i1 false) + %22 = bitcast %Blob.1* %b to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %22, i8* align 8 bitcast (%Blob.1* @.__const.7 to i8*), i32 8, i1 false) + %23 = getelementptr inbounds %Blob.0, %Blob.0* %a, i32 0, i32 0 %24 = load i32, i32* %23, align 4 %25 = call i32 @test2.int.getValue(i32 %24) %26 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.8, i32 0, i32 0), i32 %25) - %27 = getelementptr inbounds %Blob.0, %Blob.0* %b, i32 0, i32 0 + %27 = getelementptr inbounds %Blob.1, %Blob.1* %b, i32 0, i32 0 %28 = bitcast double* %tempcoerce to i8* %29 = bitcast double* %27 to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %28, i8* align 8 %29, i32 8, i1 false) diff --git a/test/test_suite/functions/test_regression_mingw.c3t b/test/test_suite/functions/test_regression_mingw.c3t index 6f7fa7739..ffc77e2f7 100644 --- a/test/test_suite/functions/test_regression_mingw.c3t +++ b/test/test_suite/functions/test_regression_mingw.c3t @@ -133,8 +133,8 @@ fn void main() printf("Min %d Max %d Elements: %d\n", MyEnum.min, MyEnum.max, (int)(MyEnum.elements)); - int max = MyEnum.max; - int min = MyEnum.min; + int maxv = MyEnum.max; + int minv = MyEnum.min; int elements = MyEnum.elements; printf("Hello\n"); IntArray array; @@ -205,7 +205,7 @@ fn Type getMult(Type a) } Type argh = 234; -optenum MyErr +fault MyErr { X, Y @@ -238,17 +238,16 @@ fn Type getValue(Blob blob) /* #expect: test.ll -%Blob = type { i32 } -%Blob.0 = type { double } -%LinkedList = type { i64, %Node*, %Node* } -%Node = type { %Node*, %Node*, i32 } -%List = type { i64, i64, i32* } +%Blob.0 = type { i32 } +%Blob.1 = type { double } %Foo2 = type { i32 } %Bobo = type { i16, float, i16, i16, float, i16 } %"int[]" = type { i32*, i64 } +%LinkedList = type { i64, %Node*, %Node* } +%Node = type { %Node*, %Node*, i32 } +%List = type { i64, i64, i32* } %Foo = type { i32, i32 } -@test2.int.argh = external global i32 @Bobo = linkonce_odr constant i8 1 @Blob = linkonce_odr constant i8 1 @Foor = linkonce_odr constant i8 1 @@ -262,8 +261,8 @@ fn Type getValue(Blob blob) @.str.3 = private unnamed_addr constant [28 x i8] c"Min %d Max %d Elements: %d\0A\00", align 1 @.str.4 = private unnamed_addr constant [7 x i8] c"Hello\0A\00", align 1 @.str.5 = private unnamed_addr constant [17 x i8] c"Element[%d]: %d\0A\00", align 1 -@.__const.6 = private unnamed_addr constant %Blob { i32 42 }, align 4 -@.__const.7 = private unnamed_addr constant %Blob.0 { double 3.330000e+01 }, align 8 +@.__const.6 = private unnamed_addr constant %Blob.0 { i32 42 }, align 4 +@.__const.7 = private unnamed_addr constant %Blob.1 { double 3.330000e+01 }, align 8 @.str.8 = private unnamed_addr constant [10 x i8] c"a was %d\0A\00", align 1 @.str.9 = private unnamed_addr constant [10 x i8] c"b was %f\0A\00", align 1 @.str.10 = private unnamed_addr constant [17 x i8] c"Mult int was %d\0A\00", align 1 @@ -280,37 +279,6 @@ fn Type getValue(Blob blob) @.str.21 = private unnamed_addr constant [12 x i8] c"Foo is: %d\0A\00", align 1 @.str.22 = private unnamed_addr constant [9 x i8] c"Mutating\00", align 1 -declare i32 @test2.int.getValue(i32) - -declare double @test2.double.getValue(i64) - -declare i32 @test2.int.getMult(i32) - -declare double @test2.double.getMult(double) - -declare void @hello_world.hello() - -declare void @"std::array::linkedlist.int.LinkedList__push"(%LinkedList*, i32) - -declare i64 @"std::array::linkedlist.int.LinkedList__len"(%LinkedList*) - -declare i32 @"std::array::linkedlist.int.LinkedList__get"(%LinkedList*, i64) - -declare void @"std::array::linkedlist.int.LinkedList__free"(%LinkedList*) - -declare void @"std::array::list.int.List__append"(%List*, i32) - -declare void @"std::array::list.int.List__push"(%List*, i32) - -declare void @"std::array::list.int.List__insertAt"(%List*, i64, i32) - -declare i64 @"std::array::list.int.List__len"(%List*) - -declare i32 @"std::array::list.int.List__get"(%List*, i64) - -declare void @"std::array::list.int.List__free"(%List*) - -; Function Attrs: nounwind define void @test.Foo2__printme(%Foo2* %0) #0 { entry: %1 = getelementptr inbounds %Foo2, %Foo2* %0, i32 0, i32 0 @@ -319,7 +287,6 @@ entry: ret void } -; Function Attrs: nounwind define i32 @test.Foo2__mutate(%Foo2* %0) #0 { entry: %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.22, i32 0, i32 0)) @@ -487,13 +454,13 @@ define void @test.main() #0 { entry: %list = alloca %LinkedList, align 8 %i = alloca i32, align 4 - %max = alloca i32, align 4 - %min = alloca i32, align 4 + %maxv = alloca i32, align 4 + %minv = alloca i32, align 4 %elements = alloca i32, align 4 %array = alloca %List, align 8 %i1 = alloca i32, align 4 - %a = alloca %Blob, align 4 - %b = alloca %Blob.0, align 8 + %a = alloca %Blob.0, align 4 + %b = alloca %Blob.1, align 8 %tempcoerce = alloca i64, align 8 %ddx = alloca %Foo, align 4 %fro = alloca i32, align 4 @@ -549,8 +516,8 @@ loop.body: ; preds = %loop.cond loop.exit: ; preds = %loop.cond call void @"std::array::linkedlist.int.LinkedList__free"(%LinkedList* %list) %11 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([28 x i8], [28 x i8]* @.str.3, i32 0, i32 0), i32 -5, i32 14, i32 3) - store i32 14, i32* %max, align 4 - store i32 -5, i32* %min, align 4 + store i32 14, i32* %maxv, align 4 + store i32 -5, i32* %minv, align 4 store i32 3, i32* %elements, align 4 %12 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.4, i32 0, i32 0)) %13 = bitcast %List* %array to i8* @@ -583,15 +550,15 @@ loop.body5: ; preds = %loop.cond2 loop.exit8: ; preds = %loop.cond2 call void @"std::array::list.int.List__free"(%List* %array) - %21 = bitcast %Blob* %a to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %21, i8* align 4 bitcast (%Blob* @.__const.6 to i8*), i32 4, i1 false) - %22 = bitcast %Blob.0* %b to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %22, i8* align 8 bitcast (%Blob.0* @.__const.7 to i8*), i32 8, i1 false) - %23 = getelementptr inbounds %Blob, %Blob* %a, i32 0, i32 0 + %21 = bitcast %Blob.0* %a to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %21, i8* align 4 bitcast (%Blob.0* @.__const.6 to i8*), i32 4, i1 false) + %22 = bitcast %Blob.1* %b to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %22, i8* align 8 bitcast (%Blob.1* @.__const.7 to i8*), i32 8, i1 false) + %23 = getelementptr inbounds %Blob.0, %Blob.0* %a, i32 0, i32 0 %24 = load i32, i32* %23, align 4 %25 = call i32 @test2.int.getValue(i32 %24) %26 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.8, i32 0, i32 0), i32 %25) - %27 = getelementptr inbounds %Blob.0, %Blob.0* %b, i32 0, i32 0 + %27 = getelementptr inbounds %Blob.1, %Blob.1* %b, i32 0, i32 0 %28 = bitcast i64* %tempcoerce to i8* %29 = bitcast double* %27 to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %28, i8* align 8 %29, i32 8, i1 false) @@ -690,6 +657,40 @@ loop.exit8: ; preds = %loop.cond2 ret void } +declare void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i32, i1 immarg) #1 + +declare void @hello_world.hello() + +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #2 + +declare void @"std::array::linkedlist.int.LinkedList__push"(%LinkedList*, i32) + +declare i64 @"std::array::linkedlist.int.LinkedList__len"(%LinkedList*) + +declare i32 @"std::array::linkedlist.int.LinkedList__get"(%LinkedList*, i64) + +declare void @"std::array::linkedlist.int.LinkedList__free"(%LinkedList*) + +declare void @"std::array::list.int.List__append"(%List*, i32) + +declare void @"std::array::list.int.List__push"(%List*, i32) + +declare void @"std::array::list.int.List__insertAt"(%List*, i64, i32) + +declare i64 @"std::array::list.int.List__len"(%List*) + +declare i32 @"std::array::list.int.List__get"(%List*, i64) + +declare void @"std::array::list.int.List__free"(%List*) + +declare i32 @test2.int.getValue(i32) + +declare double @test2.double.getValue(i64) + +declare i32 @test2.int.getMult(i32) + +declare double @test2.double.getMult(double) + // #expect: hello_world.ll define void @hello_world.hello() diff --git a/test/test_suite/generic/generic_idents.c3t b/test/test_suite/generic/generic_idents.c3t index 0723931a4..32d7ad366 100644 --- a/test/test_suite/generic/generic_idents.c3t +++ b/test/test_suite/generic/generic_idents.c3t @@ -43,10 +43,6 @@ entry: // #expect: test.ll -declare i32 @gen.int.mult(i32) - -declare double @gen.double.addMult(double, double, double) - define i32 @test.getIt(i32 %0) #0 { entry: %1 = call i32 @gen.int.mult(i32 %0) @@ -60,6 +56,9 @@ entry: ret double %1 } +declare i32 @gen.int.mult(i32) + +declare double @gen.double.addMult(double, double, double) // #expect: gen.double.ll diff --git a/test/test_suite/import/access_other_module.c3t b/test/test_suite/import/access_other_module.c3t new file mode 100644 index 000000000..10c74432f --- /dev/null +++ b/test/test_suite/import/access_other_module.c3t @@ -0,0 +1,23 @@ +// #target: x64-darwin +module foo; +fn void main() +{ + void* foekf = &math::log; + double* xok = &math::DIV_1_SQRT2; +} + +/* #expect: foo.ll + + +@"std::math.DIV_1_SQRT2" = external global double + +define void @foo.main() #0 { +entry: + %foekf = alloca i8*, align 8 + %xok = alloca double*, align 8 + store i8* bitcast (double (double)* @"std::math.log" to i8*), i8** %foekf, align 8 + store double* @"std::math.DIV_1_SQRT2", double** %xok, align 8 + ret void +} + +declare double @"std::math.log"(double) \ No newline at end of file diff --git a/test/test_suite/initializer_lists/fasta.c3t b/test/test_suite/initializer_lists/fasta.c3t index 19bed0fb0..68e52d28f 100644 --- a/test/test_suite/initializer_lists/fasta.c3t +++ b/test/test_suite/initializer_lists/fasta.c3t @@ -8,10 +8,10 @@ const SEED = 42u; uint seed = SEED; -fn float fasta_rand(float max) +fn float fasta_rand(float maxval) { seed = (seed * IA + IC) % IM; - return max * seed / IM; + return maxval * seed / IM; } private char[] alu = diff --git a/test/test_suite/initializer_lists/statics.c3t b/test/test_suite/initializer_lists/statics.c3t index d4faaf207..b0586a0de 100644 --- a/test/test_suite/initializer_lists/statics.c3t +++ b/test/test_suite/initializer_lists/statics.c3t @@ -41,9 +41,6 @@ fn int main() @test.c = internal unnamed_addr global %"Bar[]" { %Bar* getelementptr inbounds ([1 x %Bar], [1 x %Bar]* @.taddr, i32 0, i32 0), i64 1 }, align 8 @.str = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1 -declare i32 @printf(i8*, ...) - -; Function Attrs: nounwind define void @statics.test() #0 { entry: %b = alloca %"Bar[]", align 8 @@ -83,7 +80,6 @@ entry: ret void } -; Function Attrs: nounwind define i32 @main() #0 { entry: call void @statics.test() @@ -92,3 +88,4 @@ entry: ret i32 1 } +declare i32 @printf(i8*, ...) \ No newline at end of file diff --git a/test/test_suite/macros/userland_bitcast.c3t b/test/test_suite/macros/userland_bitcast.c3t index df1bb0d96..3ee407dad 100644 --- a/test/test_suite/macros/userland_bitcast.c3t +++ b/test/test_suite/macros/userland_bitcast.c3t @@ -5,6 +5,7 @@ macro testbitcast(expr, $Type) $assert($sizeof(expr) == $Type.sizeof, "Cannot bitcast between types of different size."); $Type x = void; var $size = (usize)($sizeof(expr)); + $if ($alignof(expr) >= 8 && $alignof($Type) >= 8): ulong *b = (ulong*)(&expr); ulong *to = (ulong*)(&x); diff --git a/test/test_suite/methods/enum_distinct_err_methods.c3t b/test/test_suite/methods/enum_distinct_err_methods.c3t index 3eed42e4f..30642e9f6 100644 --- a/test/test_suite/methods/enum_distinct_err_methods.c3t +++ b/test/test_suite/methods/enum_distinct_err_methods.c3t @@ -3,7 +3,7 @@ module foo; import std::io; -optenum Foo +fault Foo { X } diff --git a/test/test_suite/methods/extension_method.c3t b/test/test_suite/methods/extension_method.c3t index 84994d510..2b87b4a01 100644 --- a/test/test_suite/methods/extension_method.c3t +++ b/test/test_suite/methods/extension_method.c3t @@ -27,7 +27,6 @@ fn int main() // #expect: abc.ll -declare void @foo.Bar__test(%Bar*) define i32 @main() entry: @@ -36,3 +35,5 @@ entry: store i32 0, i32* %0, align 4 call void @foo.Bar__test(%Bar* %bar) ret i32 0 + +declare void @foo.Bar__test(%Bar*) diff --git a/test/test_suite/statements/switch_errors.c3 b/test/test_suite/statements/switch_errors.c3 index aa2317d69..9d4c2e0fa 100644 --- a/test/test_suite/statements/switch_errors.c3 +++ b/test/test_suite/statements/switch_errors.c3 @@ -135,7 +135,7 @@ fn void test_missing_no_cases(Baz x) } } -optenum MathError +fault MathError { DIVISION_BY_ZERO } diff --git a/test/test_suite/statements/various_switching.c3t b/test/test_suite/statements/various_switching.c3t index 6f27af2ad..4378e0e54 100644 --- a/test/test_suite/statements/various_switching.c3t +++ b/test/test_suite/statements/various_switching.c3t @@ -2,11 +2,11 @@ module mymodule; extern fn void printf(char *, ...); -optenum HelloErr +fault HelloErr { FOO, } -optenum ByeErr +fault ByeErr { BAR, BAZ diff --git a/test/test_suite/switch/failable_switch.c3 b/test/test_suite/switch/failable_switch.c3 index 68963012a..79cc80b4d 100644 --- a/test/test_suite/switch/failable_switch.c3 +++ b/test/test_suite/switch/failable_switch.c3 @@ -1,4 +1,4 @@ -optenum MyError +fault MyError { FOO }