From 9886d381c0af7ab5c7a73b4ceb310026ac7c2dda Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 29 Jul 2022 18:47:24 +0200 Subject: [PATCH] Update allocator and resolution. --- lib/std/core/allocators/dynamic_arena.c3 | 182 ++++++++++ .../core/{ => allocators}/mem_allocator_fn.c3 | 15 +- lib/std/core/allocators/memory_arena.c3 | 82 +++++ lib/std/core/mem.c3 | 76 +--- lib/std/core/mem_allocator.c3 | 326 ++++-------------- lib/std/core/mem_temp_allocator.c3 | 4 +- lib/std/core/str.c3 | 12 +- lib/std/core/string.c3 | 25 +- resources/examples/contextfree/boolerr.c3 | 5 +- src/compiler/ast.c | 2 +- src/compiler/compiler_internal.h | 4 +- src/compiler/context.c | 6 +- src/compiler/llvm_codegen.c | 6 +- src/compiler/llvm_codegen_debug_info.c | 9 + src/compiler/llvm_codegen_function.c | 2 +- src/compiler/llvm_codegen_type.c | 4 +- src/compiler/sema_decls.c | 52 +-- src/compiler/sema_expr.c | 25 +- src/compiler/sema_internal.h | 1 + src/compiler/sema_name_resolution.c | 39 ++- src/compiler/sema_passes.c | 2 +- src/compiler/semantic_analyser.c | 25 +- .../attributes/attribute_visibility.c3t | 12 + .../attributes/attribute_visibility.c3t | 12 + 24 files changed, 488 insertions(+), 440 deletions(-) create mode 100644 lib/std/core/allocators/dynamic_arena.c3 rename lib/std/core/{ => allocators}/mem_allocator_fn.c3 (65%) create mode 100644 lib/std/core/allocators/memory_arena.c3 create mode 100644 test/test_suite/attributes/attribute_visibility.c3t create mode 100644 test/test_suite2/attributes/attribute_visibility.c3t diff --git a/lib/std/core/allocators/dynamic_arena.c3 b/lib/std/core/allocators/dynamic_arena.c3 new file mode 100644 index 000000000..65305197e --- /dev/null +++ b/lib/std/core/allocators/dynamic_arena.c3 @@ -0,0 +1,182 @@ +module std::core::mem::allocator; + +private struct DynamicArenaPage +{ + void* memory; + void* prev_arena; + usize total; + usize used; + void* last_ptr; +} + + +/** + * @require ptr && this + * @require this.page `tried to free pointer on invalid allocator` + */ +private fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this, void* ptr) +{ + DynamicArenaPage* current_page = this.page; + if (ptr == current_page.last_ptr) + { + current_page.used = (usize)((ptr - DEFAULT_SIZE_PREFIX) - current_page.memory); + } + current_page.last_ptr = null; +} + +/** + * @require old_pointer && size > 0 + * @require this.page `tried to realloc pointer on invalid allocator` + */ +private fn void*! DynamicArenaAllocator.realloc(DynamicArenaAllocator* this, void* old_pointer, usize size, usize alignment) +{ + DynamicArenaPage* current_page = this.page; + alignment = alignment_for_allocation(alignment); + usize* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX; + usize old_size = *old_size_ptr; + // We have the old pointer and it's correctly aligned. + if (old_size >= size && mem::ptr_is_aligned(old_pointer, alignment)) + { + *old_size_ptr = size; + if (current_page.last_ptr == old_pointer) + { + current_page.used = (usize)((old_pointer - DEFAULT_SIZE_PREFIX) - current_page.memory); + } + return old_pointer; + } + if REUSE: (current_page.last_ptr == old_pointer && mem::ptr_is_aligned(old_pointer, alignment)) + { + assert(size > old_size); + usize add_size = size - old_size; + if (add_size + current_page.used > current_page.total) break REUSE; + *old_size_ptr = size; + current_page.used += add_size; + return old_pointer; + } + void* new_mem = this.alloc(size, alignment)?; + mem::copy(new_mem, old_pointer, old_size); + return new_mem; +} + +private fn void DynamicArenaAllocator.reset(DynamicArenaAllocator* this) +{ + DynamicArenaPage* page = this.page; + DynamicArenaPage** unused_page_ptr = &this.unused_page; + while (page) + { + DynamicArenaPage* next_page = page.prev_arena; + page.used = 0; + DynamicArenaPage* prev_unused = *unused_page_ptr; + *unused_page_ptr = page; + page.prev_arena = prev_unused; + page = next_page; + } + this.page = page; +} + +/** + * @require math::is_power_of_2(alignment) + * @require size > 0 + */ +private fn void*! DynamicArenaAllocator.alloc_new(DynamicArenaAllocator* this, usize size, usize alignment) +{ + usize page_size = max(this.page_size, size + DEFAULT_SIZE_PREFIX + alignment); + void* mem = this.backing_allocator.alloc(page_size)?; + DynamicArenaPage*! page = this.backing_allocator.alloc(DynamicArenaPage.sizeof); + if (catch err = page) + { + this.backing_allocator.free(mem)?; + return err!; + } + page.memory = mem; + usize offset = mem::aligned_offset((usize)mem + DEFAULT_SIZE_PREFIX, alignment) - (usize)mem; + usize* size_ptr = mem + offset - DEFAULT_SIZE_PREFIX; + *size_ptr = size; + page.prev_arena = this.page; + page.total = page_size; + page.used = size + offset; + this.page = page; + + return page.last_ptr = page.memory + offset; +} + +/** + * @require !alignment || math::is_power_of_2(alignment) + * @require size > 0 + * @require this + */ +private fn void*! DynamicArenaAllocator.alloc(DynamicArenaAllocator* this, usize size, usize alignment) +{ + alignment = alignment_for_allocation(alignment); + DynamicArenaPage* page = this.page; + if (!page && this.unused_page) + { + this.page = page = this.unused_page; + this.unused_page = page.prev_arena; + page.prev_arena = null; + } + if (!page) return this.alloc_new(size, alignment); + usize start = mem::aligned_offset((uptr)page.memory + page.used + DEFAULT_SIZE_PREFIX, alignment) - (usize)page.memory; + usize new_used = start + size; + if ALLOCATE_NEW: (new_used > page.total) + { + if ((page = this.unused_page)) + { + start = mem::aligned_offset((uptr)page.memory + DEFAULT_SIZE_PREFIX, alignment) - (usize)page.memory; + new_used = start + size; + if (page.total >= new_used) + { + this.unused_page = page.prev_arena; + page.prev_arena = this.page; + this.page = page; + break ALLOCATE_NEW; + } + } + return this.alloc_new(size, alignment); + } + page.used = new_used; + void* mem = page.memory + start; + usize* size_offset = mem - DEFAULT_SIZE_PREFIX; + *size_offset = size; + return mem; +} + +/** + * @require !alignment || math::is_power_of_2(alignment) + * @require data `unexpectedly missing the allocator` + */ +private fn void*! dynamic_arena_allocator_function(Allocator* data, usize size, usize alignment, void* old_pointer, AllocationKind kind) +{ + DynamicArenaAllocator* allocator = (DynamicArenaAllocator*)data; + switch (kind) + { + case CALLOC: + assert(!old_pointer, "Unexpected no old pointer for calloc."); + if (!size) return null; + void* mem = allocator.alloc(size, alignment)?; + mem::set(mem, 0, size); + return mem; + case ALLOC: + assert(!old_pointer, "Unexpected no old pointer for alloc."); + if (!size) return null; + return allocator.alloc(size, alignment); + case REALLOC: + if (!size) + { + if (!old_pointer) return null; + allocator.free(old_pointer); + return null; + } + if (!old_pointer) return allocator.alloc(size, alignment); + void* mem = allocator.realloc(old_pointer, size, alignment)?; + return mem; + case FREE: + if (!old_pointer) return null; + allocator.free(old_pointer); + return null; + case RESET: + allocator.reset(); + return null; + } + unreachable(); +} diff --git a/lib/std/core/mem_allocator_fn.c3 b/lib/std/core/allocators/mem_allocator_fn.c3 similarity index 65% rename from lib/std/core/mem_allocator_fn.c3 rename to lib/std/core/allocators/mem_allocator_fn.c3 index 8e9f83872..0aeb59f69 100644 --- a/lib/std/core/mem_allocator_fn.c3 +++ b/lib/std/core/allocators/mem_allocator_fn.c3 @@ -1,7 +1,10 @@ -module std::core::mem; +module std::core::mem::allocator; import libc; -private fn void*! null_allocator_fn(void *data, usize bytes, usize alignment, void* old_pointer, AllocationKind kind) +private const Allocator _NULL_ALLOCATOR = { &null_allocator_fn }; +private const Allocator _SYSTEM_ALLOCATOR = { &libc_allocator_fn }; + +private fn void*! null_allocator_fn(Allocator* this, usize bytes, usize alignment, void* old_pointer, AllocationKind kind) { switch (kind) { @@ -14,7 +17,7 @@ private fn void*! null_allocator_fn(void *data, usize bytes, usize alignment, vo } } -fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* old_pointer, AllocationKind kind) @inline +fn void*! libc_allocator_fn(Allocator* 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"); @@ -25,7 +28,7 @@ fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* ol case ALLOC: if (alignment > DEFAULT_MEM_ALIGNMENT) { - data = (void*)aligned_offset((iptr)libc::malloc(bytes + alignment), alignment); + data = (void*)mem::aligned_offset((iptr)libc::malloc(bytes + alignment), alignment); } else { @@ -36,7 +39,7 @@ fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* ol case CALLOC: if (alignment > DEFAULT_MEM_ALIGNMENT) { - data = (void*)aligned_offset((iptr)libc::calloc(bytes + alignment, 1), alignment); + data = (void*)mem::aligned_offset((iptr)libc::calloc(bytes + alignment, 1), alignment); } else { @@ -47,7 +50,7 @@ fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* ol case REALLOC: if (alignment > DEFAULT_MEM_ALIGNMENT) { - data = (void*)aligned_offset((iptr)libc::realloc(old_pointer, bytes + alignment), alignment); + data = (void*)mem::aligned_offset((iptr)libc::realloc(old_pointer, bytes + alignment), alignment); } else { diff --git a/lib/std/core/allocators/memory_arena.c3 b/lib/std/core/allocators/memory_arena.c3 new file mode 100644 index 000000000..f8ae57688 --- /dev/null +++ b/lib/std/core/allocators/memory_arena.c3 @@ -0,0 +1,82 @@ +module std::core::mem::allocator; + +/** + * @require !alignment || math::is_power_of_2(alignment) + * @require data `unexpectedly missing the allocator` + */ +private fn void*! arena_allocator_function(Allocator* data, usize size, usize alignment, void* old_pointer, AllocationKind kind) +{ + MemoryArena* arena = (MemoryArena*)data; + switch (kind) + { + case CALLOC: + case ALLOC: + assert(!old_pointer, "Unexpected old pointer for alloc."); + if (!size) return null; + alignment = alignment_for_allocation(alignment); + void* mem = arena.alloc(size, alignment, DEFAULT_SIZE_PREFIX)?; + *(usize*)(mem - DEFAULT_SIZE_PREFIX) = size; + if (kind == AllocationKind.CALLOC) mem::set(mem, 0, size); + return mem; + case REALLOC: + if (!size) nextcase FREE; + if (!old_pointer) nextcase ALLOC; + assert((uptr)old_pointer >= (uptr)arena.memory, "Pointer originates from a different allocator."); + if (size > arena.total) return AllocationFailure.OUT_OF_MEMORY!; + alignment = alignment_for_allocation(alignment); + usize* old_size_ptr = (usize*)(old_pointer - DEFAULT_SIZE_PREFIX); + usize old_size = *old_size_ptr; + // Do last allocation and alignment match? + if (arena.memory + arena.used == old_pointer + old_size && mem::ptr_is_aligned(old_pointer, alignment)) + { + if (old_size >= size) + { + *old_size_ptr = size; + arena.used -= old_size - size; + return old_pointer; + } + usize new_used = arena.used + size - old_size; + if (new_used > arena.total) return AllocationFailure.OUT_OF_MEMORY!; + arena.used = new_used; + *old_size_ptr = size; + return old_pointer; + } + // Otherwise just allocate new memory. + void* mem = arena.alloc(size, alignment, DEFAULT_SIZE_PREFIX)?; + *(usize*)(mem - DEFAULT_SIZE_PREFIX) = size; + mem::copy(mem, old_pointer, old_size); + return mem; + case FREE: + if (!old_pointer) return null; + assert((uptr)old_pointer >= (uptr)arena.memory, "Pointer originates from a different allocator."); + usize old_size = *(usize*)(old_pointer - DEFAULT_SIZE_PREFIX); + if (old_pointer + old_size == arena.memory + arena.used) + { + arena.used -= old_size; + } + return null; + case RESET: + arena.used = 0; + return null; + } + unreachable(); +} + +/** + * @require alignment > 0 `alignment must be non zero` + * @require math::is_power_of_2(alignment) + * @require size > 0 + * @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big` + * @require this != null + **/ +private fn void*! MemoryArena.alloc(MemoryArena* this, usize size, usize alignment, usize prefixed_bytes = 0) +{ + void* start_mem = this.memory; + void* unaligned_pointer = start_mem + this.used + prefixed_bytes; + if ((uptr)unaligned_pointer < (uptr)start_mem) return AllocationFailure.OUT_OF_MEMORY!; + usize offset_start = mem::aligned_offset((usize)(uptr)unaligned_pointer, alignment) - (usize)(uptr)start_mem; + usize end = offset_start + size; + if (end > this.total || end < offset_start) return AllocationFailure.OUT_OF_MEMORY!; + this.used = end; + return start_mem + offset_start; +} \ No newline at end of file diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index 064eee4db..f862debbb 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -51,33 +51,6 @@ macro void memset(void* dst, char val, usize bytes, bool $is_volatile = false, u } -enum AllocationKind -{ - ALLOC, - CALLOC, - REALLOC, - FREE, - RESET, -} - -fault AllocationFailure -{ - OUT_OF_MEMORY, - UNSUPPORTED_OPERATION, -} - -private tlocal Allocator thread_allocator = { null, SYSTEM_ALLOCATOR }; - -macro Allocator current_allocator() -{ - return thread_allocator; -} - -struct Allocator -{ - void* data; - AllocatorFunction function; -} macro @clone(&value) @builtin { @@ -153,9 +126,9 @@ fn void free(void* ptr) @builtin /** * Run with a specific allocator inside of the macro body. **/ -macro void @with_allocator(Allocator allocator; @body()) +macro void @with_allocator(Allocator* allocator; @body()) { - Allocator old_allocator = thread_allocator; + Allocator* old_allocator = thread_allocator; thread_allocator = allocator; defer thread_allocator = old_allocator; @body(); @@ -166,47 +139,12 @@ fn void*! talloc(usize size) return temp_allocator.alloc(size); } -struct MemoryArena +private tlocal Allocator* thread_allocator = allocator::LIBC_ALLOCATOR; +macro Allocator* current_allocator() { - void* memory; - usize total; - usize used; + return thread_allocator; } -/** - * @require alignment > 0 `alignment must be non zero` - * @require math::is_power_of_2(alignment) - * @require size > 0 - * @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big` - * @require this != null - **/ -fn void*! MemoryArena.alloc(MemoryArena* this, usize size, usize alignment, usize prefixed_bytes = 0) -{ - void* start_mem = this.memory; - void* unaligned_pointer = start_mem + this.used + prefixed_bytes; - if ((uptr)unaligned_pointer < (uptr)start_mem) return AllocationFailure.OUT_OF_MEMORY!; - usize offset_start = aligned_offset((usize)(uptr)unaligned_pointer, alignment) - (usize)(uptr)start_mem; - usize end = offset_start + size; - if (end > this.total || end < offset_start) return AllocationFailure.OUT_OF_MEMORY!; - this.used = end; - return start_mem + offset_start; -} -/** - * Initialize a memory arena for use using the provided bytes. - * - * @require this != null - **/ -fn void MemoryArena.init(MemoryArena* this, char[] data) -{ - this.memory = data.ptr; - this.total = data.len; - this.used = 0; -} -/** - * @require this != null - **/ -fn void MemoryArena.reset(MemoryArena* this) -{ - this.used = 0; -} \ No newline at end of file + + diff --git a/lib/std/core/mem_allocator.c3 b/lib/std/core/mem_allocator.c3 index 1e74f5a3c..13e427101 100644 --- a/lib/std/core/mem_allocator.c3 +++ b/lib/std/core/mem_allocator.c3 @@ -1,63 +1,71 @@ -module std::core::mem; - -define AllocatorFunction = fn void*!(void* alloc_data, usize new_size, usize alignment, void* old_pointer, AllocationKind kind); +module std::core::mem::allocator; const MAX_MEMORY_ALIGNMENT = 0x1000_0000; const DEFAULT_MEM_ALIGNMENT = $alignof(void*) * 2; const DEFAULT_SIZE_PREFIX = usize.sizeof; const DEFAULT_SIZE_PREFIX_ALIGNMENT = $alignof(usize); -Allocator main_allocator = { null, SYSTEM_ALLOCATOR }; +const Allocator* NULL_ALLOCATOR = &_NULL_ALLOCATOR; +const Allocator* LIBC_ALLOCATOR = &_SYSTEM_ALLOCATOR; + +define AllocatorFunction = fn void*!(Allocator* allocator, usize new_size, usize alignment, void* old_pointer, AllocationKind kind); + +struct Allocator +{ + AllocatorFunction function; +} + +enum AllocationKind +{ + ALLOC, + CALLOC, + REALLOC, + FREE, + RESET, +} + +fault AllocationFailure +{ + OUT_OF_MEMORY, + UNSUPPORTED_OPERATION, +} + -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 +fn void*! Allocator.alloc(Allocator* allocator, usize size, usize alignment = 0) @inline { - return allocator.function(allocator.data, size, alignment, null, ALLOC); + return allocator.function(allocator, 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 +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); + return allocator.function(allocator, 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 +fn void*! Allocator.calloc(Allocator* allocator, usize size, usize alignment = 0) @inline { - return allocator.function(allocator.data, size, alignment, null, CALLOC); + return allocator.function(allocator, size, alignment, null, CALLOC); } -fn void! Allocator.free(Allocator *allocator, void* old_pointer) @inline +fn void! Allocator.free(Allocator* allocator, void* old_pointer) @inline { - allocator.function(allocator.data, 0, 0, old_pointer, FREE)?; + allocator.function(allocator, 0, 0, old_pointer, FREE)?; } -fn void Allocator.reset(Allocator *allocator) +fn void Allocator.reset(Allocator* allocator) { - allocator.function(allocator.data, 0, 0, null, RESET)!!; + allocator.function(allocator, 0, 0, null, RESET)!!; } - -fn Allocator arena_allocator(MemoryArena *arena) -{ - return { arena, &arena_allocator_function }; -} - -fn Allocator dynamic_arena_allocator(DynamicArenaAllocator* allocator) -{ - return { allocator, &dynamic_arena_allocator_function }; -} - - private fn usize alignment_for_allocation(usize alignment) @inline { if (alignment < DEFAULT_MEM_ALIGNMENT) @@ -67,136 +75,26 @@ private fn usize alignment_for_allocation(usize alignment) @inline return alignment; } -/** - * @require !alignment || math::is_power_of_2(alignment) - * @require data `unexpectedly missing the allocator` - */ -fn void*! arena_allocator_function(void* data, usize size, usize alignment, void* old_pointer, AllocationKind kind) -{ - MemoryArena* arena = data; - switch (kind) - { - case CALLOC: - case ALLOC: - assert(!old_pointer, "Unexpected old pointer for alloc."); - if (!size) return null; - alignment = alignment_for_allocation(alignment); - void* mem = arena.alloc(size, alignment, DEFAULT_SIZE_PREFIX)?; - *(usize*)(mem - DEFAULT_SIZE_PREFIX) = size; - if (kind == AllocationKind.CALLOC) mem::set(mem, 0, size); - return mem; - case REALLOC: - if (!size) nextcase FREE; - if (!old_pointer) nextcase ALLOC; - assert((uptr)old_pointer >= (uptr)arena.memory, "Pointer originates from a different allocator."); - if (size > arena.total) return AllocationFailure.OUT_OF_MEMORY!; - alignment = alignment_for_allocation(alignment); - usize* old_size_ptr = (usize*)(old_pointer - DEFAULT_SIZE_PREFIX); - usize old_size = *old_size_ptr; - // Do last allocation and alignment match? - if (arena.memory + arena.used == old_pointer + old_size && ptr_is_aligned(old_pointer, alignment)) - { - if (old_size >= size) - { - *old_size_ptr = size; - arena.used -= old_size - size; - return old_pointer; - } - usize new_used = arena.used + size - old_size; - if (new_used > arena.total) return AllocationFailure.OUT_OF_MEMORY!; - arena.used = new_used; - *old_size_ptr = size; - return old_pointer; - } - // Otherwise just allocate new memory. - void* mem = arena.alloc(size, alignment, DEFAULT_SIZE_PREFIX)?; - *(usize*)(mem - DEFAULT_SIZE_PREFIX) = size; - copy(mem, old_pointer, old_size); - return mem; - case FREE: - if (!old_pointer) return null; - assert((uptr)old_pointer >= (uptr)arena.memory, "Pointer originates from a different allocator."); - usize old_size = *(usize*)(old_pointer - DEFAULT_SIZE_PREFIX); - if (old_pointer + old_size == arena.memory + arena.used) - { - arena.used -= old_size; - } - return null; - case RESET: - arena.used = 0; - return null; - } - unreachable(); -} - struct DynamicArenaAllocator { - Allocator backing_allocator; + inline Allocator allocator; + Allocator* backing_allocator; DynamicArenaPage* page; DynamicArenaPage* unused_page; usize page_size; } -private struct DynamicArenaPage -{ - void* memory; - void* prev_arena; - usize total; - usize used; - void* last_ptr; -} - - -/** - * @require !alignment || math::is_power_of_2(alignment) - * @require data `unexpectedly missing the allocator` - */ -fn void*! dynamic_arena_allocator_function(void* data, usize size, usize alignment, void* old_pointer, AllocationKind kind) -{ - DynamicArenaAllocator* allocator = data; - switch (kind) - { - case CALLOC: - assert(!old_pointer, "Unexpected no old pointer for calloc."); - if (!size) return null; - void* mem = allocator.alloc(size, alignment)?; - set(mem, 0, size); - return mem; - case ALLOC: - assert(!old_pointer, "Unexpected no old pointer for alloc."); - if (!size) return null; - return allocator.alloc(size, alignment); - case REALLOC: - if (!size) - { - if (!old_pointer) return null; - allocator.free(old_pointer); - return null; - } - if (!old_pointer) return allocator.alloc(size, alignment); - void* mem = allocator.realloc(old_pointer, size, alignment)?; - return mem; - case FREE: - if (!old_pointer) return null; - allocator.free(old_pointer); - return null; - case RESET: - allocator.reset(); - return null; - } - unreachable(); -} - /** * @require page_size >= 128 * @require this != null **/ -fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usize page_size, Allocator* backing_allocator = null) +fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usize page_size, Allocator* backing_allocator = mem::allocator()) { + this.function = &dynamic_arena_allocator_function; this.page = null; this.unused_page = null; this.page_size = page_size; - this.backing_allocator = backing_allocator ? *backing_allocator : main_allocator; + this.backing_allocator = backing_allocator; } /** @@ -222,134 +120,32 @@ fn void DynamicArenaAllocator.destroy(DynamicArenaAllocator* this) this.unused_page = null; } -/** - * @require ptr && this - * @require this.page `tried to free pointer on invalid allocator` - */ -private fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this, void* ptr) + +struct MemoryArena { - DynamicArenaPage* current_page = this.page; - if (ptr == current_page.last_ptr) - { - current_page.used = (usize)((ptr - DEFAULT_SIZE_PREFIX) - current_page.memory); - } - current_page.last_ptr = null; + inline Allocator allocator; + void* memory; + usize total; + usize used; } /** - * @require old_pointer && size > 0 - * @require this.page `tried to realloc pointer on invalid allocator` - */ -private fn void*! DynamicArenaAllocator.realloc(DynamicArenaAllocator* this, void* old_pointer, usize size, usize alignment) + * Initialize a memory arena for use using the provided bytes. + * + * @require this != null + **/ +fn void MemoryArena.init(MemoryArena* this, char[] data) { - DynamicArenaPage* current_page = this.page; - alignment = alignment_for_allocation(alignment); - usize* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX; - usize old_size = *old_size_ptr; - // We have the old pointer and it's correctly aligned. - if (old_size >= size && ptr_is_aligned(old_pointer, alignment)) - { - *old_size_ptr = size; - if (current_page.last_ptr == old_pointer) - { - current_page.used = (usize)((old_pointer - DEFAULT_SIZE_PREFIX) - current_page.memory); - } - return old_pointer; - } - if REUSE: (current_page.last_ptr == old_pointer && ptr_is_aligned(old_pointer, alignment)) - { - assert(size > old_size); - usize add_size = size - old_size; - if (add_size + current_page.used > current_page.total) break REUSE; - *old_size_ptr = size; - current_page.used += add_size; - return old_pointer; - } - void* new_mem = this.alloc(size, alignment)?; - copy(new_mem, old_pointer, old_size); - return new_mem; -} - -private fn void DynamicArenaAllocator.reset(DynamicArenaAllocator* this) -{ - DynamicArenaPage* page = this.page; - DynamicArenaPage** unused_page_ptr = &this.unused_page; - while (page) - { - DynamicArenaPage* next_page = page.prev_arena; - page.used = 0; - DynamicArenaPage* prev_unused = *unused_page_ptr; - *unused_page_ptr = page; - page.prev_arena = prev_unused; - page = next_page; - } - this.page = page; + this.function = &arena_allocator_function; + this.memory = data.ptr; + this.total = data.len; + this.used = 0; } /** - * @require math::is_power_of_2(alignment) - * @require size > 0 - */ -private fn void*! DynamicArenaAllocator.alloc_new(DynamicArenaAllocator* this, usize size, usize alignment) + * @require this != null + **/ +fn void MemoryArena.reset(MemoryArena* this) { - usize page_size = max(this.page_size, size + DEFAULT_SIZE_PREFIX + alignment); - void* mem = this.backing_allocator.alloc(page_size)?; - DynamicArenaPage*! page = this.backing_allocator.alloc(DynamicArenaPage.sizeof); - if (catch err = page) - { - this.backing_allocator.free(mem)?; - return err!; - } - page.memory = mem; - usize offset = aligned_offset((usize)mem + DEFAULT_SIZE_PREFIX, alignment) - (usize)mem; - usize* size_ptr = mem + offset - DEFAULT_SIZE_PREFIX; - *size_ptr = size; - page.prev_arena = this.page; - page.total = page_size; - page.used = size + offset; - this.page = page; - - return page.last_ptr = page.memory + offset; -} - -/** - * @require !alignment || math::is_power_of_2(alignment) - * @require size > 0 - * @require this - */ -private fn void*! DynamicArenaAllocator.alloc(DynamicArenaAllocator* this, usize size, usize alignment) -{ - alignment = alignment_for_allocation(alignment); - DynamicArenaPage* page = this.page; - if (!page && this.unused_page) - { - this.page = page = this.unused_page; - this.unused_page = page.prev_arena; - page.prev_arena = null; - } - if (!page) return this.alloc_new(size, alignment); - usize start = aligned_offset((uptr)page.memory + page.used + DEFAULT_SIZE_PREFIX, alignment) - (usize)page.memory; - usize new_used = start + size; - if ALLOCATE_NEW: (new_used > page.total) - { - if ((page = this.unused_page)) - { - start = aligned_offset((uptr)page.memory + DEFAULT_SIZE_PREFIX, alignment) - (usize)page.memory; - new_used = start + size; - if (page.total >= new_used) - { - this.unused_page = page.prev_arena; - page.prev_arena = this.page; - this.page = page; - break ALLOCATE_NEW; - } - } - return this.alloc_new(size, alignment); - } - page.used = new_used; - void* mem = page.memory + start; - usize* size_offset = mem - DEFAULT_SIZE_PREFIX; - *size_offset = size; - return mem; -} - + this.used = 0; +} \ No newline at end of file diff --git a/lib/std/core/mem_temp_allocator.c3 b/lib/std/core/mem_temp_allocator.c3 index 01bfd6bac..ca5eb3c79 100644 --- a/lib/std/core/mem_temp_allocator.c3 +++ b/lib/std/core/mem_temp_allocator.c3 @@ -30,12 +30,12 @@ fn void*! SlotAllocator.alloc(SlotAllocator *allocator, usize size) if (*page_pointer) { // TODO fix - main_allocator.free(*page_pointer)?; + thread_allocator.free(*page_pointer)?; *page_pointer = null; } if (size > allocator.page_size - $sizeof(page_pointer)) { - void* mem = main_allocator.alloc(size)?; + void* mem = thread_allocator.alloc(size)?; *page_pointer = mem; allocator.current_page = (allocator.current_page + 1) & (allocator.bitmask); return mem; diff --git a/lib/std/core/str.c3 b/lib/std/core/str.c3 index 3ee2e081b..02ba580e7 100644 --- a/lib/std/core/str.c3 +++ b/lib/std/core/str.c3 @@ -76,9 +76,8 @@ fn usize utf8_codepoints(char[] utf8) return len; } -fn Char32[]! utf8to32(char[] utf8, Allocator allocator = { null, null }) +fn Char32[]! utf8to32(char[] utf8, Allocator* allocator = mem::current_allocator) { - if (!allocator.function) allocator = mem::current_allocator(); usize codepoints = conv::utf8_codepoints(utf8); Char32* data = allocator.alloc(Char32.sizeof * (codepoints + 1))?; conv::utf8to32_unsafe(utf8, data)?; @@ -86,19 +85,17 @@ fn Char32[]! utf8to32(char[] utf8, Allocator allocator = { null, null }) return data[0..codepoints - 1]; } -fn char[] utf32to8(Char32[] utf32, Allocator allocator = { null, null }) +fn char[] utf32to8(Char32[] utf32, Allocator* allocator = mem::current_allocator) { usize len = conv::utf8len_for_utf32(utf32); - if (!allocator.function) allocator = mem::current_allocator(); char* data = allocator.alloc(len + 1)!!; conv::utf32to8_unsafe(utf32, data); data[len] = 0; return data[0..len - 1]; } -fn Char16[]! utf8to16(char[] utf8, Allocator allocator = { null, null }) +fn Char16[]! utf8to16(char[] utf8, Allocator* allocator = mem::current_allocator) { - if (!allocator.function) allocator = mem::current_allocator(); usize len16 = conv::utf16len_for_utf8(utf8); Char16* data = allocator.alloc((len16 + 1) * Char16.sizeof)?; conv::utf8to16_unsafe(utf8, data)?; @@ -107,9 +104,8 @@ fn Char16[]! utf8to16(char[] utf8, Allocator allocator = { null, null }) } -fn char[]! utf16to8(Char16[] utf16, Allocator allocator = { null, null }) +fn char[]! utf16to8(Char16[] utf16, Allocator* allocator = mem::current_allocator()) { - if (!allocator.function) allocator = mem::current_allocator(); usize len = conv::utf8len_for_utf16(utf16); char* data = allocator.alloc(len + 1)?; conv::utf16to8_unsafe(utf16, data)?; diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index 6f1a287ef..1be23e383 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -5,7 +5,7 @@ define String = distinct void*; private struct StringData { - Allocator allocator; + Allocator* allocator; usize len; usize capacity; char[*] chars; @@ -13,14 +13,9 @@ private struct StringData const usize MIN_CAPACITY = 16; -fn String new_with_capacity(usize capacity, Allocator allocator = { null, null }) +fn String new_with_capacity(usize capacity, Allocator* allocator = mem::current_allocator()) { if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; - if (!allocator.function) - { - allocator = mem::current_allocator(); - } - assert(allocator.function, "Expected an allocator to be present."); StringData* data = allocator.alloc(StringData.sizeof + capacity)!!; data.allocator = allocator; data.len = 0; @@ -144,29 +139,27 @@ fn void String.append_char32(String* str, Char32 c) data.chars[data.len++] = (char)(0x80 | (c & 0x3F)); } -fn String String.copy(String* str, Allocator allocator = { null, null }) +fn String String.copy(String* str, Allocator* allocator = null) { if (!str) { - if (allocator.function) return new_with_capacity(0, allocator); + if (allocator) return new_with_capacity(0, allocator); return (String)null; } - if (!allocator.function) allocator = mem::current_allocator(); + if (!allocator) allocator = mem::current_allocator(); StringData* data = str.data(); String new_string = new_with_capacity(data.capacity, allocator); mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len); return new_string; } -fn ZString String.copy_zstr(String* str, Allocator allocator = { null, null }) +fn ZString String.copy_zstr(String* str, Allocator* allocator = mem::current_allocator()) { usize str_len = str.len(); if (!str_len) { - if (allocator.function) return (ZString)allocator.calloc(1, 1)!!; - return (ZString)mem::calloc(1, 1); + return (ZString)allocator.calloc(1, 1)!!; } - if (!allocator.function) allocator = mem::current_allocator(); char* zstr = allocator.alloc(str_len + 1)!!; StringData* data = str.data(); mem::copy(zstr, &data.chars, str_len); @@ -174,7 +167,7 @@ fn ZString String.copy_zstr(String* str, Allocator allocator = { null, null }) return (ZString)zstr; } -fn char[] String.copy_str(String* str, Allocator allocator = { null, null }) +fn char[] String.copy_str(String* str, Allocator* allocator = mem::current_allocator()) { return str.copy_zstr(allocator)[:str.len()]; } @@ -236,7 +229,7 @@ fn void String.append_chars(String* this, char[] str) data.len += other_len; } -fn Char32[] String.copy_utf32(String* this, Allocator allocator = { null, null }) +fn Char32[] String.copy_utf32(String* this, Allocator* allocator = mem::current_allocator()) { return str::utf8to32(this.str(), allocator) @inline!!; } diff --git a/resources/examples/contextfree/boolerr.c3 b/resources/examples/contextfree/boolerr.c3 index afbbd9fd0..7dbea9a47 100644 --- a/resources/examples/contextfree/boolerr.c3 +++ b/resources/examples/contextfree/boolerr.c3 @@ -119,10 +119,9 @@ fn void main() const char[][] URLS = { "good", "title-empty", "title-missing", "head-missing", "fail" }; DynamicArenaAllocator dynamic_arena; dynamic_arena.init(1024); - Allocator allocator = mem::dynamic_arena_allocator(&dynamic_arena); foreach (char[] url : URLS) { - mem::@with_allocator(allocator) + mem::@with_allocator(&dynamic_arena) { io::printf(`Checking "https://%s/":` "\n", url); Summary summary = readAndBuildSummary(url); @@ -136,7 +135,7 @@ fn void main() // use printf here. io::printf(" Has title: %s vs %s\n", bool_to_string(has_title) ?? catch(has_title).nameof, has_title ?? false); }; - allocator.reset(); + dynamic_arena.reset(); } dynamic_arena.destroy(); } diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 65ef88dcf..e357e1d57 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -150,7 +150,7 @@ void decl_set_external_name(Decl *decl) return; } scratch_buffer_clear(); - module_append_name_to_scratch(decl->module); + module_append_name_to_scratch(decl->unit->module); scratch_buffer_append("_"); scratch_buffer_append(decl->name ? decl->name : "anon"); decl->extname = scratch_buffer_copy(); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 17346fedd..85fc6e5dd 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -306,6 +306,7 @@ typedef struct { Path *path; bool private; + Module *module; } ImportDecl; typedef struct @@ -349,7 +350,6 @@ typedef struct VarDecl_ Expr *init_expr; Decl *alias; }; - struct CompilationUnit_ *unit; union { int32_t index; @@ -607,7 +607,7 @@ typedef struct Decl_ uint32_t counter; }; uint32_t size;*/ - Module *module; + struct CompilationUnit_ *unit; Attr** attributes; Type *type; union diff --git a/src/compiler/context.c b/src/compiler/context.c index fac3cb48b..b362c58fe 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -122,7 +122,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; + if (!decl->unit || decl->unit->module == unit->module || !decl->extname) return; decl->is_external_visible = true; } @@ -166,8 +166,8 @@ void decl_register(Decl *decl) void unit_register_global_decl(CompilationUnit *unit, Decl *decl) { - assert(!decl->module || decl->module->is_generic); - decl->module = unit->module; + assert(!decl->unit || decl->unit->module->is_generic); + decl->unit = unit; switch (decl->decl_kind) { diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index a19347f30..9336e0d0b 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -664,7 +664,7 @@ void llvm_set_weak(GenContext *c, LLVMValueRef global) void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value) { - if (decl->module != c->code_module) + if (decl->unit->module != c->code_module) { llvm_set_linkonce(c, value); return; @@ -832,7 +832,7 @@ 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); + const char *name = decl->unit->module == c->code_module ? "tempglobal" : decl_get_extname(decl); decl->backend_ref = llvm_add_global_var(c, name, decl->type, decl->alignment); llvm_set_alignment(decl->backend_ref, decl->alignment); @@ -873,7 +873,7 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *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)) + if (decl->unit->module == c->code_module && !decl->is_external_visible && !visible_external(decl->visibility)) { llvm_set_internal_linkage(backend_ref); } diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index c19d116ae..3ac07fb3f 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -489,6 +489,15 @@ static LLVMMetadataRef llvm_debug_vector_type(GenContext *c, Type *type) static LLVMMetadataRef llvm_debug_func_type(GenContext *c, Type *type) { FunctionPrototype *prototype = type->func.prototype; + // 1. Generate all the parameter types, this may cause this function to be called again! + VECEACH(prototype->params, i) + { + llvm_get_debug_type(c, prototype->params[i]); + } + // 2. We might be done! + if (type->backend_debug_type) return type->backend_debug_type; + + // 3. Otherwise generate: static LLVMMetadataRef *buffer = NULL; vec_resize(buffer, 0); vec_add(buffer, llvm_get_debug_type(c, prototype->rtype)); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 0c10112a5..3bb5cc95b 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -422,7 +422,7 @@ void llvm_emit_function_body(GenContext *c, Decl *decl) if (c->debug.enable_stacktrace) { scratch_buffer_clear(); - scratch_buffer_append(decl->module->name->module); + scratch_buffer_append(decl->unit->module->name->module); scratch_buffer_append("::"); scratch_buffer_append(decl->name ? decl->name : "anon"); c->debug.func_name = llvm_emit_zstring(c, scratch_buffer_to_string()); diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 13dfc9807..6ad6c5e0a 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -475,7 +475,7 @@ static inline Module *type_base_module(Type *type) case TYPE_BITSTRUCT: case TYPE_FAULTTYPE: case TYPE_DISTINCT: - return type->decl->module; + return type->decl->unit->module; case TYPE_TYPEDEF: type = type->canonical; goto RETRY; @@ -565,7 +565,7 @@ static LLVMValueRef llvm_get_introspection_for_builtin_type(GenContext *c, Type static LLVMValueRef llvm_get_introspection_for_enum(GenContext *c, Type *type) { Decl *decl = type->decl; - bool is_external = decl->module != c->code_module; + bool is_external = decl->unit->module != c->code_module; bool is_dynamic = decl->is_dynamic; Decl **enum_vals = decl->enums.values; diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index e89fcc09d..dd9855219 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -71,8 +71,8 @@ static inline bool sema_check_no_duplicate_parameter(Decl **decls, Decl *current static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent, Decl *decl) { - assert(!decl->module || decl->module->is_generic); - decl->module = parent->module; + assert(!decl->unit || decl->unit->module->is_generic); + decl->unit = parent->unit; if (decl->name) { Decl *other = sema_resolve_symbol_in_current_dynamic_scope(context, decl->name); @@ -533,6 +533,7 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl) static inline bool sema_analyse_function_param(SemaContext *context, Decl *param, bool is_function, bool *has_default) { *has_default = false; + param->unit = context->unit; assert(param->decl_kind == DECL_VAR); // We need to check that the parameters are not typeless nor are of any macro parameter kind. if (param->var.kind != VARDECL_PARAM) @@ -567,10 +568,6 @@ static inline bool sema_analyse_function_param(SemaContext *context, Decl *param { if (!sema_analyse_expr_rhs(context, param->type, expr, true)) return false; } - else - { - param->var.unit = context->unit; - } *has_default = true; } param->alignment = type_abi_alignment(param->type); @@ -1003,7 +1000,7 @@ Decl *sema_find_operator(SemaContext *context, Expr *expr, OperatorOverload oper Decl **imports = context->unit->imports; VECEACH(imports, i) { - extension = operator_in_module(context, imports[i]->module, operator_overload); + extension = operator_in_module(context, imports[i]->import.module, operator_overload); if (extension) return extension; } return NULL; @@ -1106,7 +1103,7 @@ static inline bool unit_add_method_like(CompilationUnit *unit, Type *parent_type method_like->extname = scratch_buffer_copy(); } DEBUG_LOG("Method-like '%s.%s' analysed.", parent->name, method_like->name); - if (parent->module == unit->module) + if (parent->unit->module == unit->module) { vec_add(parent->methods, method_like); } @@ -1669,7 +1666,7 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) } Decl *function = decl_new(DECL_FUNC, NULL, decl->span, VISIBLE_EXTERN); function->name = kw_mainstub; - function->module = decl->module; + function->unit = decl->unit; function->extname = kw_main; function->has_extname = true; function->func_decl.function_signature.returntype = type_infoid(type_info_new_base(type_cint, decl->span)); @@ -1863,6 +1860,7 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl) for (unsigned i = 0; i < param_count; i++) { Decl *param = parameters[i]; + param->unit = context->unit; param->resolve_status = RESOLVE_RUNNING; assert(param->decl_kind == DECL_VAR); switch (param->var.kind) @@ -2284,7 +2282,7 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) return decl_poison(decl); } - Module *module = alias->module; + Module *module = alias->unit->module; TypeInfo **params = decl->define_decl.generic_params; unsigned parameter_count = vec_size(module->parameters); assert(parameter_count > 0); @@ -2400,58 +2398,58 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) { if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl); + SemaContext temp_context; + context = transform_context_for_eval(context, &temp_context, decl->unit); DEBUG_LOG(">>> Analysing %s.", decl->name ? decl->name : "anon"); if (decl->resolve_status == RESOLVE_RUNNING) { SEMA_ERROR(decl, "Recursive definition of '%s'.", decl->name ? decl->name : "anon"); - decl_poison(decl); - return false; + goto FAILED; } - decl->resolve_status = RESOLVE_RUNNING; - assert(decl->module); + assert(decl->unit); switch (decl->decl_kind) { case DECL_BITSTRUCT: - if (!sema_analyse_bitstruct(context, decl)) return decl_poison(decl); + if (!sema_analyse_bitstruct(context, decl)) goto FAILED; decl_set_external_name(decl); break; case DECL_STRUCT: case DECL_UNION: - if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl); + if (!sema_analyse_struct_union(context, decl)) goto FAILED; decl_set_external_name(decl); break; case DECL_FUNC: - if (!sema_analyse_func(context, decl)) return decl_poison(decl); + if (!sema_analyse_func(context, decl)) goto FAILED; break; case DECL_MACRO: case DECL_GENERIC: - if (!sema_analyse_macro(context, decl)) return decl_poison(decl); + if (!sema_analyse_macro(context, decl)) goto FAILED; break; case DECL_VAR: - if (!sema_analyse_var_decl(context, decl, false)) return decl_poison(decl); + if (!sema_analyse_var_decl(context, decl, false)) goto FAILED; decl_set_external_name(decl); break; case DECL_ATTRIBUTE: - if (!sema_analyse_attribute_decl(context, decl)) return decl_poison(decl); + if (!sema_analyse_attribute_decl(context, decl)) goto FAILED; break; case DECL_DISTINCT: - if (!sema_analyse_distinct(context, decl)) return decl_poison(decl); + if (!sema_analyse_distinct(context, decl)) goto FAILED; decl_set_external_name(decl); break; case DECL_TYPEDEF: - if (!sema_analyse_typedef(context, decl)) return decl_poison(decl); + if (!sema_analyse_typedef(context, decl)) goto FAILED; break; case DECL_ENUM: - if (!sema_analyse_enum(context, decl)) return decl_poison(decl); + if (!sema_analyse_enum(context, decl)) goto FAILED; decl_set_external_name(decl); break; case DECL_FAULT: - if (!sema_analyse_error(context, decl)) return decl_poison(decl); + if (!sema_analyse_error(context, decl)) goto FAILED; decl_set_external_name(decl); break; case DECL_DEFINE: - if (!sema_analyse_define(context, decl)) return decl_poison(decl); + if (!sema_analyse_define(context, decl)) goto FAILED; break; case DECL_POISONED: case DECL_IMPORT: @@ -2469,5 +2467,9 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) UNREACHABLE } decl->resolve_status = RESOLVE_DONE; + sema_context_destroy(&temp_context); return true; +FAILED: + sema_context_destroy(&temp_context); + return decl_poison(decl); } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 91f88ba2f..4fd32c53e 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -953,7 +953,7 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, 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) + if (decl->unit->module != context->unit->module && !decl->is_autoimport && !expr->identifier_expr.path) { const char *message; switch (decl->decl_kind) @@ -1385,14 +1385,15 @@ static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl * Expr *arg = actual_args[i] = expr_macro_copy(init_expr); if (arg->resolve_status != RESOLVE_DONE) { + SemaContext default_context; Type *rtype = NULL; - sema_context_init(&default_context, param->var.unit); - default_context.compilation_unit = context->unit; - default_context.current_function = context->current_function; - context_change_scope_with_flags(&default_context, SCOPE_NONE); - default_context.original_inline_line = context->original_inline_line ? context->original_inline_line : init_expr->span.row; - bool success = sema_analyse_expr_rhs(&default_context, param->type, arg, true); + SemaContext *new_context = transform_context_for_eval(context, &default_context, param->unit); + bool success; + SCOPE_START + new_context->original_inline_line = context->original_inline_line ? context->original_inline_line : init_expr->span.row; + success = sema_analyse_expr_rhs(new_context, param->type, arg, true); + SCOPE_END; sema_context_destroy(&default_context); if (!success) return false; } @@ -2115,7 +2116,7 @@ static inline bool sema_expr_analyse_generic_call(SemaContext *context, Expr *ca scratch_buffer_append(arg->type->canonical->name); } const char *mangled_name = scratch_buffer_interned(); - Decl **generic_cache = decl->module->generic_cache; + Decl **generic_cache = decl->unit->module->generic_cache; Decl *found = NULL; VECEACH(generic_cache, i) { @@ -3774,7 +3775,7 @@ CHECK_DEEPER: if (ambiguous) { SEMA_ERROR(expr, "'%s' is an ambiguous name and so cannot be resolved, it may refer to method defined in '%s' or one in '%s'", - kw, member->module->name->module, ambiguous->module->name->module); + kw, member->unit->module->name->module, ambiguous->unit->module->name->module); return false; } } @@ -7000,13 +7001,13 @@ static inline bool sema_expr_analyse_ct_nameof(SemaContext *context, Expr *expr) expr_rewrite_to_string(expr, decl->extname); return true; } - if (!decl->module || name_type == TOKEN_CT_NAMEOF || decl_is_local(decl)) + if (!decl->unit || name_type == TOKEN_CT_NAMEOF || decl_is_local(decl)) { expr_rewrite_to_string(expr, decl->name); return true; } scratch_buffer_clear(); - scratch_buffer_append(decl->module->name->module); + scratch_buffer_append(decl->unit->module->name->module); scratch_buffer_append("::"); scratch_buffer_append(decl->name); expr_rewrite_to_string(expr, scratch_buffer_copy()); @@ -7032,7 +7033,7 @@ static inline bool sema_expr_analyse_ct_nameof(SemaContext *context, Expr *expr) return true; } scratch_buffer_clear(); - scratch_buffer_append(type->decl->module->name->module); + scratch_buffer_append(type->decl->unit->module->name->module); scratch_buffer_append("::"); scratch_buffer_append(type->name); expr_rewrite_to_string(expr, scratch_buffer_copy()); diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 4f93a1e4b..4ccf4c2d8 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -80,6 +80,7 @@ void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, uint64_t value void expr_rewrite_to_string(Expr *expr_to_rewrite, const char *string); const char *ct_eval_expr(SemaContext *c, const char *expr_type, Expr *inner, TokenType *type, Path **path_ref, bool report_missing); extern const char *ct_eval_error; +SemaContext *transform_context_for_eval(SemaContext *context, SemaContext *temp_context, CompilationUnit *eval_unit); static inline bool expr_is_const(Expr *expr); diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 0424e3919..1f8aba91a 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -59,10 +59,10 @@ static Decl *sema_find_decl_in_imports(Decl **imports, NameResolve *name_resolve VECEACH(imports, i) { Decl *import = imports[i]; - if (import->module->is_generic != want_generic) continue; + if (import->import.module->is_generic != want_generic) continue; // Is the decl in the import. - Decl *found = sema_find_decl_in_module(import->module, path, symbol, &name_resolve->path_found); + Decl *found = sema_find_decl_in_module(import->import.module, path, symbol, &name_resolve->path_found); // No match, so continue if (!found) continue; @@ -133,7 +133,7 @@ static Decl *sema_find_decl_in_global(DeclTable *table, Module **module_list, Na // There might just be a single match. if (decls->decl_kind != DECL_DECLARRAY) { - if (path && !matches_subpath(decls->module->name, path)) return false; + if (path && !matches_subpath(decls->unit->module->name, path)) return false; name_resolve->private_decl = NULL; return decls; } @@ -145,7 +145,7 @@ static Decl *sema_find_decl_in_global(DeclTable *table, Module **module_list, Na VECEACH(decl_list, i) { Decl *candidate = decl_list[i]; - if (!ambiguous && (!path || matches_subpath(candidate->module->name, path))) + if (!ambiguous && (!path || matches_subpath(candidate->unit->module->name, path))) { ambiguous = decl; decl = candidate; @@ -158,7 +158,7 @@ static Decl *sema_find_decl_in_global(DeclTable *table, Module **module_list, Na static bool decl_is_visible(CompilationUnit *unit, Decl *decl) { - Module *module = decl->module; + Module *module = decl->unit->module; // 1. Same module as unit -> ok if (module == unit->module) return true; Module *top = module->top_module; @@ -176,7 +176,7 @@ static bool decl_is_visible(CompilationUnit *unit, Decl *decl) VECEACH(unit->imports, i) { Decl *import = unit->imports[i]; - Module *import_module = import->module; + Module *import_module = import->import.module; // 4. Same as import if (import_module == module) return true; // 5. If import and decl doesn't share a top module -> no match @@ -210,7 +210,7 @@ static Decl *sema_find_decl_in_global_new(CompilationUnit *unit, DeclTable *tabl // There might just be a single match. if (decls->decl_kind != DECL_DECLARRAY) { - if (path && !matches_subpath(decls->module->name, path)) return NULL; + if (path && !matches_subpath(decls->unit->module->name, path)) return NULL; if (!decl_is_visible(unit, decls)) { name_resolve->maybe_decl = decls; @@ -227,7 +227,7 @@ static Decl *sema_find_decl_in_global_new(CompilationUnit *unit, DeclTable *tabl VECEACH(decl_list, i) { Decl *candidate = decl_list[i]; - if (path && !matches_subpath(candidate->module->name, path)) continue; + if (path && !matches_subpath(candidate->unit->module->name, path)) continue; if (!decl_is_visible(unit, candidate)) { maybe_decl = candidate; @@ -358,7 +358,7 @@ static void sema_report_error_on_decl(Decl *found, NameResolve *name_resolve) if (!found && name_resolve->maybe_decl) { const char *maybe_name = decl_to_name(name_resolve->maybe_decl); - const char *module_name = name_resolve->maybe_decl->module->name->module; + const char *module_name = name_resolve->maybe_decl->unit->module->name->module; if (path_name) { sema_error_at(span, "Did you mean the %s '%s::%s' in module %s? If so please add 'import %s'.", @@ -377,8 +377,8 @@ static void sema_report_error_on_decl(Decl *found, NameResolve *name_resolve) { assert(found); const char *symbol_type = decl_to_name(found); - const char *found_path = found->module->name->module; - const char *other_path = name_resolve->ambiguous_other_decl->module->name->module; + const char *found_path = found->unit->module->name->module; + const char *other_path = name_resolve->ambiguous_other_decl->unit->module->name->module; if (path_name) { sema_error_at(span, @@ -414,6 +414,10 @@ INLINE Decl *sema_resolve_symbol_common(SemaContext *context, NameResolve *name_ name_resolve->private_decl = NULL; name_resolve->path_found = false; Decl *decl; + if (strcmp(name_resolve->symbol, "_NULL_ALLOCATOR") == 0) + { + decl = NULL; + } if (name_resolve->path) { decl = sema_resolve_path_symbol(context, name_resolve); @@ -523,9 +527,9 @@ Decl *sema_resolve_method(CompilationUnit *unit, Decl *type, const char *method_ VECEACH(unit->imports, i) { Decl *import = unit->imports[i]; - if (import->module->is_generic) continue; + if (import->import.module->is_generic) continue; - Decl *new_found = sema_resolve_method_in_module(import->module, actual_type, method_name, + Decl *new_found = sema_resolve_method_in_module(import->import.module, actual_type, method_name, &private, &ambiguous, import->import.private ? METHOD_SEARCH_PRIVATE_IMPORTED @@ -597,7 +601,7 @@ bool sema_symbol_is_defined_in_scope(SemaContext *c, const char *symbol) // Unknown symbol => not defined if (!decl) return false; // Defined in the same module => defined - if (decl->module == c->unit->module) return true; + if (decl->unit->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. @@ -657,14 +661,15 @@ void sema_add_member(SemaContext *context, Decl *decl) bool sema_add_local(SemaContext *context, Decl *decl) { - Module *current_module = decl->module = context->unit->module; + CompilationUnit *current_unit = decl->unit = context->unit; + // Ignore synthetic locals. if (!decl->name) return true; if (decl->decl_kind == DECL_VAR && decl->var.shadow) goto ADD_VAR; Decl *other = sema_find_local(context, decl->name); - assert(!other || other->module); - if (other && (other->module == current_module || other->is_autoimport)) + assert(!other || other->unit->module); + if (other && (other->unit->module == current_unit->module || other->is_autoimport)) { sema_shadow_error(decl, other); decl_poison(decl); diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index a9501a28a..9e6452ba4 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -110,7 +110,7 @@ void sema_analysis_pass_process_imports(Module *module) // 8. Assign the module. DEBUG_LOG("* Import of %s.", path->module); - import->module = import_module; + import->import.module = import_module; } import_count += imports; } diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 70fe7d233..86d62d760 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -171,12 +171,12 @@ void sema_analyze_stage(Module *module, AnalysisStage stage) } } -static void register_generic_decls(Module *module, Decl **decls) +static void register_generic_decls(CompilationUnit *unit, Decl **decls) { VECEACH(decls, i) { Decl *decl = decls[i]; - decl->module = module; + decl->unit = unit; switch (decl->decl_kind) { case DECL_POISONED: @@ -219,7 +219,7 @@ static void register_generic_decls(Module *module, Decl **decls) case DECL_BODYPARAM: break; } - htable_set(&module->symbols, decl->name, decl); + htable_set(&unit->module->symbols, decl->name, decl); if (decl->visibility == VISIBLE_PUBLIC) global_context_add_generic_decl(decl); } @@ -231,7 +231,8 @@ static void analyze_generic_module(Module *module) assert(module->parameters && module->is_generic); VECEACH(module->units, index) { - register_generic_decls(module, module->units[index]->global_decls); + CompilationUnit *unit = module->units[index]; + register_generic_decls(unit, unit->global_decls); } sema_analyze_stage(module, ANALYSIS_MODULE_HIERARCHY); } @@ -346,6 +347,7 @@ void sema_context_init(SemaContext *context, CompilationUnit *unit) void sema_context_destroy(SemaContext *context) { + if (!context->unit) return; generic_context_release_locals_list(context->locals); } @@ -364,3 +366,18 @@ void generic_context_release_locals_list(Decl **list) { vec_add(global_context.locals_list, list); } + +SemaContext *transform_context_for_eval(SemaContext *context, SemaContext *temp_context, CompilationUnit *eval_unit) +{ + if (eval_unit == context->unit) + { + temp_context->unit = NULL; + return context; + } + DEBUG_LOG("Changing compilation unit to %s", eval_unit->file->name); + sema_context_init(temp_context, eval_unit); + temp_context->compilation_unit = context->compilation_unit; + temp_context->current_function = context->current_function; + temp_context->current_macro = context->current_macro; + return temp_context; +} diff --git a/test/test_suite/attributes/attribute_visibility.c3t b/test/test_suite/attributes/attribute_visibility.c3t new file mode 100644 index 000000000..adfa7b9b9 --- /dev/null +++ b/test/test_suite/attributes/attribute_visibility.c3t @@ -0,0 +1,12 @@ +// #target: macos-x64 +module test; +private const int FOO = 4; +define @Align(x) = @align(x * FOO); + +module test2; +import test; +int black @Align(16) = 123; + +// #expect: test2.ll + +@test2_black = local_unnamed_addr global i32 123, align 64 \ No newline at end of file diff --git a/test/test_suite2/attributes/attribute_visibility.c3t b/test/test_suite2/attributes/attribute_visibility.c3t new file mode 100644 index 000000000..adfa7b9b9 --- /dev/null +++ b/test/test_suite2/attributes/attribute_visibility.c3t @@ -0,0 +1,12 @@ +// #target: macos-x64 +module test; +private const int FOO = 4; +define @Align(x) = @align(x * FOO); + +module test2; +import test; +int black @Align(16) = 123; + +// #expect: test2.ll + +@test2_black = local_unnamed_addr global i32 123, align 64 \ No newline at end of file