aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-09-11 12:01:17 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-09-11 12:01:17 -0400
commitdee3742b48e27ef36637d004163286d3352b0763 (patch)
tree881a9a25b4cfeb6ff632a3fa48ccd3abd7f642ae
parentbba9f1b141fba5ffc366ef9377fb3089c0c5a1a3 (diff)
Optional structs
-rw-r--r--builtins/optionals.c6
-rw-r--r--compile.c58
-rw-r--r--structs.c18
-rw-r--r--test/optionals.tm21
-rw-r--r--types.c7
5 files changed, 85 insertions, 25 deletions
diff --git a/builtins/optionals.c b/builtins/optionals.c
index 27ab8ec6..f58c8134 100644
--- a/builtins/optionals.c
+++ b/builtins/optionals.c
@@ -36,6 +36,12 @@ static inline bool is_null(const void *obj, const TypeInfo *non_optional_type)
case ArrayInfo: return ((Array_t*)obj)->length < 0;
case TableInfo: return ((Table_t*)obj)->entries.length < 0;
case FunctionInfo: return *(void**)obj == NULL;
+ case CustomInfo: {
+ int64_t offset = non_optional_type->size;
+ if (offset % non_optional_type->align)
+ offset += non_optional_type->align - (offset % non_optional_type->align);
+ return *(bool*)(obj + offset);
+ }
default: {
Text_t t = generic_as_text(NULL, false, non_optional_type);
errx(1, "is_null() not implemented for: %k", &t);
diff --git a/compile.c b/compile.c
index 04ae8606..5d46e67c 100644
--- a/compile.c
+++ b/compile.c
@@ -23,7 +23,24 @@ static CORD compile_string(env_t *env, ast_t *ast, CORD color);
static CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args);
static CORD compile_maybe_incref(env_t *env, ast_t *ast);
static CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target);
+static CORD promote_to_optional(type_t *t, CORD code);
+CORD promote_to_optional(type_t *t, CORD code)
+{
+ if (t->tag == IntType) {
+ switch (Match(t, IntType)->bits) {
+ case TYPE_IBITS8: return CORD_all("((OptionalInt8_t){.i=", code, "})");
+ case TYPE_IBITS16: return CORD_all("((OptionalInt16_t){.i=", code, "})");
+ case TYPE_IBITS32: return CORD_all("((OptionalInt32_t){.i=", code, "})");
+ case TYPE_IBITS64: return CORD_all("((OptionalInt64_t){.i=", code, "})");
+ default: errx(1, "Unsupported in type: %T", t);
+ }
+ } else if (t->tag == StructType) {
+ return CORD_all("((", compile_type(Type(OptionalType, .type=t)), "){.value=", code, "})");
+ } else {
+ return code;
+ }
+}
static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
{
if (type_eq(actual, needed))
@@ -62,8 +79,10 @@ static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
}
// Optional promotion:
- if (needed->tag == OptionalType && type_eq(actual, Match(needed, OptionalType)->type))
+ if (needed->tag == OptionalType && type_eq(actual, Match(needed, OptionalType)->type)) {
+ *code = promote_to_optional(actual, *code);
return true;
+ }
// Stack ref promotion:
if (actual->tag == PointerType && needed->tag == PointerType)
@@ -189,9 +208,9 @@ CORD compile_type(type_t *t)
case ClosureType: return "closure_t";
case PointerType: return CORD_cat(compile_type(Match(t, PointerType)->pointed), "*");
case StructType: {
- auto s = Match(t, StructType);
if (t == THREAD_TYPE)
return "pthread_t*";
+ auto s = Match(t, StructType);
return CORD_all("struct ", namespace_prefix(s->env->libname, s->env->namespace->parent), s->name, "_s");
}
case EnumType: {
@@ -200,11 +219,22 @@ CORD compile_type(type_t *t)
}
case OptionalType: {
type_t *nonnull = Match(t, OptionalType)->type;
- if (nonnull->tag == IntType)
+ switch (nonnull->tag) {
+ case BoolType: case CStringType: case BigIntType: case NumType: case TextType:
+ case ArrayType: case SetType: case TableType: case FunctionType: case ClosureType:
+ case PointerType:
+ return compile_type(nonnull);
+ case IntType:
return CORD_all("Optional", compile_type(nonnull));
- if (!supports_optionals(nonnull))
+ case StructType: {
+ if (nonnull == THREAD_TYPE)
+ return "pthread_t*";
+ auto s = Match(nonnull, StructType);
+ return CORD_all(namespace_prefix(s->env->libname, s->env->namespace->parent), "$Optional", s->name, "_t");
+ }
+ default:
compiler_err(NULL, NULL, NULL, "Optional types are not supported for: %T", t);
- return compile_type(nonnull);
+ }
}
case TypeInfoType: return "TypeInfo";
default: compiler_err(NULL, NULL, NULL, "Compiling type is not implemented for type with tag %d", t->tag);
@@ -287,6 +317,8 @@ static CORD optional_var_into_nonnull(binding_t *b)
switch (b->type->tag) {
case IntType:
return CORD_all(b->code, ".i");
+ case StructType:
+ return CORD_all(b->code, ".value");
default:
return b->code;
}
@@ -314,6 +346,8 @@ static CORD compile_optional_check(env_t *env, ast_t *ast)
return CORD_all("((", compile(env, ast), ").length >= 0)");
else if (t->tag == IntType)
return CORD_all("!(", compile(env, ast), ").is_null");
+ else if (t->tag == StructType)
+ return CORD_all("!(", compile(env, ast), ").is_null");
errx(1, "Optional check not implemented for: %T", t);
}
@@ -1672,6 +1706,7 @@ CORD compile(env_t *env, ast_t *ast)
case PointerType: return CORD_all("((", compile_type(t), ")NULL)");
case ClosureType: return "NULL_CLOSURE";
case NumType: return "nan(\"null\")";
+ case StructType: return CORD_all("((", compile_type(Type(OptionalType, .type=t)), "){.is_null=true})");
default: code_err(ast, "Nil isn't implemented for this type: %T", t);
}
}
@@ -1779,18 +1814,7 @@ CORD compile(env_t *env, ast_t *ast)
case Optional: {
ast_t *value = Match(ast, Optional)->value;
CORD value_code = compile(env, value);
- type_t *t = get_type(env, value);
- if (t->tag == IntType) {
- switch (Match(t, IntType)->bits) {
- case TYPE_IBITS8: return CORD_all("((OptionalInt8_t){.i=", value_code, "})");
- case TYPE_IBITS16: return CORD_all("((OptionalInt16_t){.i=", value_code, "})");
- case TYPE_IBITS32: return CORD_all("((OptionalInt32_t){.i=", value_code, "})");
- case TYPE_IBITS64: return CORD_all("((OptionalInt64_t){.i=", value_code, "})");
- default: errx(1, "Unsupported in type: %T", t);
- }
- } else {
- return value_code;
- }
+ return promote_to_optional(get_type(env, value), value_code);
}
case BinaryOp: {
auto binop = Match(ast, BinaryOp);
diff --git a/structs.c b/structs.c
index 566f39ff..ab5efafb 100644
--- a/structs.c
+++ b/structs.c
@@ -169,18 +169,24 @@ CORD compile_struct_typedef(env_t *env, ast_t *ast)
{
auto def = Match(ast, StructDef);
CORD full_name = CORD_cat(namespace_prefix(env->libname, env->namespace), def->name);
- CORD code = CORD_all("typedef struct ", full_name, "_s ", full_name, "_t;\n");
- CORD struct_code = CORD_all("struct ", full_name, "_s {\n");
+ CORD fields = CORD_EMPTY;
for (arg_ast_t *field = def->fields; field; field = field->next) {
type_t *field_t = get_arg_ast_type(env, field);
CORD type_code = compile_type(field_t);
- CORD_appendf(&struct_code, "%r $%s%s;\n", type_code, field->name,
- CORD_cmp(type_code, "Bool_t") ? "" : ":1");
+ fields = CORD_all(fields, type_code, " $", field->name, field_t->tag == BoolType ? ":1" : CORD_EMPTY, ";\n");
}
+ CORD struct_code = CORD_all("struct ", full_name, "_s {\n");
struct_code = CORD_all(struct_code, "};\n");
- code = CORD_all(code, struct_code);
- return code;
+ return CORD_all(
+ "typedef struct ", full_name, "_s ", full_name, "_t;\n",
+ "struct ", full_name, "_s {\n",
+ fields,
+ "};\n",
+ "typedef struct {\n",
+ full_name, "_t value;\n"
+ "Bool_t is_null:1;\n"
+ "} ", namespace_prefix(env->libname, env->namespace), "$Optional", def->name, "_t;\n");
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/test/optionals.tm b/test/optionals.tm
index 8e24f66d..b80fbab0 100644
--- a/test/optionals.tm
+++ b/test/optionals.tm
@@ -1,3 +1,12 @@
+
+struct Struct(x:Int, y:Text):
+ func maybe(should_i:Bool)-> Struct?:
+ if should_i:
+ return Struct(123, "hello")
+ else:
+ return !Struct
+
+
func maybe_int(should_i:Bool)->Int?:
if should_i:
return 123
@@ -121,6 +130,18 @@ func main():
fail("Truthy: $nope")
else: !! Falsey: $nope
+ do:
+ !! ...
+ !! Structs:
+ >> yep := Struct.maybe(yes)
+ = Struct(x=123, y="hello")?
+ >> nope := Struct.maybe(no)
+ = !Struct
+ >> if yep: >> yep
+ else: fail("Falsey: $yep")
+ >> if nope:
+ fail("Truthy: $nope")
+ else: !! Falsey: $nope
if yep := maybe_int(yes):
>> yep
diff --git a/types.c b/types.c
index daa1026f..6a1459c4 100644
--- a/types.c
+++ b/types.c
@@ -1,9 +1,10 @@
// Logic for handling type_t types
#include <gc/cord.h>
-#include <stdint.h>
-#include <signal.h>
#include <limits.h>
#include <math.h>
+#include <signal.h>
+#include <stdint.h>
+#include <sys/param.h>
#include "builtins/integers.h"
#include "builtins/table.h"
@@ -418,6 +419,7 @@ PUREFUNC size_t type_size(type_t *t)
case TYPE_IBITS8: return sizeof(OptionalInt8_t);
default: errx(1, "Invalid integer bit size");
}
+ case StructType: return padded_type_size(nonnull) + 1;
default: return type_size(nonnull);
}
}
@@ -495,6 +497,7 @@ PUREFUNC size_t type_align(type_t *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);
}
}