diff --git a/lib/std/collections/map.c3 b/lib/std/collections/map.c3 index f05399855..c66221a9f 100644 --- a/lib/std/collections/map.c3 +++ b/lib/std/collections/map.c3 @@ -8,6 +8,7 @@ const uint DEFAULT_INITIAL_CAPACITY = 16; const uint MAXIMUM_CAPACITY = 1u << 31; const float DEFAULT_LOAD_FACTOR = 0.75; const VALUE_IS_EQUATABLE = types::is_equatable(Value); +const bool COPY_KEYS = types::implements_copy(Key); struct HashMap { @@ -164,7 +165,14 @@ fn void HashMap.clear(&map) { Entry* entry = *entry_ref; if (!entry) continue; - map.free_internal(entry); + Entry *next = entry.next; + while (next) + { + Entry *to_delete = next; + next = next.next; + map.free_entry(to_delete); + } + map.free_entry(entry); *entry_ref = null; } map.count = 0; @@ -255,6 +263,9 @@ fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE) fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private { Entry* entry = malloc(Entry, .using = map.allocator); + $if COPY_KEYS: + key = key.copy(map.allocator); + $endif *entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] }; map.table[bucket_index] = entry; if (map.count++ >= map.threshold) @@ -359,7 +370,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private { prev.next = next; } - map.free_internal(e); + map.free_entry(e); return true; } prev = e; @@ -372,15 +383,26 @@ fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_i { Entry *e = map.table[bucket_index]; Entry* entry = malloc(Entry, .using = map.allocator); + $if COPY_KEYS: + key = key.copy(map.allocator); + $endif *entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] }; map.table[bucket_index] = entry; map.count++; } +fn void HashMap.free_entry(&self, Entry *entry) @local +{ + $if COPY_KEYS: + entry.key.free(self.allocator); + $endif + self.free_internal(entry); +} + struct Entry { uint hash; Key key; Value value; Entry* next; -} +} \ No newline at end of file diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index 3c45695bb..9079b8c5a 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -177,19 +177,14 @@ macro bool equals(a, b, isz len = -1, usz $align = 0) return true; } -macro @clone(&value) @builtin +macro @clone(&value, Allocator *using = mem::heap()) @builtin { - $typeof(value)* x = malloc($typeof(value)); + $typeof(value)* x = malloc($typeof(value), .using = using); *x = value; return x; } -macro @tclone(&value) @builtin -{ - $typeof(value)* x = talloc($typeof(value)); - *x = value; - return x; -} +macro @tclone(&value) @builtin => @clone(value, mem::temp()); macro type_alloc_must_be_aligned($Type) { diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index bc19d5bb4..6ca1a07c5 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -346,6 +346,12 @@ fn String String.copy(s, Allocator* using = mem::heap()) str[len] = 0; return (String)str[:len]; } +fn void String.free(&s, Allocator* using = mem::heap()) +{ + if (!s.len) return; + mem::free(s.ptr, .using = using); + *s = ""; +} fn String String.tcopy(s) => s.copy(mem::temp()) @inline; diff --git a/lib/std/core/types.c3 b/lib/std/core/types.c3 index 6dd019f59..37368fccd 100644 --- a/lib/std/core/types.c3 +++ b/lib/std/core/types.c3 @@ -229,6 +229,14 @@ macro bool is_equatable_type($Type) $endif } +/** + * Checks if a type implements the copy protocol. + **/ +macro bool implements_copy($Type) +{ + return $checks(Allocator *a, $Type x, $Type y = x.copy(a), $Type z = x.copy(), z.free(a), z.free()); +} + macro bool is_equatable_value(value) { return is_equatable_type($typeof(value)); diff --git a/releasenotes.md b/releasenotes.md index 10c0bbcb7..0db05449c 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -107,6 +107,7 @@ - `assert` may now take varargs for formatting. ### Stdlib changes +- Hashmap keys implicitly copied if copy/free are defined. - Socket handling. - `csv` package. - Updated posix/win32 stdlib namespacing diff --git a/src/version.h b/src/version.h index ae6dfc6a5..94e7eb4f3 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.603" \ No newline at end of file +#define COMPILER_VERSION "0.4.604" \ No newline at end of file diff --git a/test/test_suite/errors/error_regression_2.c3t b/test/test_suite/errors/error_regression_2.c3t index 940bf396c..9646919f8 100644 --- a/test/test_suite/errors/error_regression_2.c3t +++ b/test/test_suite/errors/error_regression_2.c3t @@ -405,7 +405,7 @@ panic_block: ; preds = %assign_optional %29 = insertvalue %"any[]" undef, ptr %varargslots, 0 %"#temp#" = insertvalue %"any[]" %29, i64 1, 1 store %"any[]" %"#temp#", ptr %indirectarg, align 8 - call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 7, i32 204, ptr byval(%"any[]") align 8 %indirectarg) + call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 7 unreachable noerr_block: ; preds = %after_check @@ -478,7 +478,7 @@ panic_block39: ; preds = %assign_optional37 %53 = insertvalue %"any[]" undef, ptr %varargslots40, 0 %"#temp#41" = insertvalue %"any[]" %53, i64 1, 1 store %"any[]" %"#temp#41", ptr %indirectarg42, align 8 - call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 7, i32 204, ptr byval(%"any[]") align 8 %indirectarg42) + call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func unreachable noerr_block43: ; preds = %after_check38 @@ -535,7 +535,7 @@ panic_block61: ; preds = %assign_optional59 %71 = insertvalue %"any[]" undef, ptr %varargslots62, 0 %"#temp#63" = insertvalue %"any[]" %71, i64 1, 1 store %"any[]" %"#temp#63", ptr %indirectarg64, align 8 - call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 7, i32 204, ptr byval(%"any[]") align 8 %indirectarg64) + call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func unreachable noerr_block65: ; preds = %after_check60 @@ -606,7 +606,7 @@ panic_block81: ; preds = %assign_optional79 %95 = insertvalue %"any[]" undef, ptr %varargslots82, 0 %"#temp#83" = insertvalue %"any[]" %95, i64 1, 1 store %"any[]" %"#temp#83", ptr %indirectarg84, align 8 - call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 7, i32 204, ptr byval(%"any[]") align 8 %indirectarg84) + call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func unreachable noerr_block85: ; preds = %after_check80 @@ -673,7 +673,7 @@ panic_block111: ; preds = %assign_optional109 %124 = insertvalue %"any[]" undef, ptr %varargslots112, 0 %"#temp#113" = insertvalue %"any[]" %124, i64 1, 1 store %"any[]" %"#temp#113", ptr %indirectarg114, align 8 - call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 7, i32 204, ptr byval(%"any[]") align 8 %indirectarg114) + call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func unreachable noerr_block115: ; preds = %after_check110 @@ -730,7 +730,7 @@ panic_block133: ; preds = %assign_optional131 %142 = insertvalue %"any[]" undef, ptr %varargslots134, 0 %"#temp#135" = insertvalue %"any[]" %142, i64 1, 1 store %"any[]" %"#temp#135", ptr %indirectarg136, align 8 - call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 7, i32 204, ptr byval(%"any[]") align 8 %indirectarg136) + call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 7 unreachable noerr_block137: ; preds = %after_check132 diff --git a/test/unit/stdlib/collections/copy_map.c3 b/test/unit/stdlib/collections/copy_map.c3 new file mode 100644 index 000000000..fe7c36873 --- /dev/null +++ b/test/unit/stdlib/collections/copy_map.c3 @@ -0,0 +1,30 @@ +module test; +import std::io; +import std::collections::map; + +fn void! copy_map() @test +{ + TrackingAllocator alloc; + alloc.init(mem::heap()); + io::printfn("Heap mem: %d", alloc.allocated()); + mem::@scoped(&alloc) + { + HashMap() x; + x.init(); + DString y; + y.append("hello"); + x.set(y.as_str(), 123); + y.append("bye"); + x.set(y.as_str(), 333); + y.clear(); + y.append("bye"); + x.set(y.as_str(), 444); + assert(x.get("hello")! == 123); + assert(x.get("hellobye")! == 333); + assert(x.get("bye")! == 444); + assert(alloc.allocated() > 0); + x.free(); + y.free(); + assert(alloc.allocated() == 0); + }; +} \ No newline at end of file