hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 437c7d0464620a00e28c4e825468ff3f34ee5a6c
parent 167d22e3038f3166a1aead5a51df3cba4907264f
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sun,  3 Oct 2021 11:37:01 +0200

all: updates for reflection support

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mfnmatch/+test.ha | 7-------
Mhare/ast/type.ha | 2+-
Mhare/types/+test.ha | 4++--
Mhare/types/hash.ha | 30++++++++++++++++--------------
Mhare/types/types.ha | 2+-
Art/types.ha | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Art/types_arch+aarch64.ha | 34++++++++++++++++++++++++++++++++++
Art/types_arch+riscv64.ha | 34++++++++++++++++++++++++++++++++++
Art/types_arch+x86_64.ha | 34++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 4++++
Mstdlib.mk | 8++++++++
Atypes/reflect.ha | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atypes/util.ha | 42++++++++++++++++++++++++++++++++++++++++++
13 files changed, 454 insertions(+), 25 deletions(-)

diff --git a/fnmatch/+test.ha b/fnmatch/+test.ha @@ -1,10 +1,3 @@ -// TODO: remove this after the forward references fix -export type flags = enum uint { - PATHNAME = 1u << 0, - NOESCAPE = 1u << 1, - PERIOD = 1u << 2, -}; - type testcase = (str, str, bool, []flags); const testcases: [_]testcase = [ diff --git a/hare/ast/type.ha b/hare/ast/type.ha @@ -9,7 +9,7 @@ export type alias_type = struct { // A built-in primitive type (int, bool, str, etc). export type builtin_type = enum { BOOL, CHAR, F32, F64, FCONST, I16, I32, I64, I8, ICONST, INT, NULL, - RUNE, SIZE, STR, U16, U32, U64, U8, UINT, UINTPTR, VOID, + RUNE, SIZE, STR, TYPE, U16, U32, U64, U8, UINT, UINTPTR, VOID, }; // An enumeration field (and optional value). diff --git a/hare/types/+test.ha b/hare/types/+test.ha @@ -328,6 +328,6 @@ fn resolve( let t = htype.repr as tagged; assert(len(t) == 3); assert(t[0].repr as builtin == builtin::INT); - assert(t[1].repr as builtin == builtin::VOID); - assert(t[2].repr as builtin == builtin::STR); + assert(t[1].repr as builtin == builtin::STR); + assert(t[2].repr as builtin == builtin::VOID); }; diff --git a/hare/types/hash.ha b/hare/types/hash.ha @@ -6,9 +6,9 @@ use fmt; // Keep ordered with respect to bootstrap harec:include/types.h type storage = enum u8 { - BOOL, CHAR, ENUM, F32, F64, FCONST, I16, I32, I64, I8, ICONST, INT, - NULL, RUNE, SIZE, U16, U32, U64, U8, UINT, UINTPTR, VOID, ALIAS, ARRAY, - FUNCTION, POINTER, SLICE, STRING, STRUCT, TAGGED, TUPLE, UNION, + BOOL, CHAR, ENUM, F32, F64, I16, I32, I64, I8, INT, NULL, RUNE, SIZE, + STRING, U16, U32, U64, U8, UINT, UINTPTR, VOID, TYPE, ALIAS, ARRAY, + FUNCTION, POINTER, SLICE, STRUCT, TAGGED, TUPLE, UNION, }; fn builtin_storage(b: builtin) u8 = { @@ -39,6 +39,8 @@ fn builtin_storage(b: builtin) u8 = { return storage::SIZE; case builtin::STR => return storage::STRING; + case builtin::TYPE => + return storage::TYPE; case builtin::U16 => return storage::U16; case builtin::U32 => @@ -167,7 +169,7 @@ export fn hash(t: *_type) u32 = { flags = flags::NONE, repr = builtin::STR, }; - assert(hash(&sample) == 3350498318); + assert(hash(&sample) == 2206074632); let sample = _type { flags = flags::NONE, @@ -191,7 +193,7 @@ export fn hash(t: *_type) u32 = { flags = flags::NONE, repr = builtin::INT, }; - assert(hash(&_int) == 1737287038); + assert(hash(&_int) == 461893804); let sample = _type { flags = flags::NONE, @@ -200,7 +202,7 @@ export fn hash(t: *_type) u32 = { flags = pointer_flags::NONE, }, }; - assert(hash(&sample) == 1453816944); + assert(hash(&sample) == 934607058); let sample = _type { flags = flags::NONE, @@ -209,7 +211,7 @@ export fn hash(t: *_type) u32 = { flags = pointer_flags::NULLABLE, }, }; - assert(hash(&sample) == 1467299071); + assert(hash(&sample) == 241140837); let sample = _type { flags = flags::NONE, @@ -229,7 +231,7 @@ export fn hash(t: *_type) u32 = { ], }, }; - assert(hash(&sample) == 3615159786); + assert(hash(&sample) == 381928193); let sample = _type { flags = flags::NONE, @@ -248,7 +250,7 @@ export fn hash(t: *_type) u32 = { }, ], }; - assert(hash(&sample) == 4136455899); + assert(hash(&sample) == 1705302210); let sample = _type { flags = flags::NONE, @@ -257,7 +259,7 @@ export fn hash(t: *_type) u32 = { member = &_int, }, }; - assert(hash(&sample) == 2972813387); + assert(hash(&sample) == 164621237); let _uint = _type { flags = flags::NONE, @@ -276,7 +278,7 @@ export fn hash(t: *_type) u32 = { &_void, ]: tagged, }; - assert(hash(&sample) == 3951208153); + assert(hash(&sample) == 204212843); let sample = _type { flags = flags::NONE, @@ -289,7 +291,7 @@ export fn hash(t: *_type) u32 = { ], }, }; - assert(hash(&sample) == 1838699449); + assert(hash(&sample) == 2700673451); let sample = _type { flags = flags::NONE, @@ -300,11 +302,11 @@ export fn hash(t: *_type) u32 = { params = [&_uint, &_int], }, }; - assert(hash(&sample) == 1706980510); + assert(hash(&sample) == 3530404333); let sample = _type { flags = flags::NONE, repr = &_void: slice, }; - assert(hash(&sample) == 3143795781); + assert(hash(&sample) == 296987721); }; diff --git a/hare/types/types.ha b/hare/types/types.ha @@ -18,7 +18,7 @@ export type array = struct { export type builtin = enum u8 { // Keep me consistent with ast::builtin BOOL, CHAR, F32, F64, I16, I32, I64, I8, INT, NULL, RUNE, SIZE, STR, - U16, U32, U64, U8, UINT, UINTPTR, VOID, + TYPE, U16, U32, U64, U8, UINT, UINTPTR, VOID, }; // An enum type, e.g. enum { FOO = 0 } diff --git a/rt/types.ha b/rt/types.ha @@ -0,0 +1,147 @@ +// This defines a subset of types/reflect.ha so we can declare static typeinfo +// structures for builtins. +type types::typeinfo = struct { + id: uint, + sz: size, + al: size, + flags: types::flags, + repr: types::repr, +}; + +type types::flags = enum uint { + NONE = 0, + CONST = 1 << 0, + ERROR = 1 << 1, +}; + +type types::repr = (types::alias | types::array | types::builtin + | types::enum_ | types::func | types::pointer | types::slice_repr + | types::struct_union | types::tagged | types::tuple); + +type types::alias = struct { + ident: []str, + secondary: type, +}; + +type types::array = struct { + length: size, + members: type, +}; + +type types::builtin = enum uint { + BOOL, CHAR, ENUM, F32, F64, I16, I32, I64, I8, INT, NULL, RUNE, SIZE, + STR, U16, U32, U64, U8, UINT, UINTPTR, VOID, TYPE, +}; + +type types::enum_ = struct { + storage: types::builtin, + values: [](str, u64), +}; + +type types::variadism = enum { + NONE, + C, + HARE, +}; + +type types::func_flags = enum uint { + NORETURN = 1 << 0, +}; + +type types::func = struct { + result: type, + variadism: types::variadism, + flags: types::func_flags, + params: []type, +}; + +type types::pointer_flags = enum uint { + NONE = 0, + NULLABLE = 1 << 0, +}; + +type types::pointer = struct { + referent: type, + flags: types::pointer_flags, +}; + +type types::slice_repr = type; + +type types::struct_kind = enum { + STRUCT, + UNION, +}; + +type types::struct_union = struct { + kind: types::struct_kind, + fields: []types::struct_field, +}; + +type types::struct_field = struct { + name: str, + offs: size, + type_: type, +}; + +type types::tagged = []type; + +type types::tuple = []types::tuple_value; + +type types::tuple_value = struct { + offs: size, + type_: type, +}; + +export const @hidden builtin_char: types::typeinfo = types::typeinfo { + id = 3950255460, + sz = 1, al = 1, flags = 0, + repr = types::builtin::CHAR, +}, @hidden builtin_f32: types::typeinfo = types::typeinfo { + id = 930681398, + sz = 4, al = 4, flags = 0, + repr = types::builtin::F32, +}, @hidden builtin_f64: types::typeinfo = types::typeinfo { + id = 2037165609, + sz = 8, al = 8, flags = 0, + repr = types::builtin::F64, +}, @hidden builtin_i8: types::typeinfo = types::typeinfo { + id = 461893804, + sz = 1, al = 1, flags = 0, + repr = types::builtin::I8, +}, @hidden builtin_i16: types::typeinfo = types::typeinfo { + id = 3312558843, + sz = 2, al = 2, flags = 0, + repr = types::builtin::I16, +}, @hidden builtin_i32: types::typeinfo = types::typeinfo { + id = 2674862226, + sz = 4, al = 4, flags = 0, + repr = types::builtin::I32, +}, @hidden builtin_i64: types::typeinfo = types::typeinfo { + id = 1099590421, + sz = 8, al = 8, flags = 0, + repr = types::builtin::I64, +}, @hidden builtin_rune: types::typeinfo = types::typeinfo { + id = 2206074632, + sz = 4, al = 4, flags = 0, + repr = types::builtin::RUNE, +}, @hidden builtin_u8: types::typeinfo = types::typeinfo { + id = 3181589295, + sz = 1, al = 1, flags = 0, + repr = types::builtin::U8, +}, @hidden builtin_u16: types::typeinfo = types::typeinfo { + id = 3481467866, + sz = 2, al = 2, flags = 0, + repr = types::builtin::U16, +}, @hidden builtin_u32: types::typeinfo = types::typeinfo { + id = 1906196061, + sz = 4, al = 4, flags = 0, + repr = types::builtin::U32, +}, @hidden builtin_u64: types::typeinfo = types::typeinfo { + id = 1268499444, + sz = 8, al = 8, flags = 0, + repr = types::builtin::U64, +}, @hidden builtin_void: types::typeinfo = types::typeinfo { + id = 3012680272, + sz = 0, al = 0, flags = 0, + repr = types::builtin::VOID, +}; diff --git a/rt/types_arch+aarch64.ha b/rt/types_arch+aarch64.ha @@ -0,0 +1,34 @@ +export const @hidden builtin_int: types::typeinfo = types::typeinfo { + id = 1737287038, + sz = 4, al = 4, flags = 0, + repr = types::builtin::INT, +}, @hidden builtin_uint: types::typeinfo = types::typeinfo { + id = 2543892678, + sz = 4, al = 4, flags = 0, + repr = types::builtin::UINT, +}, @hidden builtin_bool: types::typeinfo = types::typeinfo { + id = 292984781, + sz = 4, al = 4, flags = 0, + repr = types::builtin::BOOL, +}, @hidden builtin_size: types::typeinfo = types::typeinfo { + id = 4119164483, + sz = 8, al = 8, flags = 0, + repr = types::builtin::SIZE, +}, @hidden builtin_uintptr: types::typeinfo = types::typeinfo { + id = 3650376889, + sz = 8, al = 8, flags = 0, + repr = types::builtin::UINTPTR, +}, @hidden builtin_str: types::typeinfo = types::typeinfo { + id = 3350498318, + sz = 24, al = 8, flags = 0, + repr = types::builtin::STR, +}, @hidden builtin_null: types::typeinfo = types::typeinfo { + id = 2843771249, + sz = 8, al = 8, flags = 0, + repr = types::builtin::NULL, +}, @hidden builtin_type: types::typeinfo = types::typeinfo { + // TODO: This type ID is wrong + id = 3181589295, + sz = 8, al = 8, flags = 0, + repr = types::builtin::TYPE, +}; diff --git a/rt/types_arch+riscv64.ha b/rt/types_arch+riscv64.ha @@ -0,0 +1,34 @@ +export const @hidden builtin_int: types::typeinfo = types::typeinfo { + id = 1737287038, + sz = 4, al = 4, flags = 0, + repr = types::builtin::INT, +}, @hidden builtin_uint: types::typeinfo = types::typeinfo { + id = 2543892678, + sz = 4, al = 4, flags = 0, + repr = types::builtin::UINT, +}, @hidden builtin_bool: types::typeinfo = types::typeinfo { + id = 292984781, + sz = 4, al = 4, flags = 0, + repr = types::builtin::BOOL, +}, @hidden builtin_size: types::typeinfo = types::typeinfo { + id = 4119164483, + sz = 8, al = 8, flags = 0, + repr = types::builtin::SIZE, +}, @hidden builtin_uintptr: types::typeinfo = types::typeinfo { + id = 3650376889, + sz = 8, al = 8, flags = 0, + repr = types::builtin::UINTPTR, +}, @hidden builtin_str: types::typeinfo = types::typeinfo { + id = 3350498318, + sz = 24, al = 8, flags = 0, + repr = types::builtin::STR, +}, @hidden builtin_null: types::typeinfo = types::typeinfo { + id = 2843771249, + sz = 8, al = 8, flags = 0, + repr = types::builtin::NULL, +}, @hidden builtin_type: types::typeinfo = types::typeinfo { + // TODO: This type ID is wrong + id = 3181589295, + sz = 8, al = 8, flags = 0, + repr = types::builtin::TYPE, +}; diff --git a/rt/types_arch+x86_64.ha b/rt/types_arch+x86_64.ha @@ -0,0 +1,34 @@ +export const @hidden builtin_int: types::typeinfo = types::typeinfo { + id = 1737287038, + sz = 4, al = 4, flags = 0, + repr = types::builtin::INT, +}, @hidden builtin_uint: types::typeinfo = types::typeinfo { + id = 2543892678, + sz = 4, al = 4, flags = 0, + repr = types::builtin::UINT, +}, @hidden builtin_bool: types::typeinfo = types::typeinfo { + id = 292984781, + sz = 4, al = 4, flags = 0, + repr = types::builtin::BOOL, +}, @hidden builtin_size: types::typeinfo = types::typeinfo { + id = 4119164483, + sz = 8, al = 8, flags = 0, + repr = types::builtin::SIZE, +}, @hidden builtin_uintptr: types::typeinfo = types::typeinfo { + id = 3650376889, + sz = 8, al = 8, flags = 0, + repr = types::builtin::UINTPTR, +}, @hidden builtin_str: types::typeinfo = types::typeinfo { + id = 3350498318, + sz = 24, al = 8, flags = 0, + repr = types::builtin::STR, +}, @hidden builtin_null: types::typeinfo = types::typeinfo { + id = 2843771249, + sz = 8, al = 8, flags = 0, + repr = types::builtin::NULL, +}, @hidden builtin_type: types::typeinfo = types::typeinfo { + // TODO: This type ID is wrong + id = 3181589295, + sz = 8, al = 8, flags = 0, + repr = types::builtin::TYPE, +}; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -31,6 +31,7 @@ gensrcs_rt() { '$(PLATFORM)/socket.ha' \ '$(ARCH)/jmp.ha' \ '$(ARCH)/backtrace.ha' \ + 'types_arch$(ARCH).ha' \ ensure.ha \ jmp.ha \ malloc.ha \ @@ -38,6 +39,7 @@ gensrcs_rt() { memmove.ha \ memset.ha \ strcmp.ha \ + types.ha \ $* } @@ -813,6 +815,8 @@ types() { gen_srcs types \ limits.ha \ classes.ha \ + reflect.ha \ + util.ha \ 'arch$(ARCH).ha' gen_ssa types } diff --git a/stdlib.mk b/stdlib.mk @@ -18,6 +18,7 @@ stdlib_rt_srcs= \ $(STDLIB)/rt/$(PLATFORM)/socket.ha \ $(STDLIB)/rt/$(ARCH)/jmp.ha \ $(STDLIB)/rt/$(ARCH)/backtrace.ha \ + $(STDLIB)/rt/types_arch$(ARCH).ha \ $(STDLIB)/rt/ensure.ha \ $(STDLIB)/rt/jmp.ha \ $(STDLIB)/rt/malloc.ha \ @@ -25,6 +26,7 @@ stdlib_rt_srcs= \ $(STDLIB)/rt/memmove.ha \ $(STDLIB)/rt/memset.ha \ $(STDLIB)/rt/strcmp.ha \ + $(STDLIB)/rt/types.ha \ $(STDLIB)/rt/abort.ha \ $(STDLIB)/rt/start.ha @@ -1139,6 +1141,8 @@ $(HARECACHE)/time/time.ssa: $(stdlib_time_srcs) $(stdlib_rt) $(stdlib_linux_vdso stdlib_types_srcs= \ $(STDLIB)/types/limits.ha \ $(STDLIB)/types/classes.ha \ + $(STDLIB)/types/reflect.ha \ + $(STDLIB)/types/util.ha \ $(STDLIB)/types/arch$(ARCH).ha $(HARECACHE)/types/types.ssa: $(stdlib_types_srcs) $(stdlib_rt) @@ -1242,6 +1246,7 @@ testlib_rt_srcs= \ $(STDLIB)/rt/$(PLATFORM)/socket.ha \ $(STDLIB)/rt/$(ARCH)/jmp.ha \ $(STDLIB)/rt/$(ARCH)/backtrace.ha \ + $(STDLIB)/rt/types_arch$(ARCH).ha \ $(STDLIB)/rt/ensure.ha \ $(STDLIB)/rt/jmp.ha \ $(STDLIB)/rt/malloc.ha \ @@ -1249,6 +1254,7 @@ testlib_rt_srcs= \ $(STDLIB)/rt/memmove.ha \ $(STDLIB)/rt/memset.ha \ $(STDLIB)/rt/strcmp.ha \ + $(STDLIB)/rt/types.ha \ $(STDLIB)/rt/start+test.ha \ $(STDLIB)/rt/abort+test.ha \ $(STDLIB)/rt/+test/$(PLATFORM).ha \ @@ -2394,6 +2400,8 @@ $(TESTCACHE)/time/time.ssa: $(testlib_time_srcs) $(testlib_rt) $(testlib_linux_v testlib_types_srcs= \ $(STDLIB)/types/limits.ha \ $(STDLIB)/types/classes.ha \ + $(STDLIB)/types/reflect.ha \ + $(STDLIB)/types/util.ha \ $(STDLIB)/types/arch$(ARCH).ha $(TESTCACHE)/types/types.ssa: $(testlib_types_srcs) $(testlib_rt) diff --git a/types/reflect.ha b/types/reflect.ha @@ -0,0 +1,131 @@ +// The sz field of [[typeinfo]] is set to this value to indicate that the size +// of the type is undefined. +export def SIZE_UNDEFINED: size = -1: size; + +// Structure detailing information about a specific type. +export type typeinfo = struct { + id: uint, + sz: size, + al: size, + flags: flags, + repr: repr, +}; + +// Returns [[typeinfo]] for the provided type. +export fn reflect(in: type) *typeinfo = in: *typeinfo; + +// Returns [[typeinfo]] for the provided type, unwrapping any aliases along the +// way. +export fn unwrap(in: type) *typeinfo = { + let info = reflect(in); + match (info.repr) { + case a: alias => + return unwrap(a.secondary); + case => + return info; + }; +}; + +// Type flags. +export type flags = enum u8 { + NONE = 0, + CONST = 1 << 0, + ERROR = 1 << 1, +}; + +// Details of the type representation. +export type repr = (alias | array | builtin + | enumerated | func | pointer | slice_repr + | struct_union | tagged | tuple); + +// A type alias. +export type alias = struct { + ident: []str, + secondary: type, +}; + +// An array type. +export type array = struct { + length: size, + members: type, +}; + +// A built-in type. +export type builtin = enum u8 { + BOOL, CHAR, ENUM, F32, F64, I16, I32, I64, I8, INT, NULL, RUNE, SIZE, + STR, U16, U32, U64, U8, UINT, UINTPTR, VOID, TYPE, +}; + +// An enum type. +export type enumerated = struct { + storage: builtin, + values: [](str, u64), +}; + +// Indicates the variadism of a [[func]]. +export type variadism = enum { + NONE, + C, + HARE, +}; + +// Indicats if a [[func]] has the @noreturn attribute. +export type func_flags = enum uint { + NORETURN = 1 << 0, +}; + +// A function type, e.g. fn(x: int, y: int) int. +export type func = struct { + result: type, + variadism: variadism, + flags: func_flags, + params: []type, +}; + +// Flags which apply to a pointer type. +export type pointer_flags = enum u8 { + NONE = 0, + NULLABLE = 1 << 0, +}; + +// *int +export type pointer = struct { + secondary: type, + flags: pointer_flags, +}; + +// Type information for slice members. Distinct from [[slice]], which is the +// representation of a slice object at runtime. +export type slice_repr = type; + +// Indicates if a [[_struct]] was declared as a struct or union type. +export type struct_kind = enum { + STRUCT, + UNION, +}; + +// struct { ... } or union { ... } +export type struct_union = struct { + kind: struct_kind, + fields: []struct_field, +}; + +// A single struct field. +export type struct_field = struct { + // "" for an anonymous field + name: str, + offs: size, + type_: type, +}; + +// A tagged union type, e.g. (int | uint | void). +export type tagged = []type; + +// A tuple type, e.g. (a, b, c) +export type tuple = []tuple_value; + +// A single value of a tuple type. +export type tuple_value = struct { + offs: size, + type_: type, +}; diff --git a/types/util.ha b/types/util.ha @@ -0,0 +1,42 @@ +// Returns the value of the enum at "val" as a string. Aborts if the value is +// not present. Note that this does not work with enums being used as a flag +// type, see [[strflag]] instead. +export fn strenum(ty: type, val: *void) str = { + const ty = unwrap(ty); + const en = ty.repr as enumerated; + const value: u64 = switch (en.storage) { + case builtin::CHAR, builtin::I8, builtin::U8 => + yield *(val: *u8); + case builtin::I16, builtin::U16 => + yield *(val: *u16); + case builtin::I32, builtin::U32 => + yield *(val: *u32); + case builtin::I64, builtin::U64 => + yield *(val: *u64); + case builtin::INT, builtin::UINT => + yield switch (size(int)) { + case 4 => + yield *(val: *u32); + case 8 => + yield *(val: *u64); + case => abort(); + }; + case builtin::SIZE => + yield switch (size(size)) { + case 4 => + yield *(val: *u32); + case 8 => + yield *(val: *u64); + case => abort(); + }; + case => abort(); + }; + + for (let i = 0z; i < len(en.values); i += 1) { + if (en.values[i].1 == value) { + return en.values[i].0; + }; + }; + + abort("enum has invalid value"); +};