aboutsummaryrefslogtreecommitdiff
path: root/src/types.c
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-03-21 21:43:51 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-03-21 21:43:51 -0400
commitdcf266228512e100e779fe72f2d4e9ebb605ffe6 (patch)
tree6fb3cac1ee3647f08bca219949ed8dc61b5861f2 /src/types.c
parent9acea1807f5945c55445a16259386e4979203a1e (diff)
Move files into src/ and build into build/
Diffstat (limited to 'src/types.c')
-rw-r--r--src/types.c739
1 files changed, 739 insertions, 0 deletions
diff --git a/src/types.c b/src/types.c
new file mode 100644
index 00000000..506850e8
--- /dev/null
+++ b/src/types.c
@@ -0,0 +1,739 @@
+// Logic for handling type_t types
+#include <gc/cord.h>
+#include <limits.h>
+#include <math.h>
+#include <signal.h>
+#include <stdint.h>
+#include <sys/param.h>
+
+#include "cordhelpers.h"
+#include "environment.h"
+#include "stdlib/integers.h"
+#include "stdlib/tables.h"
+#include "stdlib/util.h"
+#include "types.h"
+
+CORD type_to_cord(type_t *t) {
+ if (!t)
+ return "(Unknown type)";
+
+ switch (t->tag) {
+ case UnknownType: return "???";
+ case AbortType: return "Abort";
+ case ReturnType: {
+ type_t *ret = Match(t, ReturnType)->ret;
+ return CORD_all("Return(", ret ? type_to_cord(ret) : "Void", ")");
+ }
+ case VoidType: return "Void";
+ case MemoryType: return "Memory";
+ case BoolType: return "Bool";
+ case ByteType: return "Byte";
+ case CStringType: return "CString";
+ case MomentType: return "Moment";
+ case TextType: return Match(t, TextType)->lang ? Match(t, TextType)->lang : "Text";
+ case BigIntType: return "Int";
+ case IntType: return CORD_asprintf("Int%d", Match(t, IntType)->bits);
+ case NumType: return Match(t, NumType)->bits == TYPE_NBITS32 ? "Num32" : "Num";
+ case ArrayType: {
+ auto array = Match(t, ArrayType);
+ return CORD_asprintf("[%r]", type_to_cord(array->item_type));
+ }
+ case TableType: {
+ auto table = Match(t, TableType);
+ if (table->default_value)
+ return CORD_asprintf("{%r=%.*s}", type_to_cord(table->key_type),
+ table->default_value->end - table->default_value->start, table->default_value->start);
+ else
+ return CORD_asprintf("{%r:%r}", type_to_cord(table->key_type), type_to_cord(table->value_type));
+ }
+ case SetType: {
+ auto set = Match(t, SetType);
+ return CORD_asprintf("{%r}", type_to_cord(set->item_type));
+ }
+ case ClosureType: {
+ return type_to_cord(Match(t, ClosureType)->fn);
+ }
+ case FunctionType: {
+ CORD c = "func(";
+ auto fn = Match(t, FunctionType);
+ for (arg_t *arg = fn->args; arg; arg = arg->next) {
+ c = CORD_cat(c, type_to_cord(arg->type));
+ if (arg->next) c = CORD_cat(c, ",");
+ }
+ if (fn->ret && fn->ret->tag != VoidType)
+ c = CORD_all(c, fn->args ? " -> " : "-> ", type_to_cord(fn->ret));
+ c = CORD_all(c, ")");
+ return c;
+ }
+ case StructType: {
+ auto struct_ = Match(t, StructType);
+ return struct_->name;
+ }
+ case PointerType: {
+ auto ptr = Match(t, PointerType);
+ CORD sigil = ptr->is_stack ? "&" : "@";
+ return CORD_all(sigil, type_to_cord(ptr->pointed));
+ }
+ case EnumType: {
+ auto tagged = Match(t, EnumType);
+ return tagged->name;
+ }
+ case OptionalType: {
+ type_t *opt = Match(t, OptionalType)->type;
+ if (opt)
+ return CORD_all(type_to_cord(opt), "?");
+ else
+ return "(Unknown optional type)";
+ }
+ case MutexedType: {
+ type_t *opt = Match(t, MutexedType)->type;
+ if (opt)
+ return CORD_all("mutexed ", type_to_cord(opt));
+ else
+ return "(Unknown optional type)";
+ }
+ case TypeInfoType: {
+ return CORD_all("Type$info(", Match(t, TypeInfoType)->name, ")");
+ }
+ case ModuleType: {
+ return CORD_all("Module(", Match(t, ModuleType)->name, ")");
+ }
+ default: {
+ raise(SIGABRT);
+ return CORD_asprintf("Unknown type: %d", t->tag);
+ }
+ }
+}
+
+PUREFUNC const char *get_type_name(type_t *t)
+{
+ switch (t->tag) {
+ case TextType: return Match(t, TextType)->lang;
+ case StructType: return Match(t, StructType)->name;
+ case EnumType: return Match(t, EnumType)->name;
+ default: return NULL;
+ }
+}
+
+int printf_pointer_size(const struct printf_info *info, size_t n, int argtypes[n], int sizes[n])
+{
+ if (n < 1) return -1;
+ (void)info;
+ argtypes[0] = PA_POINTER;
+ sizes[0] = sizeof(void*);
+ return 1;
+}
+
+int printf_type(FILE *stream, const struct printf_info *info, const void *const args[])
+{
+ (void)info;
+ type_t *t = *(type_t**)args[0];
+ if (!t) return fputs("(null)", stream);
+ return CORD_put(type_to_cord(t), stream);
+}
+
+bool type_eq(type_t *a, type_t *b)
+{
+ if (a == b) return true;
+ if (a->tag != b->tag) return false;
+ return (CORD_cmp(type_to_cord(a), type_to_cord(b)) == 0);
+}
+
+bool type_is_a(type_t *t, type_t *req)
+{
+ if (type_eq(t, req)) return true;
+ if (req->tag == OptionalType && Match(req, OptionalType)->type)
+ return type_is_a(t, Match(req, OptionalType)->type);
+ if (t->tag == PointerType && req->tag == PointerType) {
+ auto t_ptr = Match(t, PointerType);
+ auto req_ptr = Match(req, PointerType);
+ if (type_eq(t_ptr->pointed, req_ptr->pointed))
+ return (!t_ptr->is_stack && req_ptr->is_stack) || (!t_ptr->is_stack);
+ }
+ return false;
+}
+
+type_t *non_optional(type_t *t)
+{
+ return t->tag == OptionalType ? Match(t, OptionalType)->type : t;
+}
+
+PUREFUNC type_t *value_type(type_t *t)
+{
+ while (t->tag == PointerType)
+ t = Match(t, PointerType)->pointed;
+ return t;
+}
+
+type_t *type_or_type(type_t *a, type_t *b)
+{
+ if (!a) return b;
+ if (!b) return a;
+ if (a->tag == OptionalType && !Match(a, OptionalType)->type)
+ return b->tag == OptionalType ? b : Type(OptionalType, b);
+ if (b->tag == OptionalType && !Match(b, OptionalType)->type)
+ return a->tag == OptionalType ? a : Type(OptionalType, a);
+ if (a->tag == ReturnType && b->tag == ReturnType)
+ return Type(ReturnType, .ret=type_or_type(Match(a, ReturnType)->ret, Match(b, ReturnType)->ret));
+ if (type_is_a(b, a)) return a;
+ if (type_is_a(a, b)) return b;
+ if (a->tag == AbortType || a->tag == ReturnType) return non_optional(b);
+ if (b->tag == AbortType || b->tag == ReturnType) return non_optional(a);
+ if ((a->tag == IntType || a->tag == NumType) && (b->tag == IntType || b->tag == NumType)) {
+ switch (compare_precision(a, b)) {
+ case NUM_PRECISION_EQUAL: case NUM_PRECISION_MORE: return a;
+ case NUM_PRECISION_LESS: return b;
+ default: return NULL;
+ }
+ return NULL;
+ }
+ return NULL;
+}
+
+static PUREFUNC INLINE double type_min_magnitude(type_t *t)
+{
+ switch (t->tag) {
+ case BoolType: return (double)false;
+ case ByteType: return 0;
+ case BigIntType: return -1./0.;
+ case IntType: {
+ switch (Match(t, IntType)->bits) {
+ case TYPE_IBITS8: return (double)INT8_MIN;
+ case TYPE_IBITS16: return (double)INT16_MIN;
+ case TYPE_IBITS32: return (double)INT32_MIN;
+ case TYPE_IBITS64: return (double)INT64_MIN;
+ default: errx(1, "Invalid integer bit size");
+ }
+ }
+ case NumType: return -1./0.;
+ default: return NAN;
+ }
+}
+
+static PUREFUNC INLINE double type_max_magnitude(type_t *t)
+{
+ switch (t->tag) {
+ case BoolType: return (double)true;
+ case ByteType: return (double)UINT8_MAX;
+ case BigIntType: return 1./0.;
+ case IntType: {
+ switch (Match(t, IntType)->bits) {
+ case TYPE_IBITS8: return (double)INT8_MAX;
+ case TYPE_IBITS16: return (double)INT16_MAX;
+ case TYPE_IBITS32: return (double)INT32_MAX;
+ case TYPE_IBITS64: return (double)INT64_MAX;
+ default: errx(1, "Invalid integer bit size");
+ }
+ }
+ case NumType: return 1./0.;
+ default: return NAN;
+ }
+}
+
+PUREFUNC precision_cmp_e compare_precision(type_t *a, type_t *b)
+{
+ if (a->tag == OptionalType && Match(a, OptionalType)->type->tag == NumType)
+ a = Match(a, OptionalType)->type;
+ if (b->tag == OptionalType && Match(b, OptionalType)->type->tag == NumType)
+ b = Match(b, OptionalType)->type;
+
+ if (is_int_type(a) && b->tag == NumType)
+ return NUM_PRECISION_LESS;
+ else if (a->tag == NumType && is_int_type(b))
+ return NUM_PRECISION_MORE;
+
+ double a_min = type_min_magnitude(a),
+ b_min = type_min_magnitude(b),
+ a_max = type_max_magnitude(a),
+ b_max = type_max_magnitude(b);
+
+ if (isnan(a_min) || isnan(b_min) || isnan(a_max) || isnan(b_max))
+ return NUM_PRECISION_INCOMPARABLE;
+ else if (a_min == b_min && a_max == b_max) return NUM_PRECISION_EQUAL;
+ else if (a_min <= b_min && b_max <= a_max) return NUM_PRECISION_MORE;
+ else if (b_min <= a_min && a_max <= b_max) return NUM_PRECISION_LESS;
+ else return NUM_PRECISION_INCOMPARABLE;
+}
+
+PUREFUNC bool has_heap_memory(type_t *t)
+{
+ switch (t->tag) {
+ case ArrayType: return true;
+ case TableType: return true;
+ case SetType: return true;
+ case PointerType: return true;
+ case OptionalType: return has_heap_memory(Match(t, OptionalType)->type);
+ case MutexedType: return true;
+ case BigIntType: return true;
+ case StructType: {
+ for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) {
+ if (has_heap_memory(field->type))
+ return true;
+ }
+ return false;
+ }
+ case EnumType: {
+ for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) {
+ if (tag->type && has_heap_memory(tag->type))
+ return true;
+ }
+ return false;
+ }
+ default: return false;
+ }
+}
+
+PUREFUNC bool has_stack_memory(type_t *t)
+{
+ switch (t->tag) {
+ case PointerType: return Match(t, PointerType)->is_stack;
+ case OptionalType: return has_stack_memory(Match(t, OptionalType)->type);
+ case MutexedType: return has_stack_memory(Match(t, MutexedType)->type);
+ default: return false;
+ }
+}
+
+PUREFUNC const char *enum_single_value_tag(type_t *enum_type, type_t *t)
+{
+ const char *found = NULL;
+ for (tag_t *tag = Match(enum_type, EnumType)->tags; tag; tag = tag->next) {
+ if (tag->type->tag != StructType) continue;
+ auto s = Match(tag->type, StructType);
+ if (!s->fields || s->fields->next || !s->fields->type)
+ continue;
+
+ if (can_promote(t, s->fields->type)) {
+ if (found) // Ambiguous case, multiple matches
+ return NULL;
+ found = tag->name;
+ // Continue searching to check for ambiguous cases
+ }
+ }
+ return found;
+}
+
+PUREFUNC bool can_promote(type_t *actual, type_t *needed)
+{
+ if (!actual || !needed)
+ return false;
+
+ // No promotion necessary:
+ if (type_eq(actual, needed))
+ return true;
+
+ if (actual->tag == NumType && needed->tag == IntType)
+ return false;
+
+ if (actual->tag == IntType && (needed->tag == NumType || needed->tag == BigIntType))
+ return true;
+
+ if (actual->tag == BigIntType && needed->tag == NumType)
+ return true;
+
+ if (actual->tag == IntType && needed->tag == IntType) {
+ auto cmp = compare_precision(actual, needed);
+ return cmp == NUM_PRECISION_EQUAL || cmp == NUM_PRECISION_LESS;
+ }
+
+ if (needed->tag == EnumType)
+ return (enum_single_value_tag(needed, actual) != NULL);
+
+ // Lang to Text:
+ if (actual->tag == TextType && needed->tag == TextType && streq(Match(needed, TextType)->lang, "Text"))
+ return true;
+
+ // Text to C String
+ if (actual->tag == TextType && !Match(actual, TextType)->lang && needed->tag == CStringType)
+ return true;
+
+ // Automatic dereferencing:
+ if (actual->tag == PointerType && can_promote(Match(actual, PointerType)->pointed, needed))
+ return true;
+
+ if (actual->tag == OptionalType) {
+ if (needed->tag == BoolType)
+ return true;
+
+ // Ambiguous `none` to concrete optional
+ if (Match(actual, OptionalType)->type == NULL)
+ return (needed->tag == OptionalType);
+
+ // Optional num -> num
+ if (needed->tag == NumType && actual->tag == OptionalType && Match(actual, OptionalType)->type->tag == NumType)
+ return can_promote(Match(actual, OptionalType)->type, needed);
+ }
+
+ // Optional promotion:
+ if (needed->tag == OptionalType && Match(needed, OptionalType)->type != NULL && can_promote(actual, Match(needed, OptionalType)->type))
+ return true;
+
+ if (needed->tag == PointerType && actual->tag == PointerType) {
+ auto needed_ptr = Match(needed, PointerType);
+ auto actual_ptr = Match(actual, PointerType);
+
+ if (actual_ptr->is_stack && !needed_ptr->is_stack)
+ // Can't use &x for a function that wants a @Foo or ?Foo
+ return false;
+
+ if (needed_ptr->pointed->tag == TableType && actual_ptr->pointed->tag == TableType)
+ return can_promote(actual_ptr->pointed, needed_ptr->pointed);
+ else if (needed_ptr->pointed->tag != MemoryType && !type_eq(needed_ptr->pointed, actual_ptr->pointed))
+ // Can't use @Foo for a function that wants @Baz
+ // But you *can* use @Foo for a function that wants @Memory
+ return false;
+ else
+ return true;
+ }
+
+ // Cross-promotion between tables with default values and without
+ if (needed->tag == TableType && actual->tag == TableType) {
+ auto actual_table = Match(actual, TableType);
+ auto needed_table = Match(needed, TableType);
+ if (type_eq(needed_table->key_type, actual_table->key_type)
+ && type_eq(needed_table->value_type, actual_table->value_type))
+ return true;
+ }
+
+ if (needed->tag == ClosureType && actual->tag == FunctionType)
+ return can_promote(actual, Match(needed, ClosureType)->fn);
+
+ if (needed->tag == ClosureType && actual->tag == ClosureType)
+ return can_promote(Match(actual, ClosureType)->fn, Match(needed, ClosureType)->fn);
+
+ if (actual->tag == FunctionType && needed->tag == FunctionType) {
+ for (arg_t *actual_arg = Match(actual, FunctionType)->args, *needed_arg = Match(needed, FunctionType)->args;
+ actual_arg || needed_arg; actual_arg = actual_arg->next, needed_arg = needed_arg->next) {
+ if (!actual_arg || !needed_arg) return false;
+ if (type_eq(actual_arg->type, needed_arg->type)) continue;
+ if (actual_arg->type->tag == PointerType && needed_arg->type->tag == PointerType
+ && can_promote(actual_arg->type, needed_arg->type))
+ continue;
+ return false;
+ }
+ type_t *actual_ret = Match(actual, FunctionType)->ret;
+ if (!actual_ret) actual_ret = Type(VoidType);
+ type_t *needed_ret = Match(needed, FunctionType)->ret;
+ if (!needed_ret) needed_ret = Type(VoidType);
+
+ return (
+ (type_eq(actual_ret, needed_ret))
+ || (actual_ret->tag == PointerType && needed_ret->tag == PointerType
+ && can_promote(actual_ret, needed_ret)));
+ }
+
+ // Set -> Array promotion
+ if (needed->tag == ArrayType && actual->tag == SetType
+ && type_eq(Match(needed, ArrayType)->item_type, Match(actual, SetType)->item_type))
+ return true;
+
+ return false;
+}
+
+PUREFUNC bool is_int_type(type_t *t)
+{
+ return t->tag == IntType || t->tag == BigIntType;
+}
+
+PUREFUNC bool is_numeric_type(type_t *t)
+{
+ return t->tag == IntType || t->tag == BigIntType || t->tag == NumType || t->tag == ByteType;
+}
+
+PUREFUNC bool is_packed_data(type_t *t)
+{
+ if (t->tag == IntType || t->tag == NumType || t->tag == ByteType || t->tag == PointerType || t->tag == BoolType || t->tag == FunctionType) {
+ return true;
+ } else if (t->tag == StructType) {
+ for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) {
+ if (!is_packed_data(field->type))
+ return false;
+ }
+ return true;
+ } else if (t->tag == EnumType) {
+ for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) {
+ if (!is_packed_data(tag->type))
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+PUREFUNC size_t unpadded_struct_size(type_t *t)
+{
+ if (Match(t, StructType)->opaque)
+ compiler_err(NULL, NULL, NULL, "The struct type %s is opaque, so I can't get the size of it", Match(t, StructType)->name);
+ arg_t *fields = Match(t, StructType)->fields;
+ size_t size = 0;
+ size_t bit_offset = 0;
+ for (arg_t *field = fields; field; field = field->next) {
+ type_t *field_type = field->type;
+ if (field_type->tag == BoolType) {
+ bit_offset += 1;
+ if (bit_offset >= 8) {
+ size += 1;
+ bit_offset = 0;
+ }
+ } else {
+ if (bit_offset > 0) {
+ size += 1;
+ bit_offset = 0;
+ }
+ size_t align = type_align(field_type);
+ if (align > 1 && size % align > 0)
+ size += align - (size % align); // Padding
+ size += type_size(field_type);
+ }
+ }
+ if (bit_offset > 0) {
+ size += 1;
+ bit_offset = 0;
+ }
+ return size;
+}
+
+PUREFUNC size_t type_size(type_t *t)
+{
+ if (t == THREAD_TYPE) return sizeof(pthread_t*);
+ if (t == PATH_TYPE) return sizeof(Path_t);
+ if (t == PATH_TYPE_TYPE) return sizeof(PathType_t);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch-default"
+ switch (t->tag) {
+ case UnknownType: case AbortType: case ReturnType: case VoidType: return 0;
+ case MemoryType: errx(1, "Memory has undefined type size");
+ case BoolType: return sizeof(bool);
+ case ByteType: return sizeof(uint8_t);
+ case CStringType: return sizeof(char*);
+ case MomentType: return sizeof(Moment_t);
+ case BigIntType: return sizeof(Int_t);
+ case IntType: {
+ switch (Match(t, IntType)->bits) {
+ case TYPE_IBITS64: return sizeof(int64_t);
+ case TYPE_IBITS32: return sizeof(int32_t);
+ case TYPE_IBITS16: return sizeof(int16_t);
+ case TYPE_IBITS8: return sizeof(int8_t);
+ default: errx(1, "Invalid integer bit size");
+ }
+ }
+ case NumType: return Match(t, NumType)->bits == TYPE_NBITS64 ? sizeof(double) : sizeof(float);
+ case TextType: return sizeof(Text_t);
+ case ArrayType: return sizeof(Array_t);
+ case SetType: return sizeof(Table_t);
+ case TableType: return sizeof(Table_t);
+ case FunctionType: return sizeof(void*);
+ case ClosureType: return sizeof(struct {void *fn, *userdata;});
+ case PointerType: return sizeof(void*);
+ case MutexedType: return sizeof(MutexedData_t);
+ case OptionalType: {
+ type_t *nonnull = Match(t, OptionalType)->type;
+ switch (nonnull->tag) {
+ case IntType:
+ switch (Match(nonnull, IntType)->bits) {
+ case TYPE_IBITS64: return sizeof(OptionalInt64_t);
+ case TYPE_IBITS32: return sizeof(OptionalInt32_t);
+ case TYPE_IBITS16: return sizeof(OptionalInt16_t);
+ case TYPE_IBITS8: return sizeof(OptionalInt8_t);
+ default: errx(1, "Invalid integer bit size");
+ }
+ case StructType: {
+ size_t size = unpadded_struct_size(nonnull);
+ size += sizeof(bool); // is_null flag
+ size_t align = type_align(nonnull);
+ if (align > 0 && (size % align) > 0)
+ size = (size + align) - (size % align);
+ return size;
+ }
+ default: return type_size(nonnull);
+ }
+ }
+ case StructType: {
+ if (Match(t, StructType)->opaque)
+ compiler_err(NULL, NULL, NULL, "The struct type %s is opaque, so I can't get the size of it", Match(t, StructType)->name);
+ size_t size = unpadded_struct_size(t);
+ size_t align = type_align(t);
+ if (size > 0 && align > 0 && (size % align) > 0)
+ size = (size + align) - (size % align);
+ return size;
+ }
+ case EnumType: {
+ size_t max_align = 0;
+ size_t max_size = 0;
+ for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) {
+ size_t align = type_align(tag->type);
+ if (align > max_align) max_align = align;
+ size_t size = type_size(tag->type);
+ if (size > max_size) max_size = size;
+ }
+ size_t size = sizeof(UnknownType); // generic enum
+ if (max_align > 1 && size % max_align > 0) // Padding before first union field
+ size += max_align - (size % max_align);
+ size += max_size;
+ size_t align = MAX(__alignof__(UnknownType), max_align);
+ if (size % align > 0) // Padding after union
+ size += align - (size % align);
+ return size;
+ }
+ case TypeInfoType: return sizeof(TypeInfo_t);
+ case ModuleType: return 0;
+ }
+#pragma GCC diagnostic pop
+ errx(1, "This should not be reachable");
+}
+
+PUREFUNC size_t type_align(type_t *t)
+{
+ if (t == THREAD_TYPE) return __alignof__(pthread_t*);
+ if (t == PATH_TYPE) return __alignof__(Path_t);
+ if (t == PATH_TYPE_TYPE) return __alignof__(PathType_t);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch-default"
+ switch (t->tag) {
+ case UnknownType: case AbortType: case ReturnType: case VoidType: return 0;
+ case MemoryType: errx(1, "Memory has undefined type alignment");
+ case BoolType: return __alignof__(bool);
+ case ByteType: return __alignof__(uint8_t);
+ case CStringType: return __alignof__(char*);
+ case MomentType: return __alignof__(Moment_t);
+ case BigIntType: return __alignof__(Int_t);
+ case IntType: {
+ switch (Match(t, IntType)->bits) {
+ case TYPE_IBITS64: return __alignof__(int64_t);
+ case TYPE_IBITS32: return __alignof__(int32_t);
+ case TYPE_IBITS16: return __alignof__(int16_t);
+ case TYPE_IBITS8: return __alignof__(int8_t);
+ default: return 0;
+ }
+ }
+ case NumType: return Match(t, NumType)->bits == TYPE_NBITS64 ? __alignof__(double) : __alignof__(float);
+ case TextType: return __alignof__(Text_t);
+ case SetType: return __alignof__(Table_t);
+ case ArrayType: return __alignof__(Array_t);
+ case TableType: return __alignof__(Table_t);
+ case FunctionType: return __alignof__(void*);
+ case ClosureType: return __alignof__(struct {void *fn, *userdata;});
+ case PointerType: return __alignof__(void*);
+ case MutexedType: return __alignof__(MutexedData_t);
+ case OptionalType: {
+ type_t *nonnull = Match(t, OptionalType)->type;
+ switch (nonnull->tag) {
+ case IntType:
+ switch (Match(nonnull, IntType)->bits) {
+ case TYPE_IBITS64: return __alignof__(OptionalInt64_t);
+ case TYPE_IBITS32: return __alignof__(OptionalInt32_t);
+ case TYPE_IBITS16: return __alignof__(OptionalInt16_t);
+ case TYPE_IBITS8: return __alignof__(OptionalInt8_t);
+ default: errx(1, "Invalid integer bit size");
+ }
+ case StructType: return MAX(1, type_align(nonnull));
+ default: return type_align(nonnull);
+ }
+ }
+ case StructType: {
+ if (Match(t, StructType)->opaque)
+ compiler_err(NULL, NULL, NULL, "The struct type %s is opaque, so I can't get the alignment of it",
+ Match(t, StructType)->name);
+ arg_t *fields = Match(t, StructType)->fields;
+ size_t align = t->tag == StructType ? 0 : sizeof(void*);
+ for (arg_t *field = fields; field; field = field->next) {
+ size_t field_align = type_align(field->type);
+ if (field_align > align) align = field_align;
+ }
+ return align;
+ }
+ case EnumType: {
+ size_t align = __alignof__(UnknownType);
+ for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) {
+ size_t tag_align = type_align(tag->type);
+ if (tag_align > align) align = tag_align;
+ }
+ return align;
+ }
+ case TypeInfoType: return __alignof__(TypeInfo_t);
+ case ModuleType: return 0;
+ }
+#pragma GCC diagnostic pop
+ errx(1, "This should not be reachable");
+}
+
+type_t *get_field_type(type_t *t, const char *field_name)
+{
+ t = value_type(t);
+ switch (t->tag) {
+ case PointerType:
+ return get_field_type(Match(t, PointerType)->pointed, field_name);
+ case TextType: {
+ if (Match(t, TextType)->lang && streq(field_name, "text"))
+ return TEXT_TYPE;
+ else if (streq(field_name, "length")) return INT_TYPE;
+ return NULL;
+ }
+ case StructType: {
+ auto struct_t = Match(t, StructType);
+ for (arg_t *field = struct_t->fields; field; field = field->next) {
+ if (streq(field->name, field_name))
+ return field->type;
+ }
+ return NULL;
+ }
+ case EnumType: {
+ auto e = Match(t, EnumType);
+ for (tag_t *tag = e->tags; tag; tag = tag->next) {
+ if (streq(field_name, tag->name))
+ return Type(BoolType);
+ }
+ return NULL;
+ }
+ case SetType: {
+ if (streq(field_name, "length"))
+ return INT_TYPE;
+ else if (streq(field_name, "items"))
+ return Type(ArrayType, .item_type=Match(t, SetType)->item_type);
+ return NULL;
+ }
+ case TableType: {
+ if (streq(field_name, "length"))
+ return INT_TYPE;
+ else if (streq(field_name, "keys"))
+ return Type(ArrayType, Match(t, TableType)->key_type);
+ else if (streq(field_name, "values"))
+ return Type(ArrayType, Match(t, TableType)->value_type);
+ else if (streq(field_name, "fallback"))
+ return Type(OptionalType, .type=t);
+ return NULL;
+ }
+ case ArrayType: {
+ if (streq(field_name, "length")) return INT_TYPE;
+ return NULL;
+ }
+ case MomentType: {
+ if (streq(field_name, "seconds")) return Type(IntType, .bits=TYPE_IBITS64);
+ else if (streq(field_name, "microseconds")) return Type(IntType, .bits=TYPE_IBITS64);
+ return NULL;
+ }
+ default: return NULL;
+ }
+}
+
+PUREFUNC type_t *get_iterated_type(type_t *t)
+{
+ type_t *iter_value_t = value_type(t);
+ switch (iter_value_t->tag) {
+ case BigIntType: case IntType: return iter_value_t; break;
+ case ArrayType: return Match(iter_value_t, ArrayType)->item_type; break;
+ case SetType: return Match(iter_value_t, SetType)->item_type; break;
+ case TableType: return NULL;
+ case FunctionType: case ClosureType: {
+ // Iterator function
+ auto fn = iter_value_t->tag == ClosureType ?
+ Match(Match(iter_value_t, ClosureType)->fn, FunctionType) : Match(iter_value_t, FunctionType);
+ if (fn->args || fn->ret->tag != OptionalType)
+ return NULL;
+ return Match(fn->ret, OptionalType)->type;
+ }
+ default: return NULL;
+ }
+}
+
+// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0