From 33f86fcce2d539c0de5827bb223baf671a4d95bd Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Sun, 21 Apr 2024 17:31:24 +0200 Subject: [PATCH 01/12] Move reflection to its own module --- std/reflection.pr | 174 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 std/reflection.pr diff --git a/std/reflection.pr b/std/reflection.pr new file mode 100644 index 00000000..dcfa6671 --- /dev/null +++ b/std/reflection.pr @@ -0,0 +1,174 @@ +export type Type = &interface { + let name: string + let module: string + let size: size_t + let align: size_t +} + +export type OpaqueT = struct { + name: string + module: string + const size: size_t = 1 + const algin: size_t = 1 +} + +export type BaseType = struct { + name: string + module: string +} + +export type BoolT = struct { + &BaseType + const size: size_t = size_of bool + const align: size_t = align_of bool +} +export type FloatT = struct { + &BaseType + size: size_t + align: size_t +} +export type WordT = struct { + &BaseType + signed: bool + size: size_t + align: size_t +} +export type CharT = struct { + &BaseType + const size: size_t = size_of char + const align: size_t = align_of char +} + +export type BoxType = struct { + &BaseType + ref: Type +} + +export type PointerT = struct { + BoxType + const size: size_t = size_of * + const align: size_t = align_of * +} +export type ReferenceT = struct { + BoxType + const size: size_t = size_of & + const align: size_t = align_of & +} +export type WeakReferenceT = struct { + BoxType + const size: size_t = size_of weak_ref + const align: size_t = align_of weak_ref +} +export type ArrayT = struct { + BoxType + const size: size_t = size_of [*] + const align: size_t = align_of [*] +} +export type StaticArrayT = struct { + BoxType + size: size_t + align: size_t + length: size_t +} + +export type FunctionT = struct { + &BaseType + arguments: [Type] + returns: [Type] + const size: size_t = size_of -> + const align: size_t = align_of -> +} + +export type RecordType = struct { + &BaseType + size: size_t + align: size_t + members: [Field] +} +export type Field = struct { + name: string + offset: size_t + tpe: *Type +} +export type Struct = struct { + RecordType +} +export type Union = struct { + RecordType +} + +export type EnumValue = struct { + name: string + value: int64 +} +export type EnumT = struct { + BaseType + signed: bool + size: size_t + align: size_t + values: [EnumValue] +} + +export type Function = struct { + name: string + exported: bool + module: string + tpe: &FunctionT +} + +export type InterfaceT = struct { + &BaseType + const size: size_t = 0 + const align: size_t = 0 + members: [Function] +} + +export type VariantT = struct { + &BaseType + size: size_t + align: size_t + variants: [Type] +} + +export type TupleT = struct { + &BaseType + size: size_t + align: size_t + elements: [Type] +} + +var types: [Type] + +// Type registry functions +def load_types(size: size_t, data: *uint8, strings: *char) { + var index = 0 + var offset = 0 + while index < size { + index += 1 + } +} + +def type_id(index: size_t) -> Type { + return types ++ index +} + +type TypeKind = enum { + BOOL + WORD + FLOAT + STRUCT + UNION + ARRAY + STATIC_ARRAY + POINTER + REFERENCE + FUNCTION + ENUM + CHAR + STRUCTURAL + OPAQUE + WEAK_REF + TYPE + VARIANT + TUPLE +} \ No newline at end of file From 48698047de4e42e6500911e06152b7170d9dc139 Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Mon, 22 Apr 2024 19:06:04 +0200 Subject: [PATCH 02/12] Move code to reflection --- std/reflection.pr | 109 +++++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 46 deletions(-) diff --git a/std/reflection.pr b/std/reflection.pr index dcfa6671..51de4409 100644 --- a/std/reflection.pr +++ b/std/reflection.pr @@ -1,86 +1,102 @@ export type Type = &interface { let name: string let module: string + let id: uint64 let size: size_t let align: size_t } -export type OpaqueT = struct { - name: string - module: string - const size: size_t = 1 - const algin: size_t = 1 +export def implements(a: Type, b: StructuralT) -> bool { + return false } -export type BaseType = struct { +export def assignable(a: Type, b: Type) -> bool { + return false +} + +export def == (a: Type, b: Type) -> bool { + return a.id == b.id +} +export def != (a: Type, b: Type) -> bool { + return not (a == b) +} + +type BaseType = struct { name: string module: string + id: uint64 } -export type BoolT = struct { - &BaseType +export type OpaqueT = &struct { + BaseType + const size: size_t = 1 + const algin: size_t = 1 +} + +export type BoolT = &struct { + BaseType const size: size_t = size_of bool const align: size_t = align_of bool } -export type FloatT = struct { - &BaseType +export type FloatT = &struct { + BaseType size: size_t align: size_t } -export type WordT = struct { - &BaseType +export type WordT = &struct { + BaseType signed: bool size: size_t align: size_t } -export type CharT = struct { - &BaseType +export type CharT = &struct { + BaseType const size: size_t = size_of char const align: size_t = align_of char } export type BoxType = struct { - &BaseType + BaseType ref: Type } -export type PointerT = struct { +export type PointerT = &struct { BoxType const size: size_t = size_of * const align: size_t = align_of * } -export type ReferenceT = struct { +export type ReferenceT = &struct { BoxType const size: size_t = size_of & const align: size_t = align_of & } -export type WeakReferenceT = struct { +export type WeakReferenceT = &struct { BoxType const size: size_t = size_of weak_ref const align: size_t = align_of weak_ref } -export type ArrayT = struct { +export type ArrayT = &struct { BoxType const size: size_t = size_of [*] const align: size_t = align_of [*] } -export type StaticArrayT = struct { +export type StaticArrayT = &struct { BoxType size: size_t align: size_t length: size_t } -export type FunctionT = struct { - &BaseType +export type FunctionT = &struct { + BaseType arguments: [Type] returns: [Type] const size: size_t = size_of -> const align: size_t = align_of -> } -export type RecordType = struct { - &BaseType +type RecordType = struct { + BaseType size: size_t align: size_t members: [Field] @@ -90,10 +106,10 @@ export type Field = struct { offset: size_t tpe: *Type } -export type Struct = struct { +export type Struct = &struct { RecordType } -export type Union = struct { +export type Union = &struct { RecordType } @@ -101,7 +117,7 @@ export type EnumValue = struct { name: string value: int64 } -export type EnumT = struct { +export type EnumT = &struct { BaseType signed: bool size: size_t @@ -116,41 +132,29 @@ export type Function = struct { tpe: &FunctionT } -export type InterfaceT = struct { - &BaseType +export type InterfaceT = &struct { + BaseType const size: size_t = 0 const align: size_t = 0 members: [Function] } -export type VariantT = struct { - &BaseType +export type VariantT = &struct { + BaseType size: size_t align: size_t variants: [Type] } -export type TupleT = struct { - &BaseType +export type TupleT = &struct { + BaseType size: size_t align: size_t elements: [Type] } var types: [Type] - -// Type registry functions -def load_types(size: size_t, data: *uint8, strings: *char) { - var index = 0 - var offset = 0 - while index < size { - index += 1 - } -} - -def type_id(index: size_t) -> Type { - return types ++ index -} +var upper_id: int64 type TypeKind = enum { BOOL @@ -171,4 +175,17 @@ type TypeKind = enum { TYPE VARIANT TUPLE +} + +// Type registry functions +def load_types(size: size_t, data: *uint8, strings: *char) { + var index = 0 + var offset = 0 + while index < size { + index += 1 + } +} + +def type_id(index: size_t) -> Type { + return types ++ index } \ No newline at end of file From 173e9e6652fd1b5f8e645edb945c4aa11bf78daf Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Fri, 17 May 2024 11:58:13 +0200 Subject: [PATCH 03/12] Deserialize Reflection Data --- std/io.pr | 9 +++ std/reflection.pr | 191 ++++++++++++++++++++++++++++++++++++++++------ std/std.pr | 6 ++ 3 files changed, 182 insertions(+), 24 deletions(-) diff --git a/std/io.pr b/std/io.pr index a4334f99..c63d62ea 100644 --- a/std/io.pr +++ b/std/io.pr @@ -59,6 +59,11 @@ export const NO_BLOCKING = 1 return cstd::_isatty(cstd::_fileno(file)) != 0 } + export def open_memory_as_file(arr: [uint8]) -> File { + // TODO implement, see https://github.com/Arryboom/fmemopen_windows + return null + } + } else { import linux @@ -114,4 +119,8 @@ export const NO_BLOCKING = 1 export def is_a_tty(file: File) -> bool { return linux::isatty(cstd::fileno(file)) != 0 } + + export def open_memory_as_file(arr: [uint8], mode: String) -> File { + return cstd::fmemopen(arr.value, arr.size, mode.to_array().value) + } } \ No newline at end of file diff --git a/std/reflection.pr b/std/reflection.pr index 51de4409..986e8608 100644 --- a/std/reflection.pr +++ b/std/reflection.pr @@ -57,7 +57,7 @@ export type CharT = &struct { export type BoxType = struct { BaseType - ref: Type + ref: weak Type } export type PointerT = &struct { @@ -89,28 +89,28 @@ export type StaticArrayT = &struct { export type FunctionT = &struct { BaseType - arguments: [Type] - returns: [Type] + arguments: &[weak Type] + returns: &[weak Type] const size: size_t = size_of -> const align: size_t = align_of -> } -type RecordType = struct { +type RecordT = struct { BaseType size: size_t align: size_t - members: [Field] + members: &[Field] } export type Field = struct { name: string offset: size_t - tpe: *Type + tpe: weak Type } -export type Struct = &struct { - RecordType +export type StructT = &struct { + RecordT } -export type Union = &struct { - RecordType +export type UnionT = &struct { + RecordT } export type EnumValue = struct { @@ -122,39 +122,38 @@ export type EnumT = &struct { signed: bool size: size_t align: size_t - values: [EnumValue] + values: &[EnumValue] } export type Function = struct { name: string exported: bool module: string - tpe: &FunctionT + tpe: FunctionT } export type InterfaceT = &struct { BaseType const size: size_t = 0 const align: size_t = 0 - members: [Function] + members: &[Function] } export type VariantT = &struct { BaseType size: size_t align: size_t - variants: [Type] + variants: &[weak Type] } export type TupleT = &struct { BaseType size: size_t align: size_t - elements: [Type] + elements: &[weak Type] } -var types: [Type] -var upper_id: int64 +var types: &[Type] type TypeKind = enum { BOOL @@ -166,26 +165,170 @@ type TypeKind = enum { STATIC_ARRAY POINTER REFERENCE + WEAK_REF FUNCTION ENUM CHAR STRUCTURAL - OPAQUE - WEAK_REF - TYPE VARIANT TUPLE } +import io + // Type registry functions -def load_types(size: size_t, data: *uint8, strings: *char) { +def load_types(data: [uint8], num: size_t, strings: *char) { + let fp = io::open_memory_as_file(data, "r") + defer close(fp) + + types = allocate_ref(Type, num) + var index = 0 - var offset = 0 - while index < size { + while index < num { + let kind = fp.read(TypeKind) + let name = make_string(strings ++ fp.read(int)) + let module = make_string(strings ++ fp.read(int)) + let id = fp.read(size_t) + + switch kind { + case TypeKind::BOOL + types(index) = [ name = name, module = module, id = id ] !BoolT + case TypeKind::WORD + let size = fp.read(int) + let align = fp.read(int) + let signed = fp.read(bool) + types(index) = [ name = name, module = module, id = id, + size = size, align = align, signed = signed ] !WordT + case TypeKind::FLOAT + let size = fp.read(int) + let align = fp.read(int) + types(index) = [ name = name, module = module, id = id, + size = size, align = align ] !FloatT + case TypeKind::STRUCT, TypeKind::UNION + let size = fp.read(int) + let align = fp.read(int) + let members = zero_allocate(Field, fp.read(int)) + for var i in 0..members.size { + members(i) = [ name = make_string(strings ++ fp.read(int)), offset = fp.read(int) ] !Field + } + + if kind == TypeKind::STRUCT { + types(index) = [ name = name, module = module, id = id, + size = size, align = align, members = members ] !StructT + } else { + types(index) = [ name = name, module = module, id = id, + size = size, align = align, members = members ] !UnionT + } + case TypeKind::ARRAY + types(index) = [ name = name, module = module, id = id ] !ArrayT + case TypeKind::STATIC_ARRAY + let size = fp.read(size_t) + let align = fp.read(size_t) + let length = fp.read(size_t) + + types(index) = [ name = name, module = module, id = id, + size = size, align = align, length = length ] !StaticArrayT + case TypeKind::POINTER + types(index) = [ name = name, module = module, id = id ] !PointerT + case TypeKind::REFERENCE + types(index) = [ name = name, module = module, id = id ] !ReferenceT + case TypeKind::WEAK_REF + types(index) = [ name = name, module = module, id = id ] !WeakReferenceT + case TypeKind::FUNCTION + types(index) = [ name = name, module = module, id = id, + arguments = allocate_ref(type weak Type, fp.read(int)), + returns = allocate_ref(type weak Type, fp.read(int)) ] !FunctionT + case TypeKind::ENUM + let size = fp.read(int) + let align = fp.read(int) + let signed = fp.read(bool) + + let values = allocate_ref(type EnumValue, fp.read(int)) + for var i in 0..values.size { + values(i) = [ + name = make_string(strings ++ fp.read(int)), + value = fp.read(int64) + ] !EnumValue + } + types(index) = [ name = name, module = module, id = id, + size = size, align = align, values = values ] !EnumT + case TypeKind::CHAR + types(index) = [ name = name, module = module, id = id ] !CharT + case TypeKind::STRUCTURAL + let members = allocate_ref(type Function, fp.read(int)) + for var i in 0..members.size { + members(i) = [ + name = make_string(strings ++ fp.read(int)), + exported = fp.read(bool), + module = make_string(strings ++ fp.read(int)) + ] !Function + } + types(index) = [ name = name, module = module, id = id, members = members ] !InterfaceT + case TypeKind::VARIANT + types(index) = [ name = name, module = module, id = id, + variants = allocate_ref(type weak Type, fp.read(int)) + ] !VariantT + case TypeKind::TUPLE + types(index) = [ name = name, module = module, id = id, + elements = allocate_ref(type weak Type, fp.read(int)) + ] !TupleT + } + index += 1 } + + // Resolve type references + for var tpe in types { + if tpe.type == StructT { + let rec = tpe !StructT + for var i in 0..rec.fields.size { + let f = *rec.fields(i) + f.tpe = type_id(fp.read(int)) + } + } else if tpe.type == UnionT { + let rec = tpe !UnionT + for var i in 0..rec.fields.size { + let f = *rec.fields(i) + f.tpe = type_id(fp.read(int)) + } + } else if tpe.type == InterfaceT { + let intf = tpe !InterfaceT + for var i in 0..intf.members.size { + let m = *inf.members(i) + m.tpe = type_id(fp.read(int)) !FunctionT + } + } else if tpe.type == PointerT { + (box !PointerT).tpe = type_id(fp.read(int)) + } else if tpe.type == ReferenceT { + (box !ReferenceT).tpe = type_id(fp.read(int)) + } else if tpe.type == WeakReferenceT { + (box !WeakReferenceT).tpe = type_id(fp.read(int)) + } else if tpe.type == ArrayT { + (box !ArrayT).tpe = type_id(fp.read(int)) + } else if tpe.type == StaticArrayT { + (box !StaticArrayT).tpe = type_id(fp.read(int)) + } else if (tpe.type == VariantT) { + let vnt = tpe !VariantT + for var i in 0..vnt.variants.size { + vnt.variants(i) = type_id(fp.read(int)) + } + } else if (tpe.type == TupleT) { + let tuple = tpe !TupleT + for var i in 0..vnt.elements.size { + vnt.elements(i) = type_id(fp.read(int)) + } + } else if (tpe.type == FunctionT) { + let fun = tpe !FunctionT + for var i in 0..fun.arguments.size { + fun.arguments(i) = type_id(fp.read(int)) + } + for var i in 0..fun.returns.size { + fun.returns(i) = type_id(fp.read(int)) + } + } + } } def type_id(index: size_t) -> Type { - return types ++ index + return types(index) } \ No newline at end of file diff --git a/std/std.pr b/std/std.pr index 2b2fa962..88c70c26 100644 --- a/std/std.pr +++ b/std/std.pr @@ -554,6 +554,12 @@ export def read(file: File, ptr: type *T) -> size_t { return cstd::fread(ptr, T.size, 1, file) } +export def read(file: File, type T) -> T { + var data: T + cstd::fread(*data, T.size, 1, file) + return data +} + export def read_str(file: File) -> Str { var len: size_t file.read(*len) From 302071785a2828ffd6c39f76f7537bde5664a7b8 Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Fri, 17 May 2024 12:04:27 +0200 Subject: [PATCH 04/12] use a map instead --- std/reflection.pr | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/std/reflection.pr b/std/reflection.pr index 986e8608..76704f9a 100644 --- a/std/reflection.pr +++ b/std/reflection.pr @@ -153,8 +153,6 @@ export type TupleT = &struct { elements: &[weak Type] } -var types: &[Type] - type TypeKind = enum { BOOL WORD @@ -175,13 +173,18 @@ type TypeKind = enum { } import io +import map + +var types: &Map(size_t, Type) // Type registry functions def load_types(data: [uint8], num: size_t, strings: *char) { let fp = io::open_memory_as_file(data, "r") defer close(fp) - types = allocate_ref(Type, num) + if not types { + types = map::make(size_t, Type) + } var index = 0 while index < num { From 24f516e0c3c7351e4b2a663856e22495791fa795 Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Fri, 17 May 2024 16:10:43 +0200 Subject: [PATCH 05/12] Get it to compile --- std/reflection.pr | 65 +++++++++++++++++++++++++---------------------- std/strings.pr | 4 +++ 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/std/reflection.pr b/std/reflection.pr index 76704f9a..33fcb7e3 100644 --- a/std/reflection.pr +++ b/std/reflection.pr @@ -1,12 +1,12 @@ export type Type = &interface { - let name: string - let module: string + let name: StringSlice + let module: StringSlice let id: uint64 let size: size_t let align: size_t } -export def implements(a: Type, b: StructuralT) -> bool { +export def implements(a: Type, b: InterfaceT) -> bool { return false } @@ -22,8 +22,8 @@ export def != (a: Type, b: Type) -> bool { } type BaseType = struct { - name: string - module: string + name: StringSlice + module: StringSlice id: uint64 } @@ -57,7 +57,7 @@ export type CharT = &struct { export type BoxType = struct { BaseType - ref: weak Type + tpe: weak Type } export type PointerT = &struct { @@ -72,8 +72,8 @@ export type ReferenceT = &struct { } export type WeakReferenceT = &struct { BoxType - const size: size_t = size_of weak_ref - const align: size_t = align_of weak_ref + const size: size_t = size_of weak & + const align: size_t = align_of weak & } export type ArrayT = &struct { BoxType @@ -102,7 +102,7 @@ type RecordT = struct { members: &[Field] } export type Field = struct { - name: string + name: StringSlice offset: size_t tpe: weak Type } @@ -114,7 +114,7 @@ export type UnionT = &struct { } export type EnumValue = struct { - name: string + name: StringSlice value: int64 } export type EnumT = &struct { @@ -126,9 +126,9 @@ export type EnumT = &struct { } export type Function = struct { - name: string + name: StringSlice exported: bool - module: string + module: StringSlice tpe: FunctionT } @@ -174,6 +174,7 @@ type TypeKind = enum { import io import map +import std var types: &Map(size_t, Type) @@ -189,8 +190,8 @@ def load_types(data: [uint8], num: size_t, strings: *char) { var index = 0 while index < num { let kind = fp.read(TypeKind) - let name = make_string(strings ++ fp.read(int)) - let module = make_string(strings ++ fp.read(int)) + let name = make_slice(strings, fp.read(int)) + let module = make_slice(strings, fp.read(int)) let id = fp.read(size_t) switch kind { @@ -212,7 +213,7 @@ def load_types(data: [uint8], num: size_t, strings: *char) { let align = fp.read(int) let members = zero_allocate(Field, fp.read(int)) for var i in 0..members.size { - members(i) = [ name = make_string(strings ++ fp.read(int)), offset = fp.read(int) ] !Field + members(i) = [ name = make_slice(strings, fp.read(int)), offset = fp.read(int) ] !Field } if kind == TypeKind::STRUCT { @@ -249,7 +250,7 @@ def load_types(data: [uint8], num: size_t, strings: *char) { let values = allocate_ref(type EnumValue, fp.read(int)) for var i in 0..values.size { values(i) = [ - name = make_string(strings ++ fp.read(int)), + name = make_slice(strings, fp.read(int)), value = fp.read(int64) ] !EnumValue } @@ -261,9 +262,9 @@ def load_types(data: [uint8], num: size_t, strings: *char) { let members = allocate_ref(type Function, fp.read(int)) for var i in 0..members.size { members(i) = [ - name = make_string(strings ++ fp.read(int)), + name = make_slice(strings, fp.read(int)), exported = fp.read(bool), - module = make_string(strings ++ fp.read(int)) + module = make_slice(strings, fp.read(int)) ] !Function } types(index) = [ name = name, module = module, id = id, members = members ] !InterfaceT @@ -281,35 +282,37 @@ def load_types(data: [uint8], num: size_t, strings: *char) { } // Resolve type references - for var tpe in types { + for var id in @types.keys() { + let tpe = types(id) + if tpe.type == StructT { let rec = tpe !StructT - for var i in 0..rec.fields.size { - let f = *rec.fields(i) + for var i in 0..rec.members.size { + let f = *rec.members(i) f.tpe = type_id(fp.read(int)) } } else if tpe.type == UnionT { let rec = tpe !UnionT - for var i in 0..rec.fields.size { - let f = *rec.fields(i) + for var i in 0..rec.members.size { + let f = *rec.members(i) f.tpe = type_id(fp.read(int)) } } else if tpe.type == InterfaceT { let intf = tpe !InterfaceT for var i in 0..intf.members.size { - let m = *inf.members(i) + let m = *intf.members(i) m.tpe = type_id(fp.read(int)) !FunctionT } } else if tpe.type == PointerT { - (box !PointerT).tpe = type_id(fp.read(int)) + (tpe !PointerT).tpe = type_id(fp.read(int)) } else if tpe.type == ReferenceT { - (box !ReferenceT).tpe = type_id(fp.read(int)) + (tpe !ReferenceT).tpe = type_id(fp.read(int)) } else if tpe.type == WeakReferenceT { - (box !WeakReferenceT).tpe = type_id(fp.read(int)) + (tpe !WeakReferenceT).tpe = type_id(fp.read(int)) } else if tpe.type == ArrayT { - (box !ArrayT).tpe = type_id(fp.read(int)) + (tpe !ArrayT).tpe = type_id(fp.read(int)) } else if tpe.type == StaticArrayT { - (box !StaticArrayT).tpe = type_id(fp.read(int)) + (tpe !StaticArrayT).tpe = type_id(fp.read(int)) } else if (tpe.type == VariantT) { let vnt = tpe !VariantT for var i in 0..vnt.variants.size { @@ -317,8 +320,8 @@ def load_types(data: [uint8], num: size_t, strings: *char) { } } else if (tpe.type == TupleT) { let tuple = tpe !TupleT - for var i in 0..vnt.elements.size { - vnt.elements(i) = type_id(fp.read(int)) + for var i in 0..tuple.elements.size { + tuple.elements(i) = type_id(fp.read(int)) } } else if (tpe.type == FunctionT) { let fun = tpe !FunctionT diff --git a/std/strings.pr b/std/strings.pr index 4e3c4d70..5e2cd807 100644 --- a/std/strings.pr +++ b/std/strings.pr @@ -400,6 +400,10 @@ export def >= (s1: IString, s2: IString) -> bool { return cmp(s1, s2) >= 0 } +export def make_slice(s: *char, offset: size_t) -> StringSlice { + return [ parent = null, count = cstd::strlen(s), data = s, offset = offset ] !StringSlice +} + export implicit def to_slice(s: [char]) -> StringSlice { return slice(s, 0, s.length()) } From fa0f65c609ba686ccfe8cbf2e18b657f991c0897 Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Sun, 21 Jul 2024 18:55:01 +0200 Subject: [PATCH 06/12] Fix some compilation errors --- src/codegen.pr | 4 ---- src/compiler.pr | 37 +++++++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/codegen.pr b/src/codegen.pr index 69947291..f8ae454b 100644 --- a/src/codegen.pr +++ b/src/codegen.pr @@ -796,10 +796,6 @@ export def gen(module: &toolchain::Module) { gen_header(fp, module) if module.module == "main" { - module.imported.add("malloc") - module.imported.add("free") - module.imported.add("strlen") - gen_main_function(fp) } diff --git a/src/compiler.pr b/src/compiler.pr index c4c1879a..a561bb9c 100644 --- a/src/compiler.pr +++ b/src/compiler.pr @@ -741,6 +741,7 @@ def meta_to_debug_value(meta: &Value) -> DebugValue { } export def make_location(node: &parser::Node, state: &State) -> &Value { + if not node { return null } let discope = vector::peek(state.discope) if state.discope.length > 0 else null !&Value if not toolchain::debug_sym { return null } @@ -9533,19 +9534,26 @@ def generate_vtable_function(function: &Function, tpe: &typechecking::Type, stat if is_const { state.ret(@const_field.value) } else { - var deref = state.extract_value(pointer(type_entry.tpe.tpe), reference, [1]) - var value = state.load(type_entry.tpe.tpe, deref) - var findex: size_t = 0 - var ftpe: &typechecking::Type - for var field in @type_entry.tpe.tpe.fields { - if field.name == name { - findex = field.index - ftpe = field.tpe - } + var stpe = type_entry.tpe.tpe + // Resolve member + var deref = state.extract_value(pointer(stpe), reference, [1]) + var value = state.load(stpe, deref) + value.addr = deref + + let vec = vector::make(Member) + if not resolve_member(vec, stpe, name) { + return + } + + let len = vector::length(vec) + for var i in 0..len { + let j = len - i - 1 + let member = vec(j) + value = walk_MemberAccess_struct(null, stpe, member, value, state) + stpe = member.tpe } - value = state.extract_value(ftpe, value, [findex !int]) - state.ret(value) + state.ret(load_value(value, null, state)) } } } @@ -9555,7 +9563,7 @@ def generate_vtable_function(function: &Function, tpe: &typechecking::Type, stat push_label(end_label, state) swtch.value.switch_.otherwise = end_label - state.module.imported.add("abort") + import_cstd_function("abort", state) state.call("abort", null, [] ![Value]) state.module.imported.add(function.name) @@ -9621,6 +9629,11 @@ export def compile(module: &toolchain::Module) { export def compile(state: &State, is_main: bool, no_cleanup: bool = false) { toolchain::progress_update(state.module, toolchain::ProgressUpdate::START) + // Import required functions + import_cstd_function("malloc", state) + import_cstd_function("free", state) + import_cstd_function("strlen", state) + let node = state.module.node assert(node.kind == parser::NodeKind::PROGRAM) From 1b0e0c7575dcfc614994504047b62c35efa8a94f Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Tue, 3 Sep 2024 20:37:29 +0200 Subject: [PATCH 07/12] Work on the new reflection api --- src/builtins.pr | 22 ++++++ src/compiler.pr | 185 ++++++++++++++++++++++++++++++++++++++++++-- src/toolchain.pr | 3 +- src/typechecking.pr | 59 +++++++------- std/io.pr | 31 ++++++++ std/reflection.pr | 91 +++++++++++++++++----- std/strings.pr | 2 +- 7 files changed, 334 insertions(+), 59 deletions(-) diff --git a/src/builtins.pr b/src/builtins.pr index 2be66a37..5e97e27a 100644 --- a/src/builtins.pr +++ b/src/builtins.pr @@ -108,6 +108,28 @@ export let size_t_ = create_int_type("size_t", size_of size_t, true) ) } +// Reflection data +scope::create_variable( + builtins, parser::make_identifier("__reflection_data"), + parser::ShareMarker::EXPORT, parser::VarDecl::CONST, typechecking::pointer(uint8_), + [ kind = compiler::ValueKind::GLOBAL, tpe = typechecking::pointer(uint8_), name = "__reflection_data"] !&compiler::Value +) +scope::create_variable( + builtins, parser::make_identifier("__reflection_strings"), + parser::ShareMarker::EXPORT, parser::VarDecl::CONST, typechecking::pointer(char_), + [ kind = compiler::ValueKind::GLOBAL, tpe = typechecking::pointer(char_), name = "__reflection_strings"] !&compiler::Value +) +scope::create_variable( + builtins, parser::make_identifier("__reflection_data_size"), + parser::ShareMarker::EXPORT, parser::VarDecl::CONST, typechecking::pointer(size_t_), + [ kind = compiler::ValueKind::GLOBAL, tpe = typechecking::pointer(size_t_), name = "__reflection_data_size"] !&compiler::Value +) +scope::create_variable( + builtins, parser::make_identifier("__reflection_num_types"), + parser::ShareMarker::EXPORT, parser::VarDecl::CONST, typechecking::pointer(size_t_), + [ kind = compiler::ValueKind::GLOBAL, tpe = typechecking::pointer(size_t_), name = "__reflection_num_types"] !&compiler::Value +) + // These get set from toolchain export var File_: &typechecking::Type = null export var Type_: &typechecking::Type = null diff --git a/src/compiler.pr b/src/compiler.pr index a561bb9c..6660316c 100644 --- a/src/compiler.pr +++ b/src/compiler.pr @@ -15,6 +15,7 @@ import md5 import optional import arena import constants +import io export type Label = struct { name: Str @@ -1014,12 +1015,8 @@ export def charp_static(global: &Value, state: &State) -> Value { return local } -export def create_global_string(str: Str, state: &State) -> Value { - let tpe = typechecking::make_type_raw(typechecking::TypeKind::STATIC_ARRAY) - tpe._tpe = builtins::char_ - tpe.length = str.length() + 1 - tpe.size = tpe.length * (size_of char) - tpe.align = align_of char +export def make_global_string(str: Str, state: &State) -> Value { + let tpe = typechecking::make_static_array(builtins::char_, str.length() + 1) let value = [ kind = ValueKind::STRING, @@ -1031,7 +1028,7 @@ export def create_global_string(str: Str, state: &State) -> Value { } export def charp(str: Str, state: &State) -> Value { - return charp_static(create_global_string(str, state), state) + return charp_static(make_global_string(str, state), state) } def push_scope(node: &parser::Node, state: &State) { @@ -9310,6 +9307,178 @@ def change_value_to_type(tpe: &typechecking::Type, state: &State) -> &scope::Val return value } +def make_global_data(name: Str, value: &ByteStream, state: &State) { + let tpe = typechecking::make_static_array(builtins::char_, value.data().size) + + let s = value.data() !&[byte] !&[char] + let global = [ + name = name, + tpe = tpe, + value = [ tpe = tpe, kind = ValueKind::STRING, s = s ] !Value, + line = -1 + ] !&Global + + state.module.result.globals(name) = global + state.module.imported.add(name) +} + +def make_global_data(name: Str, value: size_t, state: &State) { + let global = [ + name = name, + tpe = builtins::size_t_, + value = [ tpe = builtins::size_t_, kind = ValueKind::INT, i = value ] !Value, + line = -1 + ] !&Global + + state.module.result.globals(name) = global + state.module.imported.add(name) +} + +export def generate_reflection_data() { + let data = io::make_stream() + let strings = [ strings = io::make_stream(), map = map::make(size_t) ] !&UniquedStrings + + for var key in @types_to_resolve.keys() { + let to_resolve = types_to_resolve(key) + generate_reflection_data_1(to_resolve.tpe, data, strings) + } + + for var key in @types_to_resolve.keys() { + let to_resolve = types_to_resolve(key) + generate_reflection_data_2(to_resolve.tpe, data, strings) + } + + make_global_data("__reflection_data", data, toolchain::types_state) + make_global_data("__reflection_data_size", data.data().size, toolchain::types_state) + make_global_data("__reflection_strings", strings.strings, toolchain::types_state) + make_global_data("__reflection_num_types", types_to_resolve.size, toolchain::types_state) +} + +type UniquedStrings = struct { + strings: &ByteStream + map: &SMap(size_t) +} + +def write_zt(uniqued: &UniquedStrings, s: Str) -> int { + if uniqued.map.contains(s) { + return uniqued.map(s) !int + } + let val = uniqued.strings.write_zt(s) + uniqued.map(s) = val + return val !int +} + +def generate_reflection_data_2(tpe: &typechecking::Type, data: &ByteStream, strings: &UniquedStrings) { + if tpe.kind == typechecking::TypeKind::STUB { return } + + // Collect type members + let type_members = vector::make(typechecking::TypeEntryMember) + for var member in typechecking::iterate_member_functions(tpe) { + type_members.push(member) + } + + // Write type members + data.write(type_members.length !int) + for var member in type_members { + data.write(strings.write_zt(member.function.type_name)) + data.write(member.exported) + data.write(strings.write_zt(member.module.module)) + + data.write(member.function.parameter_t.length !int) + for var arg in member.function.parameter_t { + data.write(arg.tpe.hash) // TODO Do we want function argument names maybe? + } + data.write(member.function.return_t.length !int) + for var t in member.function.return_t { + data.write(t.hash) + } + } + + switch tpe.kind { + case typechecking::TypeKind::STRUCT, typechecking::TypeKind::UNION + for var member in @tpe.fields { + data.write(member.tpe.hash) + } + case typechecking::TypeKind::STRUCTURAL + for var member in tpe.members { + for var np in member.parameter_t { + data.write(np.tpe.hash) + } + for var t in member.return_t { + data.write(t.hash) + } + } + case typechecking::TypeKind::POINTER, typechecking::TypeKind::REFERENCE, + typechecking::TypeKind::WEAK_REF, typechecking::TypeKind::ARRAY, + typechecking::TypeKind::STATIC_ARRAY + data.write(tpe.tpe.hash) + case typechecking::TypeKind::TUPLE + for var t in tpe.return_t { + data.write(t.hash) + } + case typechecking::TypeKind::VARIANT + for var t in @tpe.variants.keys() { + data.write(t.hash) + } + case typechecking::TypeKind::CLOSURE, typechecking::TypeKind::FUNCTION + for var np in tpe.parameter_t { + data.write(np.tpe.hash) + } + for var t in tpe.return_t { + data.write(t.hash) + } + } +} + +def generate_reflection_data_1(tpe: &typechecking::Type, data: &ByteStream, strings: &UniquedStrings) { + if tpe.kind == typechecking::TypeKind::STUB { return } + + data.write(tpe.kind) // This does rely on the values beind the same + data.write(strings.write_zt(tpe.name)) + data.write(strings.write_zt(tpe.module.module)) + data.write(tpe.hash) + + switch tpe.kind { + case typechecking::TypeKind::WORD + data.write(tpe.size !int) + data.write(tpe.align !int) + data.write(not tpe.unsig) + case typechecking::TypeKind::FLOAT + data.write(tpe.size !int) + data.write(tpe.align !int) + case typechecking::TypeKind::STRUCT, typechecking::TypeKind::UNION + data.write(tpe.size !int) + data.write(tpe.align !int) + data.write(tpe.fields.size) + for var i in 0..tpe.fields.size { + let field = tpe.fields(i) + let member = tpe.field_types(field.index) + data.write(strings.write_zt(field.name)) + data.write(member.offset !int) + } + case typechecking::TypeKind::STATIC_ARRAY + data.write(tpe.size) + data.write(tpe.align) + data.write(tpe.length) + case typechecking::TypeKind::FUNCTION, typechecking::TypeKind::CLOSURE + data.write(tpe.parameter_t.length !int) + data.write(tpe.return_t.length !int) + case typechecking::TypeKind::ENUM + data.write(tpe.size !int) + data.write(tpe.align !int) + data.write(not tpe.unsig) + case typechecking::TypeKind::STRUCTURAL + data.write(tpe.members.length) + for var member in tpe.members { + data.write(strings.write_zt(member.name)) + data.write(member.parameter_t.length !int) + data.write(member.return_t.length !int) + } + case typechecking::TypeKind::VARIANT, typechecking::TypeKind::TUPLE + data.write(tpe.variants.size) + } +} + export def make_result -> &Result { return [ @@ -9652,7 +9821,7 @@ export def compile(state: &State, is_main: bool, no_cleanup: bool = false) { state.finalizer.dllexport = true if builtins::builtins.fields.contains("DEBUG_REF_CYCLES") { - state.file_name_value = create_global_string(state.module.module, state) + state.file_name_value = make_global_string(state.module.module, state) } import_structures(builtins::Type_, state.module) diff --git a/src/toolchain.pr b/src/toolchain.pr index 356bf4c9..481d8f16 100644 --- a/src/toolchain.pr +++ b/src/toolchain.pr @@ -826,7 +826,8 @@ export def compile_main_file(filename: String) { } debug::trace("Resolving types") - compiler::resolve_types() + compiler::generate_reflection_data() + compiler::resolve_types() // TODO Remove this function debug::trace("Creating builtin functions") compiler::create_builtin_functions() diff --git a/src/typechecking.pr b/src/typechecking.pr index eaf97396..34254333 100644 --- a/src/typechecking.pr +++ b/src/typechecking.pr @@ -20,22 +20,28 @@ import md5 import optional export type TypeKind = enum { - TYPE + BOOL WORD FLOAT - BOOL STRUCT UNION - ENUM - FUNCTION - CLOSURE - TUPLE + ARRAY + STATIC_ARRAY POINTER - BYREF // ref parameter REFERENCE WEAK_REF - ARRAY - STATIC_ARRAY + FUNCTION + CLOSURE + ENUM + CHAR + STRUCTURAL + VARIANT + TUPLE + // This is used to express types that may by resolved to multiple types + // Tagged union types, very similar to VARIANT but the equality relation is different + // This is actually called variant in runtime + TUNION + BYREF // ref parameter RANGE RANGE_INC // Used for enums @@ -49,23 +55,17 @@ export type TypeKind = enum { // Null NULL UNDEF - CHAR - STRUCTURAL // This is a type with arguments TYPE_CONSTRUCTOR // This is used as a function parameter that is generic GENERIC - // This is used to express types that may by resolved to multiple types - VARIANT - // Tagged union types, very similar to VARIANT but the equality relation is different - // This is actually called variant in runtime - TUNION // References to structs are using a wk reference, ie. Type::wk instead of Type::_tpe // This is because references to structs can cause cycles // TODO I think we don't really need this because we can just use wk directly BOX VOID TO_INFER // Placeholder type for inference + TYPE } export type TypeMember = struct { @@ -99,6 +99,14 @@ export type StructuralTypeMember = struct { return_t: &Vector(&Type) } +def make_type_member(name: Str, parameter_t: &Vector(NamedParameter), return_t: &Vector(&Type)) -> StructuralTypeMember { + return [ + name = name, + parameter_t = parameter_t, + return_t = return_t + ] !StructuralTypeMember +} + export type TypeRef = struct { module: weak &toolchain::Module name: Str @@ -2913,11 +2921,7 @@ export def do_type_lookup(node: &parser::Node, state: &State, current_type: &Typ vector::push(return_t, ntpe) } - let structural_type_member = [ - name = name, - parameter_t = parameter_t, - return_t = return_t - ] !StructuralTypeMember + let structural_type_member = make_type_member(name, parameter_t, return_t) members.push(structural_type_member) } else { let parameter_t = vector::make(NamedParameter) @@ -2928,11 +2932,7 @@ export def do_type_lookup(node: &parser::Node, state: &State, current_type: &Typ var rtpe = box(type_lookup(return_node, state, current_return_type, false, cache)) return_t.push(rtpe) - let structural_type_member = [ - name = name, - parameter_t = parameter_t, - return_t = return_t - ] !StructuralTypeMember + let structural_type_member = make_type_member(name, parameter_t, return_t) members.push(structural_type_member) if kw == parser::MemberType::VAR { @@ -2944,11 +2944,8 @@ export def do_type_lookup(node: &parser::Node, state: &State, current_type: &Typ _tpe = rtpe ] !NamedParameter) - let structural_type_member = [ - name = "__set_" + name + "__", - parameter_t = parameter_t, - return_t = return_t - ] !StructuralTypeMember + let structural_type_member = make_type_member( + "__set_" + name + "__", parameter_t, return_t) members.push(structural_type_member) } } diff --git a/std/io.pr b/std/io.pr index 12238938..d074ca3f 100644 --- a/std/io.pr +++ b/std/io.pr @@ -1,5 +1,36 @@ import cstd import std +import vector +import strings + +export type ByteStream = struct { + _data: &Vector(byte) +} + +export def make_stream -> &ByteStream { + return [ _data = vector::make(byte) ] !ByteStream +} + +export def write(this: &ByteStream, data: type T) { + this._data.extend(T.size) + std::memcopy(*data, this._data.data.value ++ this._data.length, T.size) + this._data.length += T.size +} + +export def write_zt(this: &ByteStream, s: Str) -> size_t { + let offset = this._data.length + this._data.extend(s.length + 1) + std::memcopy(get_internal_buffer(*s), this._data.data.value ++ offset, s.length) + this._data.data(this._data.length + s.length) = 0 + this._data.length += s.length + 1 + return offset +} + +export def data(this: &ByteStream) -> [byte] { + let data = this._data.data + data.size = this._data.length + return data +} var stderr_orig_fd = -1 export var stderr_orig: File diff --git a/std/reflection.pr b/std/reflection.pr index 33fcb7e3..34474499 100644 --- a/std/reflection.pr +++ b/std/reflection.pr @@ -4,6 +4,7 @@ export type Type = &interface { let id: uint64 let size: size_t let align: size_t + let type_members: &[Function] } export def implements(a: Type, b: InterfaceT) -> bool { @@ -25,6 +26,7 @@ type BaseType = struct { name: StringSlice module: StringSlice id: uint64 + type_members: &[Function] } export type OpaqueT = &struct { @@ -87,10 +89,18 @@ export type StaticArrayT = &struct { length: size_t } -export type FunctionT = &struct { +type FunctionBase = struct { BaseType arguments: &[weak Type] returns: &[weak Type] +} +export type FunctionT = &struct { + FunctionBase + const size: size_t = size_of def -> + const align: size_t = align_of def -> +} +export type ClosureT = &struct { + FunctionBase const size: size_t = size_of -> const align: size_t = align_of -> } @@ -129,7 +139,8 @@ export type Function = struct { name: StringSlice exported: bool module: StringSlice - tpe: FunctionT + arguments: &[weak Type] + returns: &[weak Type] } export type InterfaceT = &struct { @@ -165,6 +176,7 @@ type TypeKind = enum { REFERENCE WEAK_REF FUNCTION + CLOSURE ENUM CHAR STRUCTURAL @@ -178,9 +190,15 @@ import std var types: &Map(size_t, Type) +load_types(__reflection_data, @__reflection_data_size, @__reflection_num_types, __reflection_strings) + // Type registry functions -def load_types(data: [uint8], num: size_t, strings: *char) { +def load_types(input: *uint8, size: size_t, num: size_t, strings: *char) { + var data: [uint8] + data.value = input + data.size = size let fp = io::open_memory_as_file(data, "r") + defer close(fp) if not types { @@ -238,7 +256,7 @@ def load_types(data: [uint8], num: size_t, strings: *char) { types(index) = [ name = name, module = module, id = id ] !ReferenceT case TypeKind::WEAK_REF types(index) = [ name = name, module = module, id = id ] !WeakReferenceT - case TypeKind::FUNCTION + case TypeKind::FUNCTION, TypeKind::CLOSURE types(index) = [ name = name, module = module, id = id, arguments = allocate_ref(type weak Type, fp.read(int)), returns = allocate_ref(type weak Type, fp.read(int)) ] !FunctionT @@ -263,8 +281,9 @@ def load_types(data: [uint8], num: size_t, strings: *char) { for var i in 0..members.size { members(i) = [ name = make_slice(strings, fp.read(int)), - exported = fp.read(bool), - module = make_slice(strings, fp.read(int)) + exported = true, + arguments = allocate_ref(type weak Type, fp.read(int)), + returns = allocate_ref(type weak Type, fp.read(int)) ] !Function } types(index) = [ name = name, module = module, id = id, members = members ] !InterfaceT @@ -285,51 +304,87 @@ def load_types(data: [uint8], num: size_t, strings: *char) { for var id in @types.keys() { let tpe = types(id) + let nmembers = fp.read(int) + tpe.type_members = allocate_ref(type Function, nmembers) + for var i in 0..nmembers { + let member = [ + name = make_slice(strings, fp.read(int)), + exported = fp.read(bool), + module = make_slice(strings, fp.read(int)) + ] !Function + + let arguments = allocate_ref(type weak Type, fp.read(int)) + for var i in 0..arguments.size { + arguments(i) = type_id(fp.read(size_t)) + } + let returns = allocate_ref(type weak Type, fp.read(int)) + for var i in 0..returns.size { + returns(i) = type_id(fp.read(size_t)) + } + + member.arguments = arguments + member.returns = returns + tpe.type_members()(i) = member + } + if tpe.type == StructT { let rec = tpe !StructT for var i in 0..rec.members.size { let f = *rec.members(i) - f.tpe = type_id(fp.read(int)) + f.tpe = type_id(fp.read(size_t)) } } else if tpe.type == UnionT { let rec = tpe !UnionT for var i in 0..rec.members.size { let f = *rec.members(i) - f.tpe = type_id(fp.read(int)) + f.tpe = type_id(fp.read(size_t)) } } else if tpe.type == InterfaceT { let intf = tpe !InterfaceT for var i in 0..intf.members.size { let m = *intf.members(i) - m.tpe = type_id(fp.read(int)) !FunctionT + for var i in 0..m.arguments.size { + m.arguments(i) = type_id(fp.read(size_t)) + } + for var i in 0..m.returns.size { + m.returns(i) = type_id(fp.read(size_t)) + } } } else if tpe.type == PointerT { - (tpe !PointerT).tpe = type_id(fp.read(int)) + (tpe !PointerT).tpe = type_id(fp.read(size_t)) } else if tpe.type == ReferenceT { - (tpe !ReferenceT).tpe = type_id(fp.read(int)) + (tpe !ReferenceT).tpe = type_id(fp.read(size_t)) } else if tpe.type == WeakReferenceT { - (tpe !WeakReferenceT).tpe = type_id(fp.read(int)) + (tpe !WeakReferenceT).tpe = type_id(fp.read(size_t)) } else if tpe.type == ArrayT { - (tpe !ArrayT).tpe = type_id(fp.read(int)) + (tpe !ArrayT).tpe = type_id(fp.read(size_t)) } else if tpe.type == StaticArrayT { - (tpe !StaticArrayT).tpe = type_id(fp.read(int)) + (tpe !StaticArrayT).tpe = type_id(fp.read(size_t)) } else if (tpe.type == VariantT) { let vnt = tpe !VariantT for var i in 0..vnt.variants.size { - vnt.variants(i) = type_id(fp.read(int)) + vnt.variants(i) = type_id(fp.read(size_t)) } } else if (tpe.type == TupleT) { let tuple = tpe !TupleT for var i in 0..tuple.elements.size { - tuple.elements(i) = type_id(fp.read(int)) + tuple.elements(i) = type_id(fp.read(size_t)) } } else if (tpe.type == FunctionT) { let fun = tpe !FunctionT for var i in 0..fun.arguments.size { - fun.arguments(i) = type_id(fp.read(int)) + fun.arguments(i) = type_id(fp.read(size_t)) + } + for var i in 0..fun.returns.size { + fun.returns(i) = type_id(fp.read(size_t)) + } + } else if (tpe.type == ClosureT) { + let fun = tpe !ClosureT + for var i in 0..fun.arguments.size { + fun.arguments(i) = type_id(fp.read(size_t)) } for var i in 0..fun.returns.size { - fun.returns(i) = type_id(fp.read(int)) + fun.returns(i) = type_id(fp.read(size_t)) } } } diff --git a/std/strings.pr b/std/strings.pr index 481043d8..51b089f0 100644 --- a/std/strings.pr +++ b/std/strings.pr @@ -786,7 +786,7 @@ export def int_to_hex_str(n: uint64, prefix: bool = true) -> Str { let digits = "0123456789ABCDEF" if n == 0 { - return "0x0" + return "0x0" if prefix else "0" } var str: StringBuffer = "" From 7dcbab71b1b2549767112543a1c33d351a98fbce Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Mon, 23 Sep 2024 17:40:52 +0200 Subject: [PATCH 08/12] Fix embedded structs --- src/compiler.pr | 98 ++++++++++++++++++++++++++++++--------------- src/toolchain.pr | 16 +++++++- src/typechecking.pr | 22 ++++++++++ std/reflection.pr | 13 ++++-- 4 files changed, 111 insertions(+), 38 deletions(-) diff --git a/src/compiler.pr b/src/compiler.pr index 6660316c..a4203671 100644 --- a/src/compiler.pr +++ b/src/compiler.pr @@ -2093,17 +2093,40 @@ def convert_to(kind: InsnKind, loc: &Value, value: Value, tpe: &typechecking::Ty return ret } -def get_embed_field(left: &typechecking::Type, right: &typechecking::Type, state: &State) -> &typechecking::StructMember { - if is_interface(left) and is_struct(right) { - if typechecking::implements(right, left, state.module, check_embed = false) { return null } + +def get_embed_field(left: &typechecking::Type, right: &typechecking::Type, state: &State) -> &Vector(&typechecking::StructMember) { + let res = vector::make(type &typechecking::StructMember) + // Early return if we already implement the interface! + if is_interface(left) and typechecking::implements(right, left, state.module, check_embed = false) { return res } + + if is_ref(right) { + // Unwrap reference + right = right.tpe + } + if is_struct(right) { + if is_struct(left) and typechecking::equals(left, right) or + is_interface(left) and typechecking::implements(right, left, state.module, check_embed = false) { + return res + } + for var field in @right.fields { - if field.is_embed and typechecking::implements(field.tpe, left, state.module, check_embed = false) { - return field + if field.is_embed { + if is_struct(left) and typechecking::equals(left, field.tpe) or + is_interface(left) and typechecking::implements(field.tpe, left, state.module, check_embed = false) { + + res.push(field) + return res + } else { + let embed = get_embed_field(left, field.tpe, state) + res.push(field) + res.add_all(embed) + return res + } } } } - return null + return res } // value gets loaded by this function @@ -2145,40 +2168,49 @@ def convert_to(loc: &Value, value: Value, tpe: &typechecking::Type, state: &Stat } let left = tpe.tpe if is_ref(tpe) else tpe let right = value.tpe.tpe if is_ref(value.tpe) else value.tpe - let embed_field = get_embed_field(left, right, state) - + let embed_field = get_embed_field(left, value.tpe, state) + // Try to convert to embedded struct / reference - if is_struct(right) and (is_struct(left) or embed_field) { - var is_embed = false - var field: StructMember - if embed_field { - is_embed = true - field = @embed_field + if is_struct(right) and ((is_struct(left) or is_interface(left)) and embed_field.length > 0) { + var elem = NO_VALUE + + // Unwrap reference on the value side + if is_ref(value.tpe) { + elem = state.extract_value(pointer(right), load_value(value, loc, state), [1], loc) } else { - for var f in @right.fields { - if f.is_embed and (equals(f.tpe, tpe) or equals(f.tpe, tpe.tpe)) { - is_embed = true - field = f - break + elem = @value.addr + } + + for var i in 0..embed_field.length { + let field = embed_field(i) + if is_ref(field.tpe) { + elem = state.load(elem.tpe.tpe, elem, loc) + elem = state.extract_value(field.tpe, elem, [field.index !int], loc) + if i < embed_field.length - 1 { + // Convert to a pointer for the next iteration + elem = state.extract_value(pointer(field.tpe), elem, [1], loc) + } else { + // Return reference as is + return elem } + } else { + elem = state.gep(pointer(field.tpe), field.tpe, elem, [make_int_value(0), make_int_value(field.index !int)], loc) } } - if is_embed { - var unwrap = load_value(value, loc, state) - // Unwrap reference on the value side - if is_ref(value.tpe) { - let _ref = state.extract_value(pointer(right), unwrap, [1], loc) - unwrap = state.load(right, _ref, loc) - } - // Extract element - var elem = state.extract_value(field.tpe, unwrap, [field.index !int], loc) - // Wrap in reference if needed - if is_ref(tpe) and not is_ref(field.tpe) { - elem = convert_value_to_ref(tpe, elem, loc, state, 1) - } - return elem + + var last_field = embed_field(embed_field.length - 1) + + let addr = elem + elem = state.load(elem.tpe.tpe, elem, loc) + elem.addr = addr + + // Wrap in reference if needed + if is_ref(tpe) and not is_ref(last_field.tpe) { + elem = convert_value_to_ref(tpe, elem, loc, state, 1) } + return elem } + if tpe.kind == value.tpe.kind and value.tpe.is_anon and typechecking::is_struct(value.tpe) { return convert_anon_to_struct(tpe, value, loc, state) } diff --git a/src/toolchain.pr b/src/toolchain.pr index 481d8f16..a62191f7 100644 --- a/src/toolchain.pr +++ b/src/toolchain.pr @@ -197,6 +197,16 @@ export type Stage = enum { BACKEND } +export type Cast = struct { + src: &typechecking::Type + dst: &typechecking::Type +} +export def hash(cast: Cast) -> int64 { + return combine_hashes(hash(cast.src), hash(cast.dst)) +} +export def == (a: Cast, b: Cast) -> bool { return a.src == b.src and a.dst == b.dst } +export def != (a: Cast, b: Cast) -> bool { return not (a == b) } + export type Module = struct { display_name: Str filename: Str @@ -218,10 +228,13 @@ export type Module = struct { imports: &Set(Str) dependants: &Set(weak &Module) // List of Type + // TODO These should be sets // This is a list of functions that are generated for dynamic dispatch dyn_dispatch_consteval: &Vector(&typechecking::Type) dyn_dispatch: &Vector(&typechecking::Type) + dyn_casts: &Set(Cast) + // This is needed to generate functions from create_destructor compiler_state: &compiler::State state: &typechecking::State @@ -302,11 +315,12 @@ export def make_module( scope = scpe, result = compiler::make_result(), code = compiler::make_block(), - imported = set::make(), + imported = set::make(Str), imports = set::make(Str), dependants = set::make(type weak &Module), dyn_dispatch_consteval = vector::make(type &typechecking::Type), dyn_dispatch = vector::make(type &typechecking::Type), + dyn_casts = set::make(Cast), unresolved = map::make(scope::Ident, type weak &scope::Value), inlay_hints = vector::make(type &parser::Node), closures = vector::make(type &scope::Value), diff --git a/src/typechecking.pr b/src/typechecking.pr index b7815e4e..c0141b3a 100644 --- a/src/typechecking.pr +++ b/src/typechecking.pr @@ -1282,6 +1282,21 @@ export def equals(a: &Type, b: &Type) -> bool { assert } +def contains(a: &Type) -> &Set(&Type) { + if is_ref_or_weak(a) { a = a.tpe } + let res = set::make(type &Type) + if not is_struct(a) { return res } + for var field in @a.fields { + if field.is_embed { + if is_struct(field.tpe) { + res.add(field.tpe) + res.add_all(contains(field.tpe)) + } + } + } + return res +} + def is_setter(mb: StructuralTypeMember) -> bool { return mb.name.starts_with("__set_") and mb.name.ends_with("__") } @@ -4154,6 +4169,13 @@ def walk_Cast(node: &parser::Node, state: &State) { } } + // Dynamic cast + if ltpe.is_ref_or_weak() { + if rtpe.is_struct() and ltpe.tpe != rtpe { + state.module.dyn_casts.add([src = ltpe, dst = rtpe] !Cast) + } + } + if not node.tpe { node.tpe = rtpe } diff --git a/std/reflection.pr b/std/reflection.pr index 34474499..866c64b5 100644 --- a/std/reflection.pr +++ b/std/reflection.pr @@ -8,11 +8,15 @@ export type Type = &interface { } export def implements(a: Type, b: InterfaceT) -> bool { - return false + return false // TODO } export def assignable(a: Type, b: Type) -> bool { - return false + return false // TODO +} + +export def contains(a: StructT, b: Type) -> bool { + return false // TODO } export def == (a: Type, b: Type) -> bool { @@ -304,8 +308,9 @@ def load_types(input: *uint8, size: size_t, num: size_t, strings: *char) { for var id in @types.keys() { let tpe = types(id) + let base = *(tpe !BaseType) let nmembers = fp.read(int) - tpe.type_members = allocate_ref(type Function, nmembers) + base.type_members = allocate_ref(type Function, nmembers) for var i in 0..nmembers { let member = [ name = make_slice(strings, fp.read(int)), @@ -324,7 +329,7 @@ def load_types(input: *uint8, size: size_t, num: size_t, strings: *char) { member.arguments = arguments member.returns = returns - tpe.type_members()(i) = member + base.type_members(i) = member } if tpe.type == StructT { From 2e8f433f41b48d73061b2d3fad6ccb32ec61f6b8 Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Sat, 28 Sep 2024 19:12:13 +0200 Subject: [PATCH 09/12] Add dynamic casts --- src/compiler.pr | 128 +++++++++++++++++++++++++++++++++++++++++++- src/toolchain.pr | 4 +- src/typechecking.pr | 42 ++++++++++----- 3 files changed, 158 insertions(+), 16 deletions(-) diff --git a/src/compiler.pr b/src/compiler.pr index a4203671..56146087 100644 --- a/src/compiler.pr +++ b/src/compiler.pr @@ -2166,10 +2166,34 @@ def convert_to(loc: &Value, value: Value, tpe: &typechecking::Type, state: &Stat return value } } + + let cast = [src = value.tpe, dst = tpe] !toolchain::Cast + if state.module.dyn_casts.contains(cast) { + let fun = state.module.dyn_casts(cast) + + var dst = cast.dst + if not is_ref(cast.dst) { + dst = pointer(dst) + } + + let dst_value = state.alloca(dst, loc) + state.call(fun.type_name, null, [value, dst_value], loc) + + if is_ref(cast.dst) { + let val = state.load(cast.dst, dst_value) + return val + } else { + let ptr = state.load(pointer(cast.dst), dst_value) + let res = state.load(cast.dst, ptr) + res.addr = ptr + return res + } + } + let left = tpe.tpe if is_ref(tpe) else tpe let right = value.tpe.tpe if is_ref(value.tpe) else value.tpe let embed_field = get_embed_field(left, value.tpe, state) - + // Try to convert to embedded struct / reference if is_struct(right) and ((is_struct(left) or is_interface(left)) and embed_field.length > 0) { var elem = NO_VALUE @@ -8062,6 +8086,105 @@ export def create_dyn_dispatch(dyn_dispatch: &Vector(&typechecking::Type), state } } +export def create_dyn_casts(state: &State) { + let dyn_casts = state.module.dyn_casts + for var cast in @dyn_casts.keys() { + let function = predeclare_function(dyn_casts(cast), state.module) + create_dyn_cast_function(function, cast, state) + consteval::const_module.result.functions(function.name) = function + } +} + +def create_dyn_cast_function(function: &Function, cast: toolchain::Cast, state: &State) { + // Setup function + function.block = make_block() + function.forward_declare = false + let previous_block = state.current_block + state.current_block = function.block + + var dst_arg = pointer(cast.dst) + if not is_ref(cast.dst) { + dst_arg = pointer(dst_arg) + } + + let src_value = [ kind = ValueKind::LOCAL, tpe = cast.src, name = "src.value"] !Value + let dst_value = [ kind = ValueKind::LOCAL, tpe = dst_arg, name = "dst.value" ] !Value + + // Extract type + let ref_tpe = state.extract_value( + typechecking::pointer(builtins::Type_), + src_value, + [2] + ) + let ref_tpe_deref = state.load(builtins::Type_, ref_tpe) + let tpe_value = state.extract_value( + typechecking::pointer(builtins::Type_), + ref_tpe_deref, + [4] + ) + let tpe_deref = state.load(builtins::Type_, tpe_value) + let tpe_id = state.extract_value(builtins::int64_, tpe_deref, [14]) + + // Switch + let switch_values = vector::make(SwitchValue) + let swtch = make_insn(InsnKind::SWITCH) + swtch.value.switch_ = [ + value = tpe_id, + switch_values = switch_values + ] !InsnSwitch + push_insn(swtch, state) + + let hashes = set::make(uint64) + let keys = map::keys(typechecking::types_map) + for var i in 0..keys.size { + let type_entry = typechecking::types_map(keys(i)) + + if not is_ref(type_entry.tpe) { continue } + let contains = typechecking::contains(type_entry.tpe) + + if not contains.contains(cast.dst) and not (cast.dst.is_ref() and contains.contains(cast.dst.tpe)) { continue } + + let hash = md5::high(md5::md5(debug::type_to_str(type_entry.tpe.tpe, full_name = true))) + if hashes.contains(hash) { continue } + hashes.add(hash) + + let if_true = make_label(state) + push_label(if_true, state) + + if cast.dst.is_ref() { + var res = convert_to(null !&Value, src_value, type_entry.tpe, state) + res = convert_to(null !&Value, res, cast.dst, state) + state.store(dst_value, res) + } else { + var res = convert_to(null !&Value, src_value, type_entry.tpe, state) + res = convert_to(null !&Value, res, cast.dst, state) + state.store(dst_value, @res.addr) + } + + state.ret(NO_VALUE) + + let svalue = [ + label_ = if_true, + value = [ kind = ValueKind::INT, i = hash, tpe = builtins::int64_ ] !Value + ] !SwitchValue + switch_values.push(svalue) + } + + let end_label = make_label(state) + push_label(end_label, state) + swtch.value.switch_.otherwise = end_label + + // TODO Abort with message! + import_cstd_function("abort", state) + state.call("abort", null, [] ![Value]) + + state.module.imported.add(function.name) + push_insn(make_insn(InsnKind::UNREACHABLE), state) + + // Reset state + state.current_block = previous_block +} + export let constructors = map::make(type &typechecking::Type) def create_constructors { var done = set::make() @@ -9617,7 +9740,7 @@ def generate_vtable_function(function: &Function, tpe: &typechecking::Type, stat ] !InsnSwitch push_insn(swtch, state) - let hashes = set::make(size_t) + let hashes = set::make(uint64) let keys = map::keys(typechecking::types_map) for var i in 0..keys.size { let type_entry = typechecking::types_map(keys(i)) @@ -10045,6 +10168,7 @@ export def compile(state: &State, is_main: bool, no_cleanup: bool = false) { // TODO This doesn't work for functions that return multiple parameters predeclare_functions(state.module) create_dyn_dispatch(state.module.dyn_dispatch, state) + create_dyn_casts(state) let ident = parser::make_identifier("__main__") ident.loc.module = state.module.module diff --git a/src/toolchain.pr b/src/toolchain.pr index a62191f7..ea0776f1 100644 --- a/src/toolchain.pr +++ b/src/toolchain.pr @@ -233,7 +233,7 @@ export type Module = struct { dyn_dispatch_consteval: &Vector(&typechecking::Type) dyn_dispatch: &Vector(&typechecking::Type) - dyn_casts: &Set(Cast) + dyn_casts: &Map(Cast, &typechecking::Type) // This is needed to generate functions from create_destructor compiler_state: &compiler::State @@ -320,7 +320,7 @@ export def make_module( dependants = set::make(type weak &Module), dyn_dispatch_consteval = vector::make(type &typechecking::Type), dyn_dispatch = vector::make(type &typechecking::Type), - dyn_casts = set::make(Cast), + dyn_casts = map::make(Cast, type &typechecking::Type), unresolved = map::make(scope::Ident, type weak &scope::Value), inlay_hints = vector::make(type &parser::Node), closures = vector::make(type &scope::Value), diff --git a/src/typechecking.pr b/src/typechecking.pr index c0141b3a..51dec7ac 100644 --- a/src/typechecking.pr +++ b/src/typechecking.pr @@ -1282,16 +1282,14 @@ export def equals(a: &Type, b: &Type) -> bool { assert } -def contains(a: &Type) -> &Set(&Type) { +export def contains(a: &Type) -> &Set(&Type) { if is_ref_or_weak(a) { a = a.tpe } let res = set::make(type &Type) if not is_struct(a) { return res } for var field in @a.fields { if field.is_embed { - if is_struct(field.tpe) { - res.add(field.tpe) - res.add_all(contains(field.tpe)) - } + res.add(field.tpe) + res.add_all(contains(field.tpe)) } } return res @@ -4141,6 +4139,12 @@ def walk_Cast(node: &parser::Node, state: &State) { var ltpe = left.tpe if not ltpe { return } + // Create necessary type entries + create_type_entry(ltpe) + if is_struct(ltpe) and is_ref(rtpe) { + create_type_entry(reference(ltpe)) + } + if left.kind == parser::NodeKind::INTEGER and is_integer(ltpe) and is_integer(rtpe) { left.tpe = rtpe @@ -4157,6 +4161,27 @@ def walk_Cast(node: &parser::Node, state: &State) { errors::errorn(left, "Invalid cast") return } + } else if ltpe.is_ref_or_weak() and + ltpe.tpe.is_interface() and + not implements(rtpe, ltpe.tpe, state.module) and + (rtpe.is_struct() or (rtpe.is_ref() and rtpe.tpe.is_struct())) { + + var dst = pointer(rtpe) + if not is_ref(rtpe) { + dst = pointer(dst) + } + + let params = vector::make(NamedParameter) + params.push([ name = "src", _tpe = ltpe ] !NamedParameter) + params.push([ name = "dst", _tpe = dst] !NamedParameter) + let fun = make_function_type_n( + parser::make_identifier("__cast"), + params, + vector::make(type &Type), + state.module) + + // Dynamic cast + state.module.dyn_casts([src = ltpe, dst = rtpe,] !Cast) = fun } else if is_struct(ltpe) or is_struct(rtpe) { if ltpe.kind != rtpe.kind and not is_ref(rtpe) { errors::errorn(left, "Invalid cast") @@ -4169,13 +4194,6 @@ def walk_Cast(node: &parser::Node, state: &State) { } } - // Dynamic cast - if ltpe.is_ref_or_weak() { - if rtpe.is_struct() and ltpe.tpe != rtpe { - state.module.dyn_casts.add([src = ltpe, dst = rtpe] !Cast) - } - } - if not node.tpe { node.tpe = rtpe } From 7ca22e1e3aa0974094e04fb4dda803b4657733e1 Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Sun, 29 Sep 2024 19:07:24 +0200 Subject: [PATCH 10/12] Work on reflection data --- src/compiler.pr | 37 +++++++++++++++++++--------------- src/toolchain.pr | 2 +- std/reflection.pr | 51 +++++++++++++++++++++++++++-------------------- 3 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/compiler.pr b/src/compiler.pr index 56146087..d4499b4c 100644 --- a/src/compiler.pr +++ b/src/compiler.pr @@ -9124,6 +9124,10 @@ def push_structural_members(tpe: &typechecking::Type, global: Value, module: &to } def do_create_type(tpe: &typechecking::Type, svalue: &scope::Value, module: &toolchain::Module, cache: &Vector(TypeEntry)) -> &Value { + if not is_stub(tpe) { + all_types.add(tpe) + } + if toolchain::no_stdlib { return NO_VALUE } if tpe.kind == typechecking::TypeKind::BOX { @@ -9489,24 +9493,24 @@ def make_global_data(name: Str, value: size_t, state: &State) { state.module.imported.add(name) } +export let all_types = set::make(type &typechecking::Type) + export def generate_reflection_data() { let data = io::make_stream() let strings = [ strings = io::make_stream(), map = map::make(size_t) ] !&UniquedStrings - for var key in @types_to_resolve.keys() { - let to_resolve = types_to_resolve(key) - generate_reflection_data_1(to_resolve.tpe, data, strings) + for var tpe in @all_types.keys() { + generate_reflection_data_1(tpe, data, strings) } - for var key in @types_to_resolve.keys() { - let to_resolve = types_to_resolve(key) - generate_reflection_data_2(to_resolve.tpe, data, strings) + for var tpe in @all_types.keys() { + generate_reflection_data_2(tpe, data, strings) } make_global_data("__reflection_data", data, toolchain::types_state) make_global_data("__reflection_data_size", data.data().size, toolchain::types_state) make_global_data("__reflection_strings", strings.strings, toolchain::types_state) - make_global_data("__reflection_num_types", types_to_resolve.size, toolchain::types_state) + make_global_data("__reflection_num_types", all_types.size, toolchain::types_state) } type UniquedStrings = struct { @@ -9524,8 +9528,6 @@ def write_zt(uniqued: &UniquedStrings, s: Str) -> int { } def generate_reflection_data_2(tpe: &typechecking::Type, data: &ByteStream, strings: &UniquedStrings) { - if tpe.kind == typechecking::TypeKind::STUB { return } - // Collect type members let type_members = vector::make(typechecking::TypeEntryMember) for var member in typechecking::iterate_member_functions(tpe) { @@ -9541,7 +9543,7 @@ def generate_reflection_data_2(tpe: &typechecking::Type, data: &ByteStream, stri data.write(member.function.parameter_t.length !int) for var arg in member.function.parameter_t { - data.write(arg.tpe.hash) // TODO Do we want function argument names maybe? + data.write(arg.tpe.hash if arg.tpe else 0) // TODO Do we want function argument names maybe? } data.write(member.function.return_t.length !int) for var t in member.function.return_t { @@ -9566,7 +9568,7 @@ def generate_reflection_data_2(tpe: &typechecking::Type, data: &ByteStream, stri case typechecking::TypeKind::POINTER, typechecking::TypeKind::REFERENCE, typechecking::TypeKind::WEAK_REF, typechecking::TypeKind::ARRAY, typechecking::TypeKind::STATIC_ARRAY - data.write(tpe.tpe.hash) + data.write(tpe.tpe.hash if tpe.tpe else 0) case typechecking::TypeKind::TUPLE for var t in tpe.return_t { data.write(t.hash) @@ -9586,12 +9588,12 @@ def generate_reflection_data_2(tpe: &typechecking::Type, data: &ByteStream, stri } def generate_reflection_data_1(tpe: &typechecking::Type, data: &ByteStream, strings: &UniquedStrings) { - if tpe.kind == typechecking::TypeKind::STUB { return } - data.write(tpe.kind) // This does rely on the values beind the same data.write(strings.write_zt(tpe.name)) - data.write(strings.write_zt(tpe.module.module)) + data.write(strings.write_zt(tpe.module.module if tpe.module else to_str(""))) data.write(tpe.hash) + + print(tpe.kind, " ", tpe.name, " ", (tpe.module.module if tpe.module else to_str("")), " ", tpe.hash, "\n") switch tpe.kind { case typechecking::TypeKind::WORD @@ -9604,12 +9606,15 @@ def generate_reflection_data_1(tpe: &typechecking::Type, data: &ByteStream, stri case typechecking::TypeKind::STRUCT, typechecking::TypeKind::UNION data.write(tpe.size !int) data.write(tpe.align !int) - data.write(tpe.fields.size) + data.write(tpe.fields.size !int) + + print("\tsize: ", tpe.size, " align: ", tpe.align, " members: ", tpe.fields.size, "\n") for var i in 0..tpe.fields.size { let field = tpe.fields(i) let member = tpe.field_types(field.index) data.write(strings.write_zt(field.name)) data.write(member.offset !int) + print("\tname: ", field.name, " offset: ", member.offset, "\n") } case typechecking::TypeKind::STATIC_ARRAY data.write(tpe.size) @@ -9623,7 +9628,7 @@ def generate_reflection_data_1(tpe: &typechecking::Type, data: &ByteStream, stri data.write(tpe.align !int) data.write(not tpe.unsig) case typechecking::TypeKind::STRUCTURAL - data.write(tpe.members.length) + data.write(tpe.members.length !int) for var member in tpe.members { data.write(strings.write_zt(member.name)) data.write(member.parameter_t.length !int) diff --git a/src/toolchain.pr b/src/toolchain.pr index ea0776f1..e6b6decf 100644 --- a/src/toolchain.pr +++ b/src/toolchain.pr @@ -840,8 +840,8 @@ export def compile_main_file(filename: String) { } debug::trace("Resolving types") - compiler::generate_reflection_data() compiler::resolve_types() // TODO Remove this function + compiler::generate_reflection_data() debug::trace("Creating builtin functions") compiler::create_builtin_functions() diff --git a/std/reflection.pr b/std/reflection.pr index 866c64b5..109ca374 100644 --- a/std/reflection.pr +++ b/std/reflection.pr @@ -192,7 +192,7 @@ import io import map import std -var types: &Map(size_t, Type) +var types: &Map(uint64, Type) load_types(__reflection_data, @__reflection_data_size, @__reflection_num_types, __reflection_strings) @@ -206,7 +206,7 @@ def load_types(input: *uint8, size: size_t, num: size_t, strings: *char) { defer close(fp) if not types { - types = map::make(size_t, Type) + types = map::make(uint64, Type) } var index = 0 @@ -214,7 +214,9 @@ def load_types(input: *uint8, size: size_t, num: size_t, strings: *char) { let kind = fp.read(TypeKind) let name = make_slice(strings, fp.read(int)) let module = make_slice(strings, fp.read(int)) - let id = fp.read(size_t) + let id = fp.read(uint64) + + print(kind, " ", name, " ", module, " ", id, "\n") switch kind { case TypeKind::BOOL @@ -234,8 +236,13 @@ def load_types(input: *uint8, size: size_t, num: size_t, strings: *char) { let size = fp.read(int) let align = fp.read(int) let members = zero_allocate(Field, fp.read(int)) + + print("\tsize: ", size, " align: ", align, " members: ", members.size, "\n") + for var i in 0..members.size { - members(i) = [ name = make_slice(strings, fp.read(int)), offset = fp.read(int) ] !Field + let field = [ name = make_slice(strings, fp.read(int)), offset = fp.read(int) ] !Field + members(i) = field + print("\tname: ", field.name, " offset: ", field.offset, "\n") } if kind == TypeKind::STRUCT { @@ -320,11 +327,11 @@ def load_types(input: *uint8, size: size_t, num: size_t, strings: *char) { let arguments = allocate_ref(type weak Type, fp.read(int)) for var i in 0..arguments.size { - arguments(i) = type_id(fp.read(size_t)) + arguments(i) = type_id(fp.read(uint64)) } let returns = allocate_ref(type weak Type, fp.read(int)) for var i in 0..returns.size { - returns(i) = type_id(fp.read(size_t)) + returns(i) = type_id(fp.read(uint64)) } member.arguments = arguments @@ -336,65 +343,65 @@ def load_types(input: *uint8, size: size_t, num: size_t, strings: *char) { let rec = tpe !StructT for var i in 0..rec.members.size { let f = *rec.members(i) - f.tpe = type_id(fp.read(size_t)) + f.tpe = type_id(fp.read(uint64)) } } else if tpe.type == UnionT { let rec = tpe !UnionT for var i in 0..rec.members.size { let f = *rec.members(i) - f.tpe = type_id(fp.read(size_t)) + f.tpe = type_id(fp.read(uint64)) } } else if tpe.type == InterfaceT { let intf = tpe !InterfaceT for var i in 0..intf.members.size { let m = *intf.members(i) for var i in 0..m.arguments.size { - m.arguments(i) = type_id(fp.read(size_t)) + m.arguments(i) = type_id(fp.read(uint64)) } for var i in 0..m.returns.size { - m.returns(i) = type_id(fp.read(size_t)) + m.returns(i) = type_id(fp.read(uint64)) } } } else if tpe.type == PointerT { - (tpe !PointerT).tpe = type_id(fp.read(size_t)) + (tpe !PointerT).tpe = type_id(fp.read(uint64)) } else if tpe.type == ReferenceT { - (tpe !ReferenceT).tpe = type_id(fp.read(size_t)) + (tpe !ReferenceT).tpe = type_id(fp.read(uint64)) } else if tpe.type == WeakReferenceT { - (tpe !WeakReferenceT).tpe = type_id(fp.read(size_t)) + (tpe !WeakReferenceT).tpe = type_id(fp.read(uint64)) } else if tpe.type == ArrayT { - (tpe !ArrayT).tpe = type_id(fp.read(size_t)) + (tpe !ArrayT).tpe = type_id(fp.read(uint64)) } else if tpe.type == StaticArrayT { - (tpe !StaticArrayT).tpe = type_id(fp.read(size_t)) + (tpe !StaticArrayT).tpe = type_id(fp.read(uint64)) } else if (tpe.type == VariantT) { let vnt = tpe !VariantT for var i in 0..vnt.variants.size { - vnt.variants(i) = type_id(fp.read(size_t)) + vnt.variants(i) = type_id(fp.read(uint64)) } } else if (tpe.type == TupleT) { let tuple = tpe !TupleT for var i in 0..tuple.elements.size { - tuple.elements(i) = type_id(fp.read(size_t)) + tuple.elements(i) = type_id(fp.read(uint64)) } } else if (tpe.type == FunctionT) { let fun = tpe !FunctionT for var i in 0..fun.arguments.size { - fun.arguments(i) = type_id(fp.read(size_t)) + fun.arguments(i) = type_id(fp.read(uint64)) } for var i in 0..fun.returns.size { - fun.returns(i) = type_id(fp.read(size_t)) + fun.returns(i) = type_id(fp.read(uint64)) } } else if (tpe.type == ClosureT) { let fun = tpe !ClosureT for var i in 0..fun.arguments.size { - fun.arguments(i) = type_id(fp.read(size_t)) + fun.arguments(i) = type_id(fp.read(uint64)) } for var i in 0..fun.returns.size { - fun.returns(i) = type_id(fp.read(size_t)) + fun.returns(i) = type_id(fp.read(uint64)) } } } } -def type_id(index: size_t) -> Type { +def type_id(index: uint64) -> Type { return types(index) } \ No newline at end of file From 3e1a630fba8d8943391ff977d48d0240799078f9 Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Sun, 29 Sep 2024 19:15:10 +0200 Subject: [PATCH 11/12] Unroll StringBuffer destructor --- std/strings.pr | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/std/strings.pr b/std/strings.pr index 51b089f0..fddb4ab6 100644 --- a/std/strings.pr +++ b/std/strings.pr @@ -151,6 +151,27 @@ export type StringBuffer = struct { prev: &StringBuffer data: Str offset: size_t + blocked: bool // This is annoying but needed to free the linked list without recursion +} + +export def destruct(this: *StringBuffer) -> bool { + __destruct__(*this.data) + if this.blocked { return false } + + var prev = this.prev + loop { + if not prev { return false } + prev.blocked = true + let old = prev.prev + let prev2: weak &StringBuffer = prev + __destruct__(*prev) + prev = old + if prev2 { + prev2.blocked = false + return false + } + } + return false } export def length(s: StringBuffer) -> size_t { @@ -402,7 +423,7 @@ export def >= (s1: IString, s2: IString) -> bool { } export def make_slice(s: *char, offset: size_t) -> StringSlice { - return [ parent = null, count = cstd::strlen(s), data = s, offset = offset ] !StringSlice + return [ parent = null, count = cstd::strlen(s ++ offset), data = s, offset = offset ] !StringSlice } export implicit def to_slice(s: [char]) -> StringSlice { From 9b2702b40f0fdff2a07db0416291eb793ebbdcac Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Sat, 12 Oct 2024 14:31:41 +0200 Subject: [PATCH 12/12] Fix type names --- src/debug.pr | 1 + src/scope.pr | 10 +++++++--- src/typechecking.pr | 35 ++++++++++++++++++++--------------- std/reflection.pr | 2 +- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/debug.pr b/src/debug.pr index 7e6c3ec8..43e88488 100644 --- a/src/debug.pr +++ b/src/debug.pr @@ -834,6 +834,7 @@ export def type_to_str(tpe: &typechecking::Type, full_name: bool = false) -> Str if not tpe { return "(none)" } if tpe.tc_tpe { return tc_args_to_string(tpe, full_name) } if not full_name and tpe.name { return tpe.name } + if full_name and tpe.type_name { return tpe.type_name } switch tpe.kind !int { case typechecking::TypeKind::BOX return "Box<" + type_to_str(tpe.wk, full_name) + ">" diff --git a/src/scope.pr b/src/scope.pr index b7f94133..6db376f0 100644 --- a/src/scope.pr +++ b/src/scope.pr @@ -963,7 +963,10 @@ export def generate_function(scope: &Scope, node: &parser::Node, parameter_t: &V let fun = has_function(scope, tpe) if fun { return fun } - let value = create_function_without_checking(scope, name_node, stpe.share, tpe) + // TODO The share is export for now but maybe we want to make sure that it gets the same share as the first argument + // The problem is that tpe.share doesn't work when you define it like this: + // export type X = &interface { ... } and tpe.share is a bit of a hack anyway. + let value = create_function_without_checking(scope, name_node, parser::ShareMarker::EXPORT, tpe) value.identifier = name_node value.is_generated = true @@ -1322,9 +1325,10 @@ export def get_function( parameter_t: &Vector(typechecking::NamedParameter), dry_run: bool, force_compile: bool = true, - only_function: bool = false) -> &Value { + only_function: bool = false, + context: &Scope = null) -> &Value { - let _, value = get_function_check(scope, id, parameter_t, dry_run, force_compile, only_function) + let _, value = get_function_check(scope, id, parameter_t, dry_run, force_compile, only_function, context = context) return value } diff --git a/src/typechecking.pr b/src/typechecking.pr index 935f6fff..6e53fda6 100644 --- a/src/typechecking.pr +++ b/src/typechecking.pr @@ -1081,7 +1081,7 @@ export def create_type_entry(tpe: &Type, exported: bool, entry: &Type, module: & // TODO Use a set for this for var i in 0..vector::length(type_entry.functions) { let fun = type_entry.functions(i) - if fun.function.type_name == entry.type_name { + if fun.function.name == entry.name and fun.function._defmodule == entry._defmodule and equals(fun.function, entry) { if overwrite { type_entry.functions(i) = member_function } return false } @@ -2485,11 +2485,11 @@ export def generate_concrete_functions(type_constructor: &Type, tpe: &Type, stat let pars = vector::make(NamedParameter) pars.push([ _tpe = tpe ] !NamedParameter) - replace_type_defs(ftpe.parameter_t, ftpe.return_t, pars, null, state.module) - ftpe.type_name = mangle_function_name(append_module(ftpe.name, ftpe.module.module), ftpe.parameter_t, context = ftpe.context) + replace_type_defs(ftpe.parameter_t, ftpe.return_t, pars, null, state.get_context()) + ftpe.type_name = mangle_function_name(append_module(ftpe.name, ftpe.module.module), ftpe.parameter_t, context = state.get_context()) if not is_polymorph(ftpe) { - let new_entry = create_type_entry(tpe, function.exported, ftpe, function.module) + let new_entry = create_type_entry(tpe, function.exported, ftpe, state.get_context()) if new_entry { let score, value = scope::get_function_check(state.scope, parser::make_identifier(ftpe.name), pars, false, true, force_compile = true, context = state.context) } @@ -3583,7 +3583,7 @@ def walk_Assign(node: &parser::Node, state: &State) { args.push([ _tpe = rtpe ] !NamedParameter) let ident = parser::make_identifier("__set_" + parser::identifier_to_str(name) + "__") - let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static) + let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static, context = state.context) if fun { compile_function(*fun, state.scope, args) } @@ -3862,7 +3862,7 @@ def make_function_call(node: &parser::Node, ident: &parser::Node, args: &Vector( def convert_to_call(node: &parser::Node, name: Str, args: &Vector(NamedParameter), state: &State) { let ident = parser::make_identifier(name) - let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static) + let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static, context = state.context) if fun { compile_function(*fun, state.scope, args) let parent = node.parent @@ -3877,7 +3877,7 @@ def convert_to_call(node: &parser::Node, name: Str, args: &Vector(NamedParameter def convert_to_icall(node: &parser::Node, name: Str, args: &Vector(NamedParameter), state: &State) { let ident = parser::make_identifier(name) - let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static) + let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static, context = state.context) if fun { compile_function(*fun, state.scope, args) @@ -5069,7 +5069,7 @@ def check_for_apply_and_update(node: &parser::Node, left: &parser::Node, argumen vector::insert(args, 0, [ _tpe = tpe ] !NamedParameter) let ident = parser::make_identifier("apply") - let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static) + let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static, context = state.context) if fun { vector::insert(node.value.func_call.args, 0, left) @node = @make_function_call(node, ident, node.value.func_call.args) @@ -5086,7 +5086,7 @@ def check_for_apply_and_update(node: &parser::Node, left: &parser::Node, argumen args.push([ _tpe = value.tpe ] !NamedParameter) let ident = parser::make_identifier("update") - let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static) + let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static, context = state.context) if fun { let args = vector::copy(node.value.func_call.args) vector::insert(args, 0, left) @@ -5140,7 +5140,7 @@ def check_for_apply_and_update(node: &parser::Node, left: &parser::Node, argumen } def access_check(function: &scope::Value, node: &parser::Node, state: &State) -> bool { - return not (function and (function.share !int & parser::ShareMarker::EXPORT !int == 0) and function.module != node.scope.module and function.module != state.context.module) + return not (function and (function.share !int & parser::ShareMarker::EXPORT !int == 0) and function.module != node.scope.module and function.module != state.get_context()) } export def walk_Call(node: &parser::Node, dry_run: bool, state: &State) -> bool { @@ -5574,7 +5574,7 @@ def wrap_iterable(expr: &parser::Node, state: &State) -> &parser::Node { let args = vector::make(NamedParameter) args.push([ _tpe = expr.tpe ] !NamedParameter) let ident = parser::make_identifier("iterate") - let fun = scope::get_function(state.scope, ident, args, true) + let fun = scope::get_function(state.scope, ident, args, true, context = state.context) if fun { let args = vector::make(type &parser::Node) args.push(expr) @@ -5718,7 +5718,7 @@ def walk_MemberAccess_ucs(node: &parser::Node, state: &State) -> bool { _tpe = (@left).tpe ] !NamedParameter) - let function = scope::get_function(node.scope, right, parameter_t, false, false, only_function = true) + let function = scope::get_function(node.scope, right, parameter_t, false, false, only_function = true, context = state.context) if not function { return false } if node.parent.kind == parser::NodeKind::ASSIGN and node.parent.value.assign.right.index_of(node) == -1 { return true @@ -6398,6 +6398,10 @@ export def walk_Def_with_type_argument(node: &parser::Node, parameter_t: &Vector node = parser::deep_copy_node(node) node.value.def_.function = null node.value.def_.is_generic_instance = true + + let prev_context = state.context + state.context = context + node.tpe = lookup_parameters(node, state) // Unbox @@ -6419,7 +6423,10 @@ export def walk_Def_with_type_argument(node: &parser::Node, parameter_t: &Vector super_scope.parent = context }*/ - if not node.scope { return old_node } + if not node.scope { + state.context = prev_context + return old_node + } node.inner_scope = scope::enter_function_scope(node.scope) let share = node.value.def_.share @@ -6499,8 +6506,6 @@ export def walk_Def_with_type_argument(node: &parser::Node, parameter_t: &Vector create_type_entry(first_param.tpe, (share !int & parser::ShareMarker::EXPORT !int) != 0, tpe, state.module, true) } - let prev_context = state.context - state.context = context walk_Def(node, state, polymorph = true) state.context = prev_context diff --git a/std/reflection.pr b/std/reflection.pr index ac31d3e4..21c16e62 100644 --- a/std/reflection.pr +++ b/std/reflection.pr @@ -426,7 +426,7 @@ def load_types(input: *uint8, size: size_t, num: size_t, strings: *char) { } } -def type_id(id: uint64) -> Type { +export def type_id(id: uint64) -> Type { if id == 0 { return null } return types(id) } \ No newline at end of file