diff --git a/compile.c b/compile.c index b93dac4..a026260 100644 --- a/compile.c +++ b/compile.c @@ -1461,9 +1461,7 @@ CORD expr_as_text(env_t *env, CORD expr, type_t *t, CORD color) case FunctionType: case ClosureType: return CORD_asprintf("Func$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t)); case PointerType: return CORD_asprintf("Pointer$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t)); case OptionalType: return CORD_asprintf("Optional$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t)); - case StructType: case EnumType: - return CORD_asprintf("(%r)->CustomInfo.as_text(stack(%r), %r, %r)", - compile_type_info(env, t), expr, color, compile_type_info(env, t)); + case StructType: case EnumType: return CORD_asprintf("generic_as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t)); default: compiler_err(NULL, NULL, NULL, "Stringifying is not supported for %T", t); } } @@ -1952,6 +1950,7 @@ CORD compile(env_t *env, ast_t *ast) code_err(ast, "I don't know how to get the negative value of type %T", t); } + // TODO: for constructors, do new(T, ...) instead of heap((T){...}) case HeapAllocate: return CORD_asprintf("heap(%r)", compile(env, Match(ast, HeapAllocate)->value)); case StackReference: { ast_t *subject = Match(ast, StackReference)->value; @@ -3464,7 +3463,8 @@ CORD compile_type_info(env_t *env, type_t *t) return CORD_asprintf("Closure$info(%r)", CORD_quoted(type_to_cord(t))); } case OptionalType: { - return CORD_asprintf("Optional$info(%r)", compile_type_info(env, Match(t, OptionalType)->type)); + type_t *non_optional = Match(t, OptionalType)->type; + return CORD_asprintf("Optional$info(%zu, %zu, %r)", type_size(t), type_align(t), compile_type_info(env, non_optional)); } case TypeInfoType: return CORD_all("TypeInfo$info(", CORD_quoted(type_to_cord(Match(t, TypeInfoType)->type)), ")"); case MemoryType: return "&Memory$info"; @@ -3656,9 +3656,9 @@ CORD compile_file(env_t *env, ast_t *ast) "#include \n" "#include \"", name, ".tm.h\"\n\n", env->code->local_typedefs, "\n", + env->code->typeinfos, "\n", env->code->staticdefs, "\n", env->code->funcs, "\n", - env->code->typeinfos, "\n", "public void $", env->namespace->name, "$$initialize(void) {\n", "static bool initialized = false;\n", "if (initialized) return;\n", diff --git a/enums.c b/enums.c index 2fedca1..da4dd08 100644 --- a/enums.c +++ b/enums.c @@ -13,193 +13,53 @@ #include "typecheck.h" #include "stdlib/util.h" -PUREFUNC static bool has_extra_data(tag_ast_t *tags) -{ - for (tag_ast_t *tag = tags; tag; tag = tag->next) { - if (tag->fields) return true; - } - return false; -} - -static CORD compile_str_method(env_t *env, ast_t *ast) -{ - auto def = Match(ast, EnumDef); - CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name); - CORD str_func = CORD_all("static Text_t ", full_name, "$as_text(", full_name, "_t *obj, bool use_color) {\n" - "\tif (!obj) return Text(\"", def->name, "\");\n" - "switch (obj->tag) {\n"); - for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { - if (!tag->fields) { - str_func = CORD_all(str_func, "\tcase ", full_name, "$tag$", tag->name, ": return use_color ? Text(\"\\x1b[36;1m", - def->name, ".", tag->name, "\\x1b[m\") : Text(\"", def->name, ".", tag->name, "\");\n"); - continue; - } - - str_func = CORD_all(str_func, "\tcase ", full_name, "$tag$", tag->name, ": return Text$concat(use_color ? Text(\"\\x1b[36;1m", - def->name, ".", tag->name, "\\x1b[m(\") : Text(\"", def->name, ".", tag->name, "(\")"); - - if (tag->secret) { - str_func = CORD_cat(str_func, ", use_color ? Text(\"\\x1b[2m...\\x1b[m\") : Text(\"...\", \")\"));\n"); - continue; - } - - if (tag->fields && !tag->fields->next) { // Single-member tags don't need to print member names: - type_t *field_t = get_arg_ast_type(env, tag->fields); - CORD field_str = expr_as_text(env, CORD_all("obj->$", tag->name, ".$", tag->fields->name), field_t, "use_color"); - str_func = CORD_all(str_func, ", ", field_str); - } else { - for (arg_ast_t *field = tag->fields; field; field = field->next) { - type_t *field_t = get_arg_ast_type(env, field); - CORD field_str = expr_as_text(env, CORD_all("obj->$", tag->name, ".$", field->name), field_t, "use_color"); - str_func = CORD_all(str_func, ", Text(\"", field->name, "=\"), ", field_str); - if (field->next) str_func = CORD_cat(str_func, ", Text(\", \")"); - } - } - str_func = CORD_cat(str_func, ", Text(\")\"));\n"); - } - str_func = CORD_cat(str_func, "\tdefault: return (Text_t){.length=0};\n\t}\n}\n"); - return str_func; -} - -static CORD compile_compare_method(env_t *env, ast_t *ast) -{ - auto def = Match(ast, EnumDef); - CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name); - if (!has_extra_data(def->tags)) { - // Comparisons are simpler if there is only a tag, no tagged data: - return CORD_all("static int ", full_name, "$compare(const ", full_name, "_t *x, const ", full_name, - "_t *y, const TypeInfo_t *info) {\n" - "(void)info;\n" - "return (x->tag - y->tag);\n" - "}\n"); - } - CORD cmp_func = CORD_all("static int ", full_name, "$compare(const ", full_name, "_t *x, const ", full_name, - "_t *y, const TypeInfo_t *info) {\n" - "(void)info;\n" - "int diff = x->tag - y->tag;\n" - "if (diff) return diff;\n" - "switch (x->tag) {\n"); - for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { - if (tag->fields) { - type_t *tag_type = Table$str_get(*env->types, CORD_to_const_char_star(CORD_all(def->name, "$", tag->name))); - cmp_func = CORD_all(cmp_func, "\tcase ", full_name, "$tag$", tag->name, ": " - "return generic_compare(&x->$", tag->name, ", &y->$", tag->name, ", ", compile_type_info(env, tag_type), ");\n"); - } else { - cmp_func = CORD_all(cmp_func, "\tcase ", full_name, "$tag$", tag->name, ": return 0;\n"); - } - } - cmp_func = CORD_all(cmp_func, "default: return 0;\n}\n}\n"); - return cmp_func; -} - -static CORD compile_equals_method(env_t *env, ast_t *ast) -{ - auto def = Match(ast, EnumDef); - CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name); - if (!has_extra_data(def->tags)) { - // Equality is simpler if there is only a tag, no tagged data: - return CORD_all("static bool ", full_name, "$equal(const ", full_name, "_t *x, const ", full_name, - "_t *y, const TypeInfo_t *info) {\n" - "(void)info;\n" - "return (x->tag == y->tag);\n" - "}\n"); - } - CORD eq_func = CORD_all("static bool ", full_name, "$equal(const ", full_name, "_t *x, const ", full_name, - "_t *y, const TypeInfo_t *info) {\n" - "(void)info;\n" - "if (x->tag != y->tag) return no;\n" - "switch (x->tag) {\n"); - for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { - if (tag->fields) { - type_t *tag_type = Table$str_get(*env->types, CORD_to_const_char_star(CORD_all(def->name, "$", tag->name))); - eq_func = CORD_all(eq_func, "\tcase ", full_name, "$tag$", tag->name, ": " - "return generic_equal(&x->$", tag->name, ", &y->$", tag->name, ", ", compile_type_info(env, tag_type), ");\n"); - } else { - eq_func = CORD_all(eq_func, "\tcase ", full_name, "$tag$", tag->name, ": return yes;\n"); - } - } - eq_func = CORD_all(eq_func, "default: return 0;\n}\n}\n"); - return eq_func; -} - -static CORD compile_hash_method(env_t *env, ast_t *ast) -{ - auto def = Match(ast, EnumDef); - CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name); - if (!has_extra_data(def->tags)) { - // Hashing is simpler if there is only a tag, no tagged data: - return CORD_all("static uint64_t ", full_name, "$hash(const ", full_name, "_t *obj, const TypeInfo_t *info) {\n" - "(void)info;\n" - "return siphash24((void*)&obj->tag, sizeof(obj->tag));\n" - "\n}\n"); - } - CORD hash_func = CORD_all("static uint64_t ", full_name, "$hash(const ", full_name, "_t *obj, const TypeInfo_t *info) {\n" - "(void)info;\n" - "uint64_t hashes[2] = {(uint64_t)obj->tag, 0};\n" - "switch (obj->tag) {\n"); - for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { - if (tag->fields) { - type_t *tag_type = Table$str_get(*env->types, CORD_to_const_char_star(CORD_all(def->name, "$", tag->name))); - hash_func = CORD_all(hash_func, "\tcase ", full_name, "$tag$", tag->name, ": " - "hashes[1] = generic_hash(&obj->$", tag->name, ", ", compile_type_info(env, tag_type), ");\n" - "break;\n"); - } else { - hash_func = CORD_all(hash_func, "\tcase ", full_name, "$tag$", tag->name, ": break;\n"); - } - } - hash_func = CORD_all(hash_func, "}\n" - "return siphash24((void*)&hashes, sizeof(hashes));\n}\n"); - return hash_func; -} - void compile_enum_def(env_t *env, ast_t *ast) { auto def = Match(ast, EnumDef); CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name); + + // Compile member types and constructors: for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { - compile_struct_def(env, WrapAST(ast, StructDef, .name=CORD_to_const_char_star(CORD_all(def->name, "$", tag->name)), .fields=tag->fields)); - if (tag->fields) { // Constructor macros: - CORD arg_sig = CORD_EMPTY; - for (arg_ast_t *field = tag->fields; field; field = field->next) { - type_t *field_t = get_arg_ast_type(env, field); - arg_sig = CORD_all(arg_sig, compile_declaration(field_t, CORD_all("$", field->name))); - if (field->next) arg_sig = CORD_cat(arg_sig, ", "); - } - if (arg_sig == CORD_EMPTY) arg_sig = "void"; - CORD constructor_impl = CORD_all("public inline ", full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ") { return (", - full_name, "_t){.tag=", full_name, "$tag$", tag->name, ", .$", tag->name, "={"); - for (arg_ast_t *field = tag->fields; field; field = field->next) { - constructor_impl = CORD_all(constructor_impl, "$", field->name); - if (field->next) constructor_impl = CORD_cat(constructor_impl, ", "); - } - constructor_impl = CORD_cat(constructor_impl, "}}; }\n"); - env->code->funcs = CORD_cat(env->code->funcs, constructor_impl); + if (!tag->fields) continue; + + const char *tag_name = heap_strf("%s$%s", def->name, tag->name); + compile_struct_def(env, WrapAST(ast, StructDef, .name=tag_name, .fields=tag->fields, .secret=tag->secret)); + CORD arg_sig = CORD_EMPTY; + for (arg_ast_t *field = tag->fields; field; field = field->next) { + type_t *field_t = get_arg_ast_type(env, field); + arg_sig = CORD_all(arg_sig, compile_declaration(field_t, CORD_all("$", field->name))); + if (field->next) arg_sig = CORD_cat(arg_sig, ", "); } + if (arg_sig == CORD_EMPTY) arg_sig = "void"; + CORD constructor_impl = CORD_all("public inline ", full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ") { return (", + full_name, "_t){.tag=", full_name, "$tag$", tag->name, ", .$", tag->name, "={"); + for (arg_ast_t *field = tag->fields; field; field = field->next) { + constructor_impl = CORD_all(constructor_impl, "$", field->name); + if (field->next) constructor_impl = CORD_cat(constructor_impl, ", "); + } + constructor_impl = CORD_cat(constructor_impl, "}}; }\n"); + env->code->funcs = CORD_cat(env->code->funcs, constructor_impl); } + int num_tags = 0; + for (tag_ast_t *t = def->tags; t; t = t->next) + num_tags += 1; + type_t *t = Table$str_get(*env->types, def->name); - CORD typeinfo = CORD_asprintf("public const TypeInfo_t %s = {%zu, %zu, {.tag=EnumInfo, .CustomInfo={", - full_name, type_size(t), type_align(t)); + CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, {.tag=EnumInfo, .EnumInfo={.name=\"%s\", " + ".num_tags=%d, .tags=(NamedType_t[]){", + full_name, type_size(t), type_align(t), def->name, num_tags); - env->code->funcs = CORD_all(env->code->funcs, compile_str_method(env, ast)); - if (has_extra_data(def->tags)) { - env->code->funcs = CORD_all( - env->code->funcs, - compile_equals_method(env, ast), compile_compare_method(env, ast), - compile_hash_method(env, ast)); - typeinfo = CORD_all( - typeinfo, - ".as_text=(void*)", full_name, "$as_text, " - ".equal=(void*)", full_name, "$equal, " - ".hash=(void*)", full_name, "$hash, " - ".compare=(void*)", full_name, "$compare"); - } else { - // No need for custom methods if there is no tagged data - typeinfo = CORD_all(typeinfo, ".as_text=(void*)", full_name, "$as_text"); + for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { + const char *tag_type_name = heap_strf("%s$%s", def->name, tag->name); + type_t *tag_type = Table$str_get(*env->types, tag_type_name); + if (tag_type && Match(tag_type, StructType)->fields) + typeinfo = CORD_all(typeinfo, "{\"", tag->name, "\", ", compile_type_info(env, tag_type), "}, "); + else + typeinfo = CORD_all(typeinfo, "{\"", tag->name, "\"}, "); } - typeinfo = CORD_cat(typeinfo, "}}};\n"); + typeinfo = CORD_all(typeinfo, "}}}};\n"); env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo); - compile_namespace(env, def->name, def->namespace); } @@ -211,35 +71,40 @@ CORD compile_enum_header(env_t *env, ast_t *ast) CORD enum_def = CORD_all("struct ", full_name, "_s {\n" "\tenum { ", full_name, "$null=0, "); + bool has_any_tags_with_fields = false; for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { enum_def = CORD_all(enum_def, full_name, "$tag$", tag->name); if (tag->next) enum_def = CORD_all(enum_def, ", "); + has_any_tags_with_fields = has_any_tags_with_fields || (tag->fields != NULL); } - enum_def = CORD_all(enum_def, "} tag;\n" - "union {\n"); - for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { - CORD field_def = compile_struct_header(env, WrapAST(ast, StructDef, .name=CORD_to_const_char_star(CORD_all(def->name, "$", tag->name)), .fields=tag->fields)); - all_defs = CORD_all(all_defs, field_def); - enum_def = CORD_all(enum_def, full_name, "$", tag->name, "_t $", tag->name, ";\n"); + enum_def = CORD_all(enum_def, "} tag;\n"); + + if (has_any_tags_with_fields) { + enum_def = CORD_all(enum_def, "union {\n"); + for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { + if (!tag->fields) continue; + CORD field_def = compile_struct_header(env, WrapAST(ast, StructDef, .name=CORD_to_const_char_star(CORD_all(def->name, "$", tag->name)), .fields=tag->fields)); + all_defs = CORD_all(all_defs, field_def); + enum_def = CORD_all(enum_def, full_name, "$", tag->name, "_t $", tag->name, ";\n"); + } + enum_def = CORD_all(enum_def, "};\n"); } - enum_def = CORD_all(enum_def, "};\n};\n"); + enum_def = CORD_all(enum_def, "};\n"); all_defs = CORD_all(all_defs, enum_def); all_defs = CORD_all(all_defs, "extern const TypeInfo_t ", full_name, ";\n"); for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { - all_defs = CORD_all(all_defs, - "extern const TypeInfo_t ", namespace_prefix(env, env->namespace), def->name, "$", tag->name, ";\n"); - if (tag->fields) { // Constructor macros: - CORD arg_sig = CORD_EMPTY; - for (arg_ast_t *field = tag->fields; field; field = field->next) { - type_t *field_t = get_arg_ast_type(env, field); - arg_sig = CORD_all(arg_sig, compile_declaration(field_t, CORD_all("$", field->name))); - if (field->next) arg_sig = CORD_all(arg_sig, ", "); - } - if (arg_sig == CORD_EMPTY) arg_sig = "void"; - CORD constructor_def = CORD_all(full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ");\n"); - all_defs = CORD_all(all_defs, constructor_def); + if (!tag->fields) continue; + + CORD arg_sig = CORD_EMPTY; + for (arg_ast_t *field = tag->fields; field; field = field->next) { + type_t *field_t = get_arg_ast_type(env, field); + arg_sig = CORD_all(arg_sig, compile_declaration(field_t, CORD_all("$", field->name))); + if (field->next) arg_sig = CORD_all(arg_sig, ", "); } + if (arg_sig == CORD_EMPTY) arg_sig = "void"; + CORD constructor_def = CORD_all(full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ");\n"); + all_defs = CORD_all(all_defs, constructor_def); } return CORD_all(all_defs, compile_namespace_header(env, def->name, def->namespace)); } diff --git a/stdlib/metamethods.c b/stdlib/metamethods.c index 8243e64..0e7d348 100644 --- a/stdlib/metamethods.c +++ b/stdlib/metamethods.c @@ -4,6 +4,7 @@ #include #include "arrays.h" +#include "bools.h" #include "channels.h" #include "functiontype.h" #include "metamethods.h" @@ -15,6 +16,7 @@ #include "util.h" +#pragma GCC diagnostic ignored "-Wstack-protector" PUREFUNC public uint64_t generic_hash(const void *obj, const TypeInfo_t *type) { switch (type->tag) { @@ -23,8 +25,54 @@ PUREFUNC public uint64_t generic_hash(const void *obj, const TypeInfo_t *type) case ChannelInfo: return Channel$hash((Channel_t**)obj, type); case TableInfo: return Table$hash(obj, type); case OptionalInfo: return is_null(obj, type->OptionalInfo.type) ? 0 : generic_hash(obj, type->OptionalInfo.type); - case EmptyStructInfo: return 0; - case CustomInfo: case StructInfo: case EnumInfo: case CStringInfo: // These all share the same info + case StructInfo: { + if (type->StructInfo.num_fields == 0) { + return 0; + } else if (type->StructInfo.num_fields == 1) { + return generic_hash(obj, type->StructInfo.fields[0].type); + } else { + uint32_t field_hashes[type->StructInfo.num_fields] = {}; + ptrdiff_t byte_offset = 0; + ptrdiff_t bit_offset = 0; + for (int i = 0; i < type->StructInfo.num_fields; i++) { + NamedType_t field = type->StructInfo.fields[i]; + if (field.type == &Bool$info) { + bool b = ((*(char*)(obj + byte_offset)) >> bit_offset) & 0x1; + field_hashes[i] = (uint32_t)b; + bit_offset += 1; + if (bit_offset >= 8) { + byte_offset += 1; + bit_offset = 0; + } + } else { + if (bit_offset > 0) { + byte_offset += 1; + bit_offset = 0; + } + if (field.type->align && byte_offset % field.type->align > 0) + byte_offset += field.type->align - (byte_offset % field.type->align); + field_hashes[i] = generic_hash(obj + byte_offset, field.type); + byte_offset += field.type->size; + } + } + return siphash24((void*)field_hashes, sizeof(field_hashes)); + } + } + case EnumInfo: { + int32_t tag = *(int32_t*)obj; + uint32_t components[2] = {(uint32_t)tag, 0}; + + const TypeInfo_t *value = type->EnumInfo.tags[tag-1].type; + if (value && value->size > 0) { + ptrdiff_t byte_offset = sizeof(int32_t); + if (value->align && byte_offset % value->align > 0) + byte_offset += value->align - (byte_offset % value->align); + components[1] = generic_hash(obj + byte_offset, value); + } + return siphash24((void*)components, sizeof(components)); + + } + case CustomInfo: case CStringInfo: // These all share the same info if (!type->CustomInfo.hash) goto hash_data; return type->CustomInfo.hash(obj, type); @@ -52,8 +100,52 @@ PUREFUNC public int32_t generic_compare(const void *x, const void *y, const Type else if (x_is_null != y_is_null) return (int32_t)y_is_null - (int32_t)x_is_null; else return generic_compare(x, y, type->OptionalInfo.type); } - case EmptyStructInfo: return 0; - case CustomInfo: case StructInfo: case EnumInfo: case CStringInfo: // These all share the same info + case StructInfo: { + ptrdiff_t byte_offset = 0; + ptrdiff_t bit_offset = 0; + for (int i = 0; i < type->StructInfo.num_fields; i++) { + NamedType_t field = type->StructInfo.fields[i]; + if (field.type == &Bool$info) { + bool bx = ((*(char*)(x + byte_offset)) >> bit_offset) & 0x1; + bool by = ((*(char*)(y + byte_offset)) >> bit_offset) & 0x1; + if (bx != by) + return (int32_t)bx - (int32_t)by; + bit_offset += 1; + if (bit_offset >= 8) { + byte_offset += 1; + bit_offset = 0; + } + } else { + if (bit_offset > 0) { + byte_offset += 1; + bit_offset = 0; + } + if (field.type->align && byte_offset % field.type->align > 0) + byte_offset += field.type->align - (byte_offset % field.type->align); + int32_t cmp = generic_compare(x + byte_offset, y + byte_offset, field.type); + if (cmp != 0) + return cmp; + byte_offset += field.type->size; + } + } + return 0; + } + case EnumInfo: { + int32_t x_tag = *(int32_t*)x; + int32_t y_tag = *(int32_t*)y; + if (x_tag != y_tag) + return x_tag > y_tag ? 1 : -1; + + const TypeInfo_t *value = type->EnumInfo.tags[x_tag-1].type; + if (value && value->size > 0) { + ptrdiff_t byte_offset = sizeof(int32_t); + if (value->align && byte_offset % value->align > 0) + byte_offset += value->align - (byte_offset % value->align); + return generic_compare(x + byte_offset, y + byte_offset, value); + } + return 0; + } + case CustomInfo: case CStringInfo: // These all share the same info if (!type->CustomInfo.compare) goto compare_data; return type->CustomInfo.compare(x, y, type); @@ -73,7 +165,6 @@ PUREFUNC public bool generic_equal(const void *x, const void *y, const TypeInfo_ case ArrayInfo: return Array$equal(x, y, type); case ChannelInfo: return Channel$equal((Channel_t**)x, (Channel_t**)y, type); case TableInfo: return Table$equal(x, y, type); - case EmptyStructInfo: return true; case OptionalInfo: { bool x_is_null = is_null(x, type->OptionalInfo.type); bool y_is_null = is_null(y, type->OptionalInfo.type); @@ -81,7 +172,9 @@ PUREFUNC public bool generic_equal(const void *x, const void *y, const TypeInfo_ else if (x_is_null != y_is_null) return false; else return generic_equal(x, y, type->OptionalInfo.type); } - case CustomInfo: case StructInfo: case EnumInfo: case CStringInfo: // These all share the same info + case StructInfo: case EnumInfo: + return (generic_compare(x, y, type) == 0); + case CustomInfo: case CStringInfo: // These all share the same info if (!type->CustomInfo.equal) goto use_generic_compare; return type->CustomInfo.equal(x, y, type); @@ -102,15 +195,64 @@ public Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo_t * case TableInfo: return Table$as_text(obj, colorize, type); case TypeInfoInfo: return Type$as_text(obj, colorize, type); case OptionalInfo: return Optional$as_text(obj, colorize, type); - case EmptyStructInfo: return colorize ? - Text$concat(Text("\x1b[0;1m"), Text$from_str(type->EmptyStructInfo.name), Text("\x1b[m()")) - : Text$concat(Text$from_str(type->EmptyStructInfo.name), Text("()")); - case CustomInfo: case StructInfo: case EnumInfo: case CStringInfo: // These all share the same info + case StructInfo: { + if (!obj) return Text$from_str(type->StructInfo.name); + + if (type->StructInfo.is_secret) + return Text$format(colorize ? "\x1b[0;1m%s\x1b[m(...)" : "%s(...)", type->StructInfo.name); + + Text_t text = Text$format(colorize ? "\x1b[0;1m%s\x1b[m(" : "%s(", type->StructInfo.name); + ptrdiff_t byte_offset = 0; + ptrdiff_t bit_offset = 0; + for (int i = 0; i < type->StructInfo.num_fields; i++) { + NamedType_t field = type->StructInfo.fields[i]; + if (i > 0) + text = Text$concat(text, Text(", ")); + + if (type->StructInfo.num_fields > 1) + text = Text$concat(text, Text$from_str(field.name), Text("=")); + + if (field.type == &Bool$info) { + bool b = ((*(char*)(obj + byte_offset)) >> bit_offset) & 0x1; + text = Text$concat(text, Text$from_str(colorize ? (b ? "\x1b[35myes\x1b[m" : "\x1b[35mno\x1b[m") : (b ? "yes" : "no"))); + bit_offset += 1; + if (bit_offset >= 8) { + byte_offset += 1; + bit_offset = 0; + } + } else { + if (bit_offset > 0) { + byte_offset += 1; + bit_offset = 0; + } + if (field.type->align && byte_offset % field.type->align > 0) + byte_offset += field.type->align - (byte_offset % field.type->align); + text = Text$concat(text, generic_as_text(obj + byte_offset, colorize, field.type)); + byte_offset += field.type->size; + } + } + return Text$concat(text, Text(")")); + } + case EnumInfo: { + if (!obj) return Text$from_str(type->EnumInfo.name); + + int32_t tag = *(int32_t*)obj; + NamedType_t value = type->EnumInfo.tags[tag-1]; + if (!value.type || value.type->size == 0) + return Text$format(colorize ? "\x1b[36;1m%s\x1b[m.\x1b[1m%s\x1b[m" : "%s.%s", type->EnumInfo.name, value.name); + + ptrdiff_t byte_offset = sizeof(int32_t); + if (value.type->align && byte_offset % value.type->align > 0) + byte_offset += value.type->align - (byte_offset % value.type->align); + return Text$concat(Text$format(colorize ? "\x1b[36;1m%s\x1b[m." : "%s.", type->EnumInfo.name), + generic_as_text(obj + byte_offset, colorize, value.type)); + } + case CustomInfo: case CStringInfo: // These all share the same info if (!type->CustomInfo.as_text) fail("No text function provided for type!\n"); return type->CustomInfo.as_text(obj, colorize, type); case OpaqueInfo: return Text("???"); - default: errx(1, "Invalid type tag: %d", type->tag); + default: fail("Invalid type tag: %d", type->tag); } } diff --git a/stdlib/types.c b/stdlib/types.c index ebc9156..073bb5b 100644 --- a/stdlib/types.c +++ b/stdlib/types.c @@ -25,7 +25,7 @@ public Text_t Type$as_text(const void *typeinfo, bool colorize, const TypeInfo_t return Text$from_str(type->TypeInfoInfo.type_str); } -public const TypeInfo_t Void$info = {.size=0, .align=0, .tag=EmptyStructInfo}; -public const TypeInfo_t Abort$info = {.size=0, .align=0, .tag=EmptyStructInfo}; +public const TypeInfo_t Void$info = {.size=0, .align=0, .tag=StructInfo}; +public const TypeInfo_t Abort$info = {.size=0, .align=0, .tag=StructInfo}; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/types.h b/stdlib/types.h index 7e113dc..f9b32af 100644 --- a/stdlib/types.h +++ b/stdlib/types.h @@ -7,18 +7,23 @@ #include "datatypes.h" -struct TypeInfo_t; +typedef struct TypeInfo_s TypeInfo_t; -typedef uint64_t (*hash_fn_t)(const void*, const struct TypeInfo_t*); -typedef int32_t (*compare_fn_t)(const void*, const void*, const struct TypeInfo_t*); -typedef bool (*equal_fn_t)(const void*, const void*, const struct TypeInfo_t*); -typedef Text_t (*text_fn_t)(const void*, bool, const struct TypeInfo_t*); +typedef uint64_t (*hash_fn_t)(const void*, const TypeInfo_t*); +typedef int32_t (*compare_fn_t)(const void*, const void*, const TypeInfo_t*); +typedef bool (*equal_fn_t)(const void*, const void*, const TypeInfo_t*); +typedef Text_t (*text_fn_t)(const void*, bool, const TypeInfo_t*); -typedef struct TypeInfo_t { +typedef struct { + const char *name; + const TypeInfo_t *type; +} NamedType_t; + +struct TypeInfo_s { int64_t size, align; struct { // Anonymous tagged union for convenience - enum { CustomInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ArrayInfo, ChannelInfo, TableInfo, FunctionInfo, - OptionalInfo, TypeInfoInfo, OpaqueInfo, EmptyStructInfo, CStringInfo } tag; + enum { Invalid, CustomInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ArrayInfo, ChannelInfo, TableInfo, FunctionInfo, + OptionalInfo, TypeInfoInfo, OpaqueInfo, CStringInfo } tag; union { struct { equal_fn_t equal; @@ -28,16 +33,16 @@ typedef struct TypeInfo_t { } CustomInfo; struct { const char *sigil; - const struct TypeInfo_t *pointed; + const TypeInfo_t *pointed; } PointerInfo; struct { const char *lang; } TextInfo; struct { - const struct TypeInfo_t *item; + const TypeInfo_t *item; } ArrayInfo, ChannelInfo; struct { - const struct TypeInfo_t *key, *value; + const TypeInfo_t *key, *value; } TableInfo; struct { const char *type_str; @@ -46,16 +51,24 @@ typedef struct TypeInfo_t { const char *type_str; } TypeInfoInfo; struct { - const struct TypeInfo_t *type; + const TypeInfo_t *type; } OptionalInfo; #pragma GCC diagnostic ignored "-Wpedantic" struct {} OpaqueInfo; struct { const char *name; - } EmptyStructInfo; + int num_tags; + NamedType_t *tags; + } EnumInfo; + struct { + const char *name; + int num_fields; + bool is_secret:1; + NamedType_t *fields; + } StructInfo; }; }; -} TypeInfo_t; +}; #define Pointer$info(sigil_expr, pointed_info) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \ .tag=PointerInfo, .PointerInfo={.sigil=sigil_expr, .pointed=pointed_info}}) @@ -73,7 +86,7 @@ typedef struct TypeInfo_t { .tag=FunctionInfo, .FunctionInfo.type_str=typestr}) #define Type$info(typestr) &((TypeInfo_t){.size=sizeof(TypeInfo_t), .align=__alignof__(TypeInfo_t), \ .tag=TypeInfoInfo, .TypeInfoInfo.type_str=typestr}) -#define Optional$info(t) &((TypeInfo_t){.size=(t)->size, .align=(t)->align, \ +#define Optional$info(_size, _align, t) &((TypeInfo_t){.size=_size, .align=_align, \ .tag=OptionalInfo, .OptionalInfo.type=t}) extern const TypeInfo_t Void$info; diff --git a/structs.c b/structs.c index f907d39..14cebb7 100644 --- a/structs.c +++ b/structs.c @@ -12,112 +12,6 @@ #include "typecheck.h" #include "stdlib/util.h" -static CORD compile_str_method(env_t *env, ast_t *ast) -{ - auto def = Match(ast, StructDef); - CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name); - const char *name = def->name; - const char *dollar = strrchr(name, '$'); - if (dollar) name = dollar + 1; - CORD str_func = CORD_asprintf("static Text_t %r$as_text(%r_t *obj, bool use_color) {\n" - "\tif (!obj) return Text(\"%s\");\n", full_name, full_name, name); - if (def->secret) { - CORD_appendf(&str_func, "\treturn use_color ? Text(\"\\x1b[0;1m%s\\x1b[m(\\x1b[2m...\\x1b[m)\") : Text(\"%s(...)\");\n}", - name, name); - } else if (def->fields && !def->fields->next) { // Single-member structs don't need to print names: - type_t *field_type = get_arg_ast_type(env, def->fields); - CORD field_str = expr_as_text(env, CORD_cat("obj->$", def->fields->name), field_type, "use_color"); - str_func = CORD_all(str_func, "\treturn Text$concat(use_color ? Text(\"\\x1b[0;1m", name, "\\x1b[m(\") : Text(\"", name, "(\"), ", - field_str, ", Text(\")\"));\n}\n"); - } else { - CORD_appendf(&str_func, "\treturn Text$concat(use_color ? Text(\"\\x1b[0;1m%s\\x1b[m(\") : Text(\"%s(\")", name, name); - for (arg_ast_t *field = def->fields; field; field = field->next) { - type_t *field_type = get_arg_ast_type(env, field); - CORD field_str = expr_as_text(env, CORD_cat("obj->$", field->name), field_type, "use_color"); - CORD_appendf(&str_func, ", Text(\"%s=\"), %r", field->name, field_str); - if (field->next) CORD_appendf(&str_func, ", Text(\", \")"); - } - CORD_appendf(&str_func, ", Text(\")\"));\n}\n"); - } - return str_func; -} - -static CORD compile_compare_method(env_t *env, ast_t *ast) -{ - auto def = Match(ast, StructDef); - CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name); - CORD cmp_func = CORD_all("static int ", full_name, "$compare(const ", full_name, "_t *x, const ", full_name, - "_t *y, const TypeInfo_t *info) {\n" - "(void)info;\n", - "int diff;\n"); - for (arg_ast_t *field = def->fields; field; field = field->next) { - type_t *field_type = get_arg_ast_type(env, field); - switch (field_type->tag) { - case BigIntType: - cmp_func = CORD_all(cmp_func, "diff = Int$compare_value(x->$", field->name, ", y->$", field->name, ");"); - break; - case BoolType: case IntType: case NumType: case PointerType: case FunctionType: - cmp_func = CORD_all(cmp_func, "diff = (x->$", field->name, " > y->$", field->name, ") - (x->$", field->name, " < y->$", field->name, ");"); - break; - default: - cmp_func = CORD_all(cmp_func, "diff = generic_compare(&x->$", field->name, ", &y->$", field->name, ", ", - compile_type_info(env, field_type), ");\n"); - break; - } - cmp_func = CORD_all(cmp_func, "if (diff != 0) return diff;\n"); - } - cmp_func = CORD_all(cmp_func, "return 0;\n}\n"); - return cmp_func; -} - -static CORD compile_equals_method(env_t *env, ast_t *ast) -{ - auto def = Match(ast, StructDef); - CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name); - CORD eq_func = CORD_all("static bool ", full_name, "$equal(const ", full_name, "_t *x, const ", full_name, - "_t *y, const TypeInfo_t *info) {\n" - "(void)info;\n"); - CORD condition = CORD_EMPTY; - for (arg_ast_t *field = def->fields; field; field = field->next) { - if (condition != CORD_EMPTY) - condition = CORD_all(condition, " && "); - type_t *field_type = get_arg_ast_type(env, field); - switch (field_type->tag) { - case BigIntType: - condition = CORD_all(condition, "Int$equal_value(x->$", field->name, ", y->$", field->name, ")"); - break; - case BoolType: case IntType: case NumType: case PointerType: case FunctionType: - condition = CORD_all(condition, "(x->$", field->name, " == y->$", field->name, ")"); - break; - default: - condition = CORD_all(condition, "generic_equal(&x->$", field->name, ", &y->$", field->name, ", ", - compile_type_info(env, field_type), ")"); - break; - } - } - eq_func = CORD_all(eq_func, "return ", condition, ";\n}\n"); - return eq_func; -} - -static CORD compile_hash_method(env_t *env, ast_t *ast) -{ - auto def = Match(ast, StructDef); - CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name); - CORD hash_func = CORD_all("static uint64_t ", full_name, "$hash(const ", full_name, "_t *obj, const TypeInfo_t *info) {\n" - "(void)info;\n" - "uint64_t field_hashes[] = {"); - for (arg_ast_t *field = def->fields; field; field = field->next) { - type_t *field_type = get_arg_ast_type(env, field); - if (field_type->tag == BoolType) // Bools can be bit fields, so you can't use *obj->field there: - hash_func = CORD_all(hash_func, "\n(uint32_t)(obj->$", field->name, "),"); - else - hash_func = CORD_all(hash_func, "\ngeneric_hash(&obj->$", field->name, ", ", compile_type_info(env, field_type), "),"); - } - hash_func = CORD_all(hash_func, "};\n" - "return siphash24((void*)&field_hashes, sizeof(field_hashes));\n}\n"); - return hash_func; -} - void compile_struct_def(env_t *env, ast_t *ast) { auto def = Match(ast, StructDef); @@ -125,48 +19,25 @@ void compile_struct_def(env_t *env, ast_t *ast) type_t *t = Table$str_get(*env->types, def->name); assert(t && t->tag == StructType); - auto struct_ = Match(t, StructType); - if (def->fields) { - CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, {.tag=StructInfo, .CustomInfo={", - full_name, type_size(t), type_align(t)); + int num_fields = 0; + for (arg_ast_t *f = def->fields; f; f = f->next) + num_fields += 1; + const char *short_name = def->name; + if (strchr(short_name, '$')) + short_name = strrchr(short_name, '$') + 1; - typeinfo = CORD_all(typeinfo, ".as_text=(void*)", full_name, "$as_text, "); - env->code->funcs = CORD_all(env->code->funcs, compile_str_method(env, ast)); - - if (struct_->fields && !struct_->fields->next) { // Single member, can just use its methods - type_t *member_t = struct_->fields->type; - switch (member_t->tag) { - case IntType: case NumType: - typeinfo = CORD_all(typeinfo, ".compare=(void*)", type_to_cord(member_t), "$compare, " - ".equal=(void*)", type_to_cord(member_t), "$equal, "); - goto got_methods; - case BigIntType: case TextType: - typeinfo = CORD_all(typeinfo, ".hash=(void*)", type_to_cord(member_t), "$hash", ", ", - ".compare=(void*)", type_to_cord(member_t), "$compare, " - ".equal=(void*)", type_to_cord(member_t), "$equal, "); - goto got_methods; - case BoolType: goto got_methods; - default: break; - } - } - if (struct_->fields) { - env->code->funcs = CORD_all(env->code->funcs, compile_compare_method(env, ast), - compile_equals_method(env, ast), compile_hash_method(env, ast)); - typeinfo = CORD_all( - typeinfo, - ".compare=(void*)", full_name, "$compare, " - ".equal=(void*)", full_name, "$equal, " - ".hash=(void*)", full_name, "$hash"); - } - got_methods:; - typeinfo = CORD_cat(typeinfo, "}}};\n"); - env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo); - } else { - // If there are no fields, we can use an EmptyStructInfo typeinfo, which generates less code: - CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, {.tag=EmptyStructInfo, .EmptyStructInfo.name=%r}};\n", - full_name, type_size(t), type_align(t), CORD_quoted(def->name)); - env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo); + env->code->typeinfos = CORD_all("public const TypeInfo_t ", full_name, ";\n", env->code->typeinfos); + CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, .tag=StructInfo, .StructInfo.name=\"%s\"%s, " + ".StructInfo.num_fields=%ld, .StructInfo.fields=(NamedType_t[%d]){", + full_name, type_size(t), type_align(t), short_name, def->secret ? ", .StructInfo.is_secret=true" : "", + num_fields, num_fields); + for (arg_ast_t *f = def->fields; f; f = f->next) { + type_t *field_type = get_arg_ast_type(env, f); + typeinfo = CORD_all(typeinfo, "{\"", f->name, "\", ", compile_type_info(env, field_type), "}"); + if (f->next) typeinfo = CORD_all(typeinfo, ", "); } + typeinfo = CORD_all(typeinfo, "}};\n"); + env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo); compile_namespace(env, def->name, def->namespace); } diff --git a/types.c b/types.c index 17009f9..a0e347c 100644 --- a/types.c +++ b/types.c @@ -431,7 +431,7 @@ PUREFUNC size_t type_size(type_t *t) type_t *nonnull = Match(t, OptionalType)->type; switch (nonnull->tag) { case IntType: - switch (Match(t, IntType)->bits) { + 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); @@ -511,7 +511,7 @@ PUREFUNC size_t type_align(type_t *t) type_t *nonnull = Match(t, OptionalType)->type; switch (nonnull->tag) { case IntType: - switch (Match(t, IntType)->bits) { + 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);