Optional structs

This commit is contained in:
Bruce Hill 2024-09-11 12:01:17 -04:00
parent bba9f1b141
commit dee3742b48
5 changed files with 85 additions and 25 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);
}
}