From 300f4d38ab5f27456dc0ef274f47f830f09fcb7a Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 6 Mar 2023 23:23:35 +0100 Subject: [PATCH] Addition of object type. Fixes to const union initialization. --- lib/std/collections/list.c3 | 8 + lib/std/collections/object.c3 | 414 +++++++++++++++++++++++++ src/compiler/llvm_codegen.c | 21 +- src/version.h | 2 +- test/unit/stdlib/collections/object.c3 | 23 ++ 5 files changed, 463 insertions(+), 5 deletions(-) create mode 100644 lib/std/collections/object.c3 create mode 100644 test/unit/stdlib/collections/object.c3 diff --git a/lib/std/collections/list.c3 b/lib/std/collections/list.c3 index 5e1852a28..efb6426ae 100644 --- a/lib/std/collections/list.c3 +++ b/lib/std/collections/list.c3 @@ -95,6 +95,14 @@ fn void List.insert_at(List* list, usz index, Type type) list.entries[index] = type; } +/** + * @require index < list.size + **/ +fn void List.set_at(List* list, usz index, Type type) +{ + list.entries[index] = type; +} + fn void List.remove_last(List* list) { list.size--; diff --git a/lib/std/collections/object.c3 b/lib/std/collections/object.c3 new file mode 100644 index 000000000..0c151eae4 --- /dev/null +++ b/lib/std/collections/object.c3 @@ -0,0 +1,414 @@ +module std::collections::object; +import std::collections::map; +import std::collections::list; + +const Object TRUE_OBJECT = { .b = true, .type = bool.typeid }; +const Object FALSE_OBJECT = { .b = false, .type = bool.typeid }; +const Object NULL_OBJECT = { .type = void*.typeid }; + +struct Object +{ + typeid type; + Allocator* allocator; + union + { + uint128 i; + double f; + bool b; + String s; + void* other; + ObjectInternalList array; + ObjectInternalMap map; + } +} + +fn Object* new_obj(Allocator* allocator = mem::heap()) +{ + Object* o = malloc(Object, .using = allocator); + *o = { .allocator = allocator, .type = void.typeid }; + return o; +} + +fn Object* new_null() +{ + return &NULL_OBJECT; +} + +fn Object* new_int(int128 i, Allocator* allocator = mem::heap()) +{ + Object* o = malloc(Object, .using = allocator); + *o = { .i = i, .allocator = allocator, .type = int128.typeid }; + return o; +} + +macro Object* new_enum(e, Allocator* allocator = mem::heap()) +{ + Object* o = malloc(Object, .using = allocator); + *o = { .i = (int128)e, .allocator = allocator, .type = $typeof(e).typeid }; + return o; +} + +fn Object* new_float(double f, Allocator* allocator = mem::current_allocator()) +{ + Object* o = malloc(Object, .using = allocator); + *o = { .f = f, .allocator = allocator, .type = double.typeid }; + return o; +} + +fn Object* new_string(String s, Allocator* allocator = mem::heap()) +{ + Object* o = malloc(Object, .using = allocator); + *o = { .s = s.copyz(allocator), .allocator = allocator, .type = String.typeid }; + return o; +} + + +fn Object* new_bool(bool b) +{ + return b ? &TRUE_OBJECT : &FALSE_OBJECT; +} + +/** + * @param [&inout] o + **/ +fn void Object.free(Object* o) +{ + switch (o.type) + { + case void: + break; + case String: + free(o.s, .using = o.allocator); + case ObjectInternalList: + foreach (ol : o.array) + { + ol.free(); + } + o.array.free(); + case ObjectInternalMap: + @pool() + { + foreach (key : o.map.key_tlist()) + { + o.map.get(key).free(); + free(key, .using = o.allocator); + } + o.map.free(); + }; + default: + break; + } + if (o.allocator) free(o, .using = o.allocator); +} + +fn bool Object.is_null(Object* this) @inline => this == &NULL_OBJECT; +fn bool Object.is_empty(Object* this) @inline => this.type == void.typeid; +fn bool Object.is_map(Object* this) @inline => this.type == ObjectInternalMap.typeid; +fn bool Object.is_array(Object* this) @inline => this.type == ObjectInternalList.typeid; +fn bool Object.is_bool(Object* this) @inline => this.type == bool.typeid; +fn bool Object.is_string(Object* this) @inline => this.type == String.typeid; +fn bool Object.is_float(Object* this) @inline => this.type == double.typeid; +fn bool Object.is_int(Object* this) @inline => this.type == int128.typeid; +fn bool Object.is_keyable(Object* this) => this.is_empty() || this.is_map(); +fn bool Object.is_indexable(Object* this) => this.is_empty() || this.is_array(); + +/** + * @require o.is_keyable() + **/ +fn void Object.init_map_if_needed(Object* o) @private +{ + if (o.is_empty()) + { + o.type = ObjectInternalMap.typeid; + o.map.init(.allocator = o.allocator); + } +} + +/** + * @require o.is_indexable() + **/ +fn void Object.init_array_if_needed(Object* o) @private +{ + if (o.is_empty()) + { + o.type = ObjectInternalList.typeid; + o.array.init(.allocator = o.allocator); + } +} + +/** + * @require o.is_keyable() + **/ +fn void Object.set_object(Object* o, String key, Object* new_object) @private +{ + o.init_map_if_needed(); + ObjectInternalMapEntry*! entry = o.map.get_entry(key); + defer + { + (void)free(entry.key, .using = o.allocator); + entry.value.free(); + } + o.map.set(str::copyz(key, o.map.allocator), new_object); +} + +macro Object* object_from_value(value) @private +{ + var $Type = $typeof(value); + $if (types::is_int($Type)): + return new_int(value); + $elif (types::is_float($Type)): + return new_float(value); + $elif ($Type.typeid == String.typeid): + return new_string(value); + $elif ($Type.typeid == bool.typeid): + return new_bool(value); + $elif ($Type.typeid == Object*.typeid): + return value; + $elif ($Type.typeid == void*.typeid): + assert(value == null); + return &NULL_OBJECT; + $elif ($checks(String s = value)): + return new_string(value); + $else: + $assert(false, "Unsupported object type."); + $endif; +} + +macro Object* Object.set(Object* o, String key, value) +{ + Object* val = object_from_value(value); + o.set_object(key, val); + return val; +} + +/** + * @require o.is_indexable() + **/ +macro Object* Object.set_at(Object* o, usz index, String key, value) +{ + Object* val = object_from_value(value); + o.set_object_at(key, index, val); + return val; +} + +/** + * @require o.is_indexable() + * @ensure return != null + **/ +macro Object* Object.append(Object* o, value) +{ + Object* val = object_from_value(value); + o.append_object(val); + return val; +} + +/** + * @require o.is_keyable() + **/ +fn Object*! Object.get(Object* o, String key) => o.is_empty() ? SearchResult.MISSING! : o.map.get(key); + +/** + * @require o.is_indexable() + **/ +fn Object* Object.get_at(Object* o, usz index) +{ + return o.array.get(index); +} + +/** + * @require o.is_indexable() + **/ +fn void Object.append_object(Object* o, Object* to_append) +{ + o.init_array_if_needed(); + o.array.append(to_append); +} + +/** + * @require o.is_indexable() + **/ +fn void Object.set_object_at(Object* o, usz index, Object* to_set) +{ + o.init_array_if_needed(); + while (o.array.len() < index) + { + o.array.append(&NULL_OBJECT); + } + if (o.array.len() == index) + { + o.array.append(to_set); + return; + } + o.array.get(index).free(); + o.array.set_at(index, to_set); +} + +macro get_integer_value(Object* value, $Type) +{ + if (value.is_float()) + { + return ($Type)value.f; + } + if (value.is_string()) + { + $if ($Type.kindof == TypeKind.SIGNED_INT): + return ($Type)str::to_int128(value.s); + $else: + return ($Type)str::to_uint128(value.s); + $endif; + } + if (!value.is_int()) return NumberConversion.MALFORMED_INTEGER!; + return ($Type)value.i; +} + +/** + * @require o.is_indexable() + **/ +macro Object.get_integer_at(Object* o, $Type, usz index) @private +{ + return get_integer_value(o.get_at(index), $Type); +} + +/** + * @require o.is_keyable() + **/ +macro Object.get_integer(Object* o, $Type, String key) @private +{ + return get_integer_value(o.get(key), $Type); +} + +fn ichar! Object.get_ichar(Object* o, String key) => o.get_integer(ichar, key); +fn short! Object.get_short(Object* o, String key) => o.get_integer(short, key); +fn int! Object.get_int(Object* o, String key) => o.get_integer(int, key); +fn long! Object.get_long(Object* o, String key) => o.get_integer(long, key); +fn int128! Object.get_int128(Object* o, String key) => o.get_integer(int128, key); + +fn ichar! Object.get_ichar_at(Object* o, usz index) => o.get_integer_at(ichar, index); +fn short! Object.get_short_at(Object* o, usz index) => o.get_integer_at(short, index); +fn int! Object.get_int_at(Object* o, usz index) => o.get_integer_at(int, index); +fn long! Object.get_long_at(Object* o, usz index) => o.get_integer_at(long, index); +fn int128! Object.get_int128_at(Object* o, usz index) => o.get_integer_at(int128, index); + +fn char! Object.get_char(Object* o, String key) => o.get_integer(ichar, key); +fn short! Object.get_ushort(Object* o, String key) => o.get_integer(ushort, key); +fn uint! Object.get_uint(Object* o, String key) => o.get_integer(uint, key); +fn ulong! Object.get_ulong(Object* o, String key) => o.get_integer(ulong, key); +fn uint128! Object.get_uint128(Object* o, String key) => o.get_integer(uint128, key); + +fn char! Object.get_char_at(Object* o, usz index) => o.get_integer_at(char, index); +fn ushort! Object.get_ushort_at(Object* o, usz index) => o.get_integer_at(ushort, index); +fn uint! Object.get_uint_at(Object* o, usz index) => o.get_integer_at(uint, index); +fn ulong! Object.get_ulong_at(Object* o, usz index) => o.get_integer_at(ulong, index); +fn uint128! Object.get_uint128_at(Object* o, usz index) => o.get_integer_at(uint128, index); + +/** + * @require o.is_keyable() + **/ +fn String! Object.get_string(Object* o, String key) +{ + Object* value = o.get(key)?; + assert(value.is_string()); + return value.s; +} + +/** + * @require o.is_indexable() + **/ +fn String Object.get_string_at(Object* o, usz index) +{ + Object* value = o.get_at(index); + assert(value.is_string()); + return value.s; +} + +/** + * @require o.is_keyable() + **/ +macro String! Object.get_enum(Object* o, $EnumType, String key) +{ + Object value = o.get(key)?; + assert($EnumType.typeid == value.type); + return ($EnumType)value.i; +} + +/** + * @require o.is_indexable() + **/ +macro String Object.get_enum_at(Object* o, $EnumType, usz index) +{ + Object value = o.get_at(index); + assert($EnumType.typeid == value.type); + return ($EnumType)value.i; +} + +/** + * @require o.is_keyable() + **/ +fn bool! Object.get_bool(Object* o, String key) +{ + Object* value = o.get(key)?; + assert(value.is_bool()); + return value.b; +} + + +/** + * @require o.is_indexable() + **/ +fn bool Object.get_bool_at(Object* o, usz index) +{ + Object* value = o.get_at(index); + assert(value.is_bool()); + return value.b; +} + +/** + * @require o.is_keyable() + **/ +fn double! Object.get_float(Object* o, String key) +{ + Object* value = o.get(key)?; + switch (value.type.kindof) + { + case SIGNED_INT: + return (double)value.i; + case UNSIGNED_INT: + return (double)(uint128)value.i; + case FLOAT: + return value.f; + default: + unreachable(); + } +} + +/** + * @require o.is_indexable() + **/ +fn double Object.get_float_at(Object* o, usz index) +{ + Object* value = o.get_at(index); + switch (value.type.kindof) + { + case SIGNED_INT: + return (double)value.i; + case UNSIGNED_INT: + return (double)(uint128)value.i; + case FLOAT: + return value.f; + default: + unreachable(); + } +} + +fn Object* Object.get_or_create_obj(Object* o, String key) +{ + if (try obj = o.get(key) && !obj.is_null()) return obj; + Object* container = new_obj(); + o.set(key, container); + return container; +} + +typedef ObjectInternalMap @private = HashMap; +typedef ObjectInternalList @private = List; +typedef ObjectInternalMapEntry @private = Entry; + diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 46c439252..1c6931729 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -256,10 +256,12 @@ LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_ } Decl *decl = const_init->type->decl; Decl **members = decl->strukt.members; + bool is_packed = decl->is_packed; uint32_t count = vec_size(members); if (decl->decl_kind == DECL_UNION && count) count = 1; LLVMValueRef *entries = NULL; bool was_modified = false; + ByteSize prev_size = 0; for (MemberIndex i = 0; i < count; i++) { if (members[i]->padding) @@ -273,12 +275,23 @@ LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_ { was_modified = true; } - AlignSize new_align = llvm_abi_alignment(c, element_type); - AlignSize expected_align = llvm_abi_alignment(c, expected_type); - if (i != 0 && new_align < expected_align) + // We may need to adjust alignment here due to the lack + // of LLVM union support (while still being strict about structs) + if (i > 0 && was_modified) { - vec_add(entries, llvm_emit_const_padding(c, expected_align - new_align)); + // Let's look at the old offset. + ByteSize old_offset = members[i - 1]->offset; + // What is the expected offset we would get? + ByteSize new_offset = is_packed ? old_offset + prev_size : aligned_offset(old_offset + prev_size, llvm_abi_alignment(c, element_type)); + // Add the padding we have built in. + new_offset += members[i]->padding; + // If this offset is too small, add const padding. + if (new_offset < members[i]->offset) + { + vec_add(entries, llvm_emit_const_padding(c, members[i]->offset - new_offset)); + } } + prev_size = llvm_abi_size(c, element_type); vec_add(entries, element); } if (decl->strukt.padding) diff --git a/src/version.h b/src/version.h index 406170cd8..da6ec54c4 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.99" \ No newline at end of file +#define COMPILER_VERSION "0.4.100" \ No newline at end of file diff --git a/test/unit/stdlib/collections/object.c3 b/test/unit/stdlib/collections/object.c3 new file mode 100644 index 000000000..cb86c63c4 --- /dev/null +++ b/test/unit/stdlib/collections/object.c3 @@ -0,0 +1,23 @@ +module object_test @test; +import std::collections::object; + +fn void test_general() +{ + Object* root = object::new_obj(); + root.set("foo", 1); + root.set("bar", "baz"); + assert(root.get_int("foo")? == 1); + assert(root.get_string("bar")? == "baz"); + Object* goo = root.set("goo", object::new_obj()); + goo.append("hello"); + goo.append(132); + assert(root.get("goo").get_int_at(1)? == 132); + assert(root.get("goo").get_string_at(0)? == "hello"); + Object* abc = root.get_or_create_obj("abc80"); + abc.set("cool", 1.3); + assert(root.get("abc80").get_int("cool")? == 1); + assert(root.get("abc80").get_float("cool")? == 1.3); + assert((root.get_int("yyy") ?? -1) == -1); + root.set("yyy", true); + assert(root.get_bool("yyy") ?? false); +}