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:
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");
+};