aboutsummaryrefslogtreecommitdiff
path: root/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 /types.c
parent9acea1807f5945c55445a16259386e4979203a1e (diff)
Move files into src/ and build into build/
Diffstat (limited to 'types.c')
-rw-r--r--types.c739
1 files changed, 0 insertions, 739 deletions
diff --git a/types.c b/types.c
deleted file mode 100644
index 506850e8..00000000
--- a/types.c
+++ /dev/null
@@ -1,739 +0,0 @@
-// 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